示例#1
0
        internal async Task ShouldWarnWhenInlineRubyLambdaHasMissingHandlerMethod()
        {
            var logger = new TestLogger(this.output);
            var mockS3 = new Mock <IPSS3Util>();
            var lambdaFunctionResource = this.LambdaFunctionResource(
                "index.mistyped_handler",
                "ruby2.7",
                new Dictionary <object, object> {
                { "ZipFile", InlineRuby }
            });

            var mockOSInfo = new Mock <IOSInfo>();

            mockOSInfo.Setup(i => i.OSPlatform).Returns(OSPlatform.Windows);

            var artifact = new LambdaArtifact(
                new TestPathResolver(),
                lambdaFunctionResource,
                new TestLogger(this.output),
                mockOSInfo.Object,
                Directory.GetCurrentDirectory());

            var packager = LambdaPackager.CreatePackager(artifact, mockS3.Object, logger, new OSInfo());

            Func <Task> act = async() => { await packager.Package(null); };
            await act.Should().NotThrowAsync();

            logger.WarningMessages.Should().ContainMatch(
                "*If your method is within a class, validation is not yet supported for this.");
        }
        protected override Task <bool> PerformActionAsync()
        {
            EnsureInProjectDirectory();

            // Disable interactive since this command is intended to be run as part of a pipeline.
            this.DisableInteractive = true;

            return(Task.Run(() =>
            {
                // Release will be the default configuration if nothing set.
                var configuration = this.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false);

                var targetFramework = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                var projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
                var msbuildParameters = this.GetStringValueOrDefault(this.MSBuildParameters, CommonDefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);
                var disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, LambdaDefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();

                var zipArchivePath = GetStringValueOrDefault(this.OutputPackageFileName, LambdaDefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE, false);

                string publishLocation;
                var success = LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, out publishLocation, ref zipArchivePath);
                if (!success)
                {
                    this.Logger.WriteLine("Failed to create application package");
                    return false;
                }


                this.Logger.WriteLine("Lambda project successfully packaged: " + zipArchivePath);

                return true;
            }));
        }
示例#3
0
        internal async Task ShouldThrowWhenInlineLambdaHasMissingHandlerMethod(string resourceType, string runtime)
        {
            var mockS3 = new Mock <IPSS3Util>();
            var mockLambdaFunctionResource = new Mock <IResource>();

            mockLambdaFunctionResource.Setup(r => r.Name).Returns("MockInlineLambda");
            mockLambdaFunctionResource.Setup(r => r.Type).Returns(resourceType);
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue("Runtime")).Returns(runtime);
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue("Handler"))
            .Returns("index.mistyped_handler");
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue(InlineCodePropertyMap[resourceType]))
            .Returns(InlineCodeCodeMap[runtime]);

            var mockOSInfo = new Mock <IOSInfo>();

            mockOSInfo.Setup(i => i.OSPlatform).Returns(OSPlatform.Windows);

            var artifact = new LambdaArtifact(
                new TestPathResolver(),
                mockLambdaFunctionResource.Object,
                new TestLogger(this.output),
                mockOSInfo.Object,
                Directory.GetCurrentDirectory());

            // Verify parsing
            var packager = LambdaPackager.CreatePackager(artifact, mockS3.Object, new TestLogger(this.output), new OSInfo());

            artifact.HandlerInfo.MethodPart.Should().Be("mistyped_handler");

            Func <Task> act = async() => { await packager.Package(null); };

            await act.Should().ThrowAsync <PackagerException>().WithMessage("*Cannot locate handler method*");
        }
        protected override async Task <bool> PerformActionAsync()
        {
            EnsureInProjectDirectory();

            // Disable interactive since this command is intended to be run as part of a pipeline.
            DisableInteractive = true;

            string projectLocation    = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
            string s3Bucket           = this.GetStringValueOrDefault(this.S3Bucket, LambdaDefinedCommandOptions.ARGUMENT_S3_BUCKET, true);
            string s3Prefix           = this.GetStringValueOrDefault(this.S3Prefix, LambdaDefinedCommandOptions.ARGUMENT_S3_PREFIX, false);
            string templatePath       = this.GetStringValueOrDefault(this.CloudFormationTemplate, LambdaDefinedCommandOptions.ARGUMENT_CLOUDFORMATION_TEMPLATE, true);
            string outputTemplatePath = this.GetStringValueOrDefault(this.CloudFormationOutputTemplate, LambdaDefinedCommandOptions.ARGUMENT_OUTPUT_CLOUDFORMATION_TEMPLATE, true);

            if (!Path.IsPathRooted(templatePath))
            {
                templatePath = Path.Combine(Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation), templatePath);
            }

            if (!File.Exists(templatePath))
            {
                throw new LambdaToolsException($"Template file {templatePath} cannot be found.", LambdaToolsException.LambdaErrorCode.ServerlessTemplateNotFound);
            }

            await Utilities.ValidateBucketRegionAsync(this.S3Client, s3Bucket);

            string zipArchivePath      = null;
            var    configuration       = this.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
            var    targetFramework     = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
            var    msbuildParameters   = this.GetStringValueOrDefault(this.MSBuildParameters, CommonDefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);
            var    disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, LambdaDefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();
            string publishLocation;

            LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, out publishLocation, ref zipArchivePath);
            if (string.IsNullOrEmpty(zipArchivePath))
            {
                return(false);
            }

            string s3KeyApplicationBundle;

            using (var stream = new MemoryStream(File.ReadAllBytes(zipArchivePath)))
            {
                s3KeyApplicationBundle = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, Path.GetFileName(zipArchivePath), stream);
            }

            this.Logger.WriteLine($"Updating CloudFormation template to point to application bundle: s3://{s3Bucket}/{s3KeyApplicationBundle}");
            var templateBody = File.ReadAllText(templatePath);

            // Process any template substitutions
            templateBody = LambdaUtilities.ProcessTemplateSubstitions(this.Logger, templateBody, this.GetKeyValuePairOrDefault(this.TemplateSubstitutions, LambdaDefinedCommandOptions.ARGUMENT_CLOUDFORMATION_TEMPLATE_SUBSTITUTIONS, false), Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation));

            var transformedBody = LambdaUtilities.UpdateCodeLocationInTemplate(templateBody, s3Bucket, s3KeyApplicationBundle);

            this.Logger.WriteLine($"Writing updated template: {outputTemplatePath}");
            File.WriteAllText(outputTemplatePath, transformedBody);

            return(true);
        }
        public void RootPathWithTrailingSlash(string file)
        {
            var files = LambdaPackager.ConvertToMapOfFiles(GetTestRoot() + "/", new string[] { file });

            Assert.Single(files);

            Assert.True(files.ContainsKey(file));
            Assert.Equal($"{GetTestRoot()}/{file}", files[file]);
        }
        public void FileCombinations(string file)
        {
            var files = LambdaPackager.ConvertToMapOfFiles(GetTestRoot(), new string[] { file });

            Assert.Single(files);

            Assert.True(files.ContainsKey(file));
            Assert.Equal($"{GetTestRoot()}/{file}", files[file]);
        }
        public override Task <bool> ExecuteAsync()
        {
            // Disable interactive since this command is intended to be run as part of a pipeline.
            this.DisableInteractive = true;

            return(Task.Run(() =>
            {
                try
                {
                    string configuration = this.GetStringValueOrDefault(this.Configuration, DefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
                    string targetFramework = this.GetStringValueOrDefault(this.TargetFramework, DefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                    string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, DefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
                    string msbuildParameters = this.GetStringValueOrDefault(this.MSBuildParameters, DefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);
                    bool disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, DefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();

                    var zipArchivePath = GetStringValueOrDefault(this.OutputPackageFileName, DefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE, false);

                    string publishLocation;
                    bool success = LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, out publishLocation, ref zipArchivePath);
                    if (!success)
                    {
                        this.Logger.WriteLine("Failed to create application package");
                        return false;
                    }


                    this.Logger.WriteLine("Lambda project successfully packaged: " + zipArchivePath);
                    return true;
                }
                catch (LambdaToolsException e)
                {
                    this.Logger.WriteLine(e.Message);
                    this.LastToolsException = e;
                    return false;
                }
                catch (Exception e)
                {
                    this.Logger.WriteLine($"Unknown error executing Lambda packaging: {e.Message}");
                    this.Logger.WriteLine(e.StackTrace);
                    return false;
                }
            }));
        }
示例#8
0
        public async Task ShouldValidateHandlerForPreZippedLambda()
        {
            var mockS3 = new Mock <IPSS3Util>();

            var template = Path.Combine(this.handlerTestDirectory, "zipped_lambda.yaml");

            var parser   = TemplateParser.Create(await File.ReadAllTextAsync(template));
            var function = parser.GetResources().FirstOrDefault(r => r.Type == "AWS::Serverless::Function");

            function.Should().NotBeNull("you broke the template!");

            var mockOSInfo = new Mock <IOSInfo>();

            mockOSInfo.Setup(i => i.OSPlatform).Returns(OSPlatform.Windows);

            var artifact = new LambdaArtifact(new TestPathResolver(), function, new TestLogger(this.output), mockOSInfo.Object, template);
            var packager = LambdaPackager.CreatePackager(artifact, mockS3.Object, new TestLogger(this.output), new OSInfo());

            Func <Task> act = async() => { await packager.Package(null); };
            await act.Should().NotThrowAsync();
        }
示例#9
0
        internal async Task ShouldParseAndValidateInlineLambdaFunction(
            string resourceType,
            LambdaRuntimeType expectedRuntimeType,
            string runtime)
        {
            var mockS3 = new Mock <IPSS3Util>();
            var mockLambdaFunctionResource = new Mock <IResource>();

            mockLambdaFunctionResource.Setup(r => r.Name).Returns("MockInlineLambda");
            mockLambdaFunctionResource.Setup(r => r.Type).Returns(resourceType);
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue("Runtime")).Returns(runtime);
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue("Handler")).Returns("index.handler");
            mockLambdaFunctionResource.Setup(r => r.GetResourcePropertyValue(InlineCodePropertyMap[resourceType]))
            .Returns(InlineCodeCodeMap[runtime]);

            var mockOSInfo = new Mock <IOSInfo>();

            mockOSInfo.Setup(i => i.OSPlatform).Returns(OSPlatform.Windows);

            var artifact = new LambdaArtifact(
                new TestPathResolver(),
                mockLambdaFunctionResource.Object,
                new TestLogger(this.output),
                mockOSInfo.Object,
                Directory.GetCurrentDirectory());

            // Verify parsing
            artifact.ArtifactType.Should().Be(LambdaArtifactType.Inline);
            artifact.InlineCode.Should().NotBeNull();
            artifact.RuntimeInfo.RuntimeType.Should().Be(expectedRuntimeType);
            artifact.HandlerInfo.FilePart.Should().Be("index");
            artifact.HandlerInfo.MethodPart.Should().Be("handler");

            // Verify handler check
            var         packager = LambdaPackager.CreatePackager(artifact, mockS3.Object, new TestLogger(this.output), new OSInfo());
            Func <Task> act      = async() => { await packager.Package(null); };

            await act.Should().NotThrowAsync();
        }
示例#10
0
        public override Task <bool> ExecuteAsync()
        {
            return(Task.Run(() =>
            {
                try
                {
                    string configuration = this.GetStringValueOrDefault(this.Configuration, DefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
                    string targetFramework = this.GetStringValueOrDefault(this.TargetFramework, DefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                    string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, DefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);

                    var zipArchivePath = GetStringValueOrDefault(this.OutputPackageFileName, DefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE, false);

                    string publishLocation;
                    bool success = LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, out publishLocation, ref zipArchivePath);
                    if (!success)
                    {
                        this.Logger.WriteLine("Failed to create application package");
                        return false;
                    }


                    this.Logger.WriteLine("Lambda project successfully packaged: " + zipArchivePath);
                    return true;
                }
                catch (LambdaToolsException e)
                {
                    this.Logger.WriteLine(e.Message);
                    this.LastToolsException = e;
                    return false;
                }
                catch (Exception e)
                {
                    this.Logger.WriteLine($"Unknown error executing Lambda packaging: {e.Message}");
                    this.Logger.WriteLine(e.StackTrace);
                    return false;
                }
            }));
        }
示例#11
0
        protected override async Task <bool> PerformActionAsync()
        {
            EnsureInProjectDirectory();

            // Disable interactive since this command is intended to be run as part of a pipeline.
            this.DisableInteractive = true;

            var layerVersionArns = this.GetStringValuesOrDefault(this.LayerVersionArns, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_LAYERS, false);
            LayerPackageInfo layerPackageInfo = null;

            if (layerVersionArns != null)
            {
                if (!this.DisableRegionAndCredentialsCheck)
                {
                    // Region and credentials are only required if using layers. This is new behavior so do a preemptive check when there are layers to
                    // see if region and credentials are set. If they are not set give a specific error message about region and credentials required
                    // when using layers.
                    try
                    {
                        base.DetermineAWSRegion();
                    }
                    catch (Exception)
                    {
                        throw new ToolsException("Region is required for the package command when layers are specified. The layers must be inspected to see how they affect packaging.", ToolsException.CommonErrorCode.RegionNotConfigured);
                    }
                    try
                    {
                        base.DetermineAWSCredentials();
                    }
                    catch (Exception)
                    {
                        throw new ToolsException("AWS credentials are required for the package command when layers are specified. The layers must be inspected to see how they affect packaging.", ToolsException.CommonErrorCode.InvalidCredentialConfiguration);
                    }
                }

                layerPackageInfo = await LambdaUtilities.LoadLayerPackageInfos(this.Logger, this.LambdaClient, this.S3Client, layerVersionArns);
            }
            else
            {
                layerPackageInfo = new LayerPackageInfo();
            }

            var projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);

            // Release will be the default configuration if nothing set.
            var configuration = this.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false);

            var targetFramework = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, false);

            if (string.IsNullOrEmpty(targetFramework))
            {
                targetFramework = Utilities.LookupTargetFrameworkFromProjectFile(Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation));
            }

            var msbuildParameters   = this.GetStringValueOrDefault(this.MSBuildParameters, CommonDefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);
            var disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, LambdaDefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();

            var zipArchivePath = GetStringValueOrDefault(this.OutputPackageFileName, LambdaDefinedCommandOptions.ARGUMENT_OUTPUT_PACKAGE, false);

            string publishLocation;
            var    success = LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, layerPackageInfo, out publishLocation, ref zipArchivePath);

            if (!success)
            {
                this.Logger.WriteLine("Failed to create application package");
                return(false);
            }


            this.Logger.WriteLine("Lambda project successfully packaged: " + zipArchivePath);
            var dotnetSharedStoreValue = layerPackageInfo.GenerateDotnetSharedStoreValue();

            if (!string.IsNullOrEmpty(dotnetSharedStoreValue))
            {
                this.NewDotnetSharedStoreValue = dotnetSharedStoreValue;

                this.Logger.WriteLine($"\nWarning: You must set the {LambdaConstants.ENV_DOTNET_SHARED_STORE} environment variable when deploying the package. " +
                                      "If not set the layers specified will not be located by the .NET Core runtime. The trailing '/' is required.");
                this.Logger.WriteLine($"{LambdaConstants.ENV_DOTNET_SHARED_STORE}: {dotnetSharedStoreValue}");
            }

            return(true);
        }
示例#12
0
        public static int Execute(CreateLocalLayerOptions opts)
        {
            Console.WriteLine($"Creating runtime package store from manifest: {opts.Manifest}");

            if (opts.EnableOptimization)
            {
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    throw new Exception($"Package optimization is only possible on Amazon Linux. To use this feature execute the command in an Amazon Linux environment.");
                }
                else
                {
                    Console.WriteLine("Warning: Package optimization has been enabled. Be sure to run this on an Amazon Linux environment or the optimization might not be compatbile with the Lambda runtime.");
                }
            }

            if (!File.Exists(opts.Manifest))
            {
                throw new Exception($"Can not find package manifest {opts.Manifest}. Make sure to point to a file not a directory.");
            }

            var tempDirectoryName = $"{opts.StoreName}-{DateTime.UtcNow.Ticks}".ToLower();

            var tempRootPath         = Path.Combine(Path.GetTempPath(), tempDirectoryName);
            var storeOutputDirectory = Path.Combine(tempRootPath, Common.Constants.DEFAULT_LAYER_OPT_DIRECTORY);

            var convertResult = ManifestUtilities.ConvertManifestToSdkManifest(opts.Manifest);

            if (convertResult.ShouldDelete)
            {
                Console.WriteLine("Converted ASP.NET Core project file to temporary package manifest file.");
            }

            var cliWrapper  = new LambdaDotNetCLIWrapper(Directory.GetCurrentDirectory());
            var storeResult = cliWrapper.Store(
                !string.IsNullOrEmpty(opts.ProjectLocation) ? opts.ProjectLocation : Directory.GetCurrentDirectory(),
                storeOutputDirectory,
                opts.TargetFramework,
                convertResult.PackageManifest,
                opts.EnableOptimization);

            if (storeResult.exitCode != 0)
            {
                throw new Exception($"Error executing the 'dotnet store' command");
            }

            var manifest = ProjectUtilities.FindArtifactOutput(storeResult.filePath);

            //string[] files = Directory.GetFiles(storeResult.filePath, "artifact.xml", SearchOption.AllDirectories);

            var updatedContent = File.ReadAllText(manifest);
            var manifestPath   = Path.Combine(Directory.GetCurrentDirectory(), $"{opts.StoreName}.xml");

            File.WriteAllText(manifestPath, updatedContent);

            Console.WriteLine($"Created package manifest file ({manifestPath})");

            if (convertResult.ShouldDelete)
            {
                File.Delete(convertResult.PackageManifest);
            }

            var zipPath = Path.Combine(Directory.GetCurrentDirectory(), $"{opts.StoreName}.zip");

            if (File.Exists(zipPath))
            {
                File.Delete(zipPath);
            }

            LambdaPackager.BundleDirectory(zipPath, tempRootPath, false);

            return(0);
        }
示例#13
0
        protected override async Task <bool> PerformActionAsync()
        {
            string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
            string zipArchivePath  = null;
            string package         = this.GetStringValueOrDefault(this.Package, LambdaDefinedCommandOptions.ARGUMENT_PACKAGE, false);

            var layerVersionArns = this.GetStringValuesOrDefault(this.LayerVersionArns, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_LAYERS, false);
            var layerPackageInfo = await LambdaUtilities.LoadLayerPackageInfos(this.Logger, this.LambdaClient, this.S3Client, layerVersionArns);

            Lambda.PackageType packageType = DeterminePackageType();
            string             ecrImageUri = null;

            if (packageType == Lambda.PackageType.Image)
            {
                var pushResults = await PushLambdaImageAsync();

                if (!pushResults.Success)
                {
                    if (pushResults.LastToolsException != null)
                    {
                        throw pushResults.LastToolsException;
                    }

                    return(false);
                }

                ecrImageUri = pushResults.ImageUri;
            }
            else
            {
                if (string.IsNullOrEmpty(package))
                {
                    EnsureInProjectDirectory();

                    // Release will be the default configuration if nothing set.
                    string configuration = this.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, false);

                    var targetFramework = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, false);
                    if (string.IsNullOrEmpty(targetFramework))
                    {
                        targetFramework = Utilities.LookupTargetFrameworkFromProjectFile(Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation));

                        // If we still don't know what the target framework is ask the user what targetframework to use.
                        // This is common when a project is using multi targeting.
                        if (string.IsNullOrEmpty(targetFramework))
                        {
                            targetFramework = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                        }
                    }
                    string msbuildParameters = this.GetStringValueOrDefault(this.MSBuildParameters, CommonDefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);

                    ValidateTargetFrameworkAndLambdaRuntime(targetFramework);

                    bool   disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, LambdaDefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();
                    string publishLocation;
                    LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, layerPackageInfo, out publishLocation, ref zipArchivePath);
                    if (string.IsNullOrEmpty(zipArchivePath))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!File.Exists(package))
                    {
                        throw new LambdaToolsException($"Package {package} does not exist", LambdaToolsException.LambdaErrorCode.InvalidPackage);
                    }
                    if (!string.Equals(Path.GetExtension(package), ".zip", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new LambdaToolsException($"Package {package} must be a zip file", LambdaToolsException.LambdaErrorCode.InvalidPackage);
                    }

                    this.Logger.WriteLine($"Skipping compilation and using precompiled package {package}");
                    zipArchivePath = package;
                }
            }


            MemoryStream lambdaZipArchiveStream = null;

            if (zipArchivePath != null)
            {
                lambdaZipArchiveStream = new MemoryStream(File.ReadAllBytes(zipArchivePath));
            }
            try
            {
                var    s3Bucket = this.GetStringValueOrDefault(this.S3Bucket, LambdaDefinedCommandOptions.ARGUMENT_S3_BUCKET, false);
                string s3Key    = null;
                if (zipArchivePath != null && !string.IsNullOrEmpty(s3Bucket))
                {
                    await Utilities.ValidateBucketRegionAsync(this.S3Client, s3Bucket);

                    var functionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true);
                    var s3Prefix     = this.GetStringValueOrDefault(this.S3Prefix, LambdaDefinedCommandOptions.ARGUMENT_S3_PREFIX, false);
                    s3Key = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, functionName, lambdaZipArchiveStream);
                }


                var currentConfiguration = await GetFunctionConfigurationAsync();

                if (currentConfiguration == null)
                {
                    this.Logger.WriteLine($"Creating new Lambda function {this.FunctionName}");
                    var createRequest = new CreateFunctionRequest
                    {
                        PackageType  = packageType,
                        FunctionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true),
                        Description  = this.GetStringValueOrDefault(this.Description, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_DESCRIPTION, false),
                        Role         = this.GetRoleValueOrDefault(this.Role, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_ROLE,
                                                                  Constants.LAMBDA_PRINCIPAL, LambdaConstants.AWS_LAMBDA_MANAGED_POLICY_PREFIX,
                                                                  LambdaConstants.KNOWN_MANAGED_POLICY_DESCRIPTIONS, true),

                        Publish    = this.GetBoolValueOrDefault(this.Publish, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_PUBLISH, false).GetValueOrDefault(),
                        MemorySize = this.GetIntValueOrDefault(this.MemorySize, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_MEMORY_SIZE, true).GetValueOrDefault(),
                        Timeout    = this.GetIntValueOrDefault(this.Timeout, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_TIMEOUT, true).GetValueOrDefault(),
                        KMSKeyArn  = this.GetStringValueOrDefault(this.KMSKeyArn, LambdaDefinedCommandOptions.ARGUMENT_KMS_KEY_ARN, false),
                        VpcConfig  = new VpcConfig
                        {
                            SubnetIds        = this.GetStringValuesOrDefault(this.SubnetIds, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_SUBNETS, false)?.ToList(),
                            SecurityGroupIds = this.GetStringValuesOrDefault(this.SecurityGroupIds, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_SECURITY_GROUPS, false)?.ToList()
                        }
                    };

                    if (packageType == Lambda.PackageType.Zip)
                    {
                        createRequest.Handler = this.GetStringValueOrDefault(this.Handler, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_HANDLER, true);
                        createRequest.Runtime = this.GetStringValueOrDefault(this.Runtime, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_RUNTIME, true);
                        createRequest.Layers  = layerVersionArns?.ToList();

                        if (s3Bucket != null)
                        {
                            createRequest.Code = new FunctionCode
                            {
                                S3Bucket = s3Bucket,
                                S3Key    = s3Key
                            };
                        }
                        else
                        {
                            createRequest.Code = new FunctionCode
                            {
                                ZipFile = lambdaZipArchiveStream
                            };
                        }
                    }
                    else if (packageType == Lambda.PackageType.Image)
                    {
                        createRequest.Code = new FunctionCode
                        {
                            ImageUri = ecrImageUri
                        };

                        createRequest.ImageConfig = new ImageConfig
                        {
                            Command          = this.GetStringValuesOrDefault(this.ImageCommand, LambdaDefinedCommandOptions.ARGUMENT_IMAGE_COMMAND, false)?.ToList(),
                            EntryPoint       = this.GetStringValuesOrDefault(this.ImageEntryPoint, LambdaDefinedCommandOptions.ARGUMENT_IMAGE_ENTRYPOINT, false)?.ToList(),
                            WorkingDirectory = this.GetStringValueOrDefault(this.ImageWorkingDirectory, LambdaDefinedCommandOptions.ARGUMENT_IMAGE_WORKING_DIRECTORY, false)
                        };
                    }

                    var environmentVariables = GetEnvironmentVariables(null);

                    var dotnetShareStoreVal = layerPackageInfo.GenerateDotnetSharedStoreValue();
                    if (!string.IsNullOrEmpty(dotnetShareStoreVal))
                    {
                        if (environmentVariables == null)
                        {
                            environmentVariables = new Dictionary <string, string>();
                        }
                        environmentVariables[LambdaConstants.ENV_DOTNET_SHARED_STORE] = dotnetShareStoreVal;
                    }

                    if (environmentVariables != null && environmentVariables.Count > 0)
                    {
                        createRequest.Environment = new Model.Environment
                        {
                            Variables = environmentVariables
                        };
                    }

                    var tags = this.GetKeyValuePairOrDefault(this.Tags, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_TAGS, false);
                    if (tags != null && tags.Count > 0)
                    {
                        createRequest.Tags = tags;
                    }

                    var deadLetterQueue = this.GetStringValueOrDefault(this.DeadLetterTargetArn, LambdaDefinedCommandOptions.ARGUMENT_DEADLETTER_TARGET_ARN, false);
                    if (!string.IsNullOrEmpty(deadLetterQueue))
                    {
                        createRequest.DeadLetterConfig = new DeadLetterConfig {
                            TargetArn = deadLetterQueue
                        };
                    }

                    var tracingMode = this.GetStringValueOrDefault(this.TracingMode, LambdaDefinedCommandOptions.ARGUMENT_TRACING_MODE, false);
                    if (!string.IsNullOrEmpty(tracingMode))
                    {
                        createRequest.TracingConfig = new TracingConfig {
                            Mode = tracingMode
                        };
                    }


                    try
                    {
                        await this.LambdaClient.CreateFunctionAsync(createRequest);

                        this.Logger.WriteLine("New Lambda function created");
                    }
                    catch (Exception e)
                    {
                        throw new LambdaToolsException($"Error creating Lambda function: {e.Message}", LambdaToolsException.LambdaErrorCode.LambdaCreateFunction, e);
                    }
                }
                else
                {
                    this.Logger.WriteLine($"Updating code for existing function {this.FunctionName}");

                    var updateCodeRequest = new UpdateFunctionCodeRequest
                    {
                        FunctionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true)
                    };

                    // In case the function is currently being updated from previous deployment wait till it available
                    // to be updated.
                    if (currentConfiguration.LastUpdateStatus == LastUpdateStatus.InProgress)
                    {
                        await LambdaUtilities.WaitTillFunctionAvailableAsync(Logger, this.LambdaClient, updateCodeRequest.FunctionName);
                    }

                    if (packageType == Lambda.PackageType.Zip)
                    {
                        if (s3Bucket != null)
                        {
                            updateCodeRequest.S3Bucket = s3Bucket;
                            updateCodeRequest.S3Key    = s3Key;
                        }
                        else
                        {
                            updateCodeRequest.ZipFile = lambdaZipArchiveStream;
                        }
                    }
                    else if (packageType == Lambda.PackageType.Image)
                    {
                        updateCodeRequest.ImageUri = ecrImageUri;
                    }

                    try
                    {
                        await this.LambdaClient.UpdateFunctionCodeAsync(updateCodeRequest);
                    }
                    catch (Exception e)
                    {
                        throw new LambdaToolsException($"Error updating code for Lambda function: {e.Message}", LambdaToolsException.LambdaErrorCode.LambdaUpdateFunctionCode, e);
                    }

                    await base.UpdateConfigAsync(currentConfiguration, layerPackageInfo.GenerateDotnetSharedStoreValue());

                    await base.ApplyTags(currentConfiguration.FunctionArn);

                    var publish = this.GetBoolValueOrDefault(this.Publish, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_PUBLISH, false).GetValueOrDefault();
                    if (publish)
                    {
                        await base.PublishFunctionAsync(updateCodeRequest.FunctionName);
                    }
                }
            }
            finally
            {
                lambdaZipArchiveStream?.Dispose();
            }

            return(true);
        }
        /// <summary>
        /// Processes resources that can point to artifacts in S3.
        /// </summary>
        /// <param name="resource">The resource.</param>
        /// <param name="templatePath">The template path.</param>
        /// <param name="workingDirectory">The working directory.</param>
        /// <returns><c>true</c> if the containing template should be modified (to point to S3); else <c>false</c></returns>
        /// <exception cref="InvalidDataException">Unsupported derivative of FileSystemInfo</exception>
        /// <exception cref="MissingMethodException">Missing constructor for the replacement template artifact.</exception>
        private async Task <bool> ProcessResource(
            IResource resource,
            string templatePath,
            string workingDirectory)
        {
            var templateModified = false;

            foreach (var propertyToCheck in PackagedResources[resource.Type])
            {
                ResourceUploadSettings resourceToUpload;

                // See if we have a lambda
                var lambdaResource = new LambdaArtifact(this.pathResolver, resource, this.logger, this.platform, templatePath);

                if (lambdaResource.ArtifactType != LambdaArtifactType.NotLambda)
                {
                    // We do
                    using (var packager = LambdaPackager.CreatePackager(lambdaResource, this.s3Util, this.logger, this.platform))
                    {
                        resourceToUpload = await packager.Package(workingDirectory);

                        if (resourceToUpload == null)
                        {
                            // Lambda syntax does not imply a template modification
                            // i.e. it is inline code or already an S3 reference.
                            continue;
                        }

                        // The template will be altered to an S3 location,
                        // however the zip may or may not be uploaded.
                        templateModified = true;
                    }
                }
                else
                {
                    string resourceFile;

                    try
                    {
                        resourceFile = (string)resource.GetResourcePropertyValue(propertyToCheck.PropertyPath);
                    }
                    catch (FormatException)
                    {
                        if (!propertyToCheck.Required)
                        {
                            // Property is missing, but CloudFormation does not require it.
                            continue;
                        }

                        throw;
                    }

                    if (resourceFile == null)
                    {
                        // Property was not found, or was not a value type.
                        continue;
                    }

                    var fsi = ResolveFileSystemResource(this.pathResolver, templatePath, resourceFile);

                    if (fsi == null)
                    {
                        // Property value did not resolve to a path in the file system
                        continue;
                    }

                    templateModified = true;
                    switch (fsi)
                    {
                    case FileInfo fi:

                        // Property value points to a file
                        resourceToUpload = await ArtifactPackager.PackageFile(
                            fi,
                            workingDirectory,
                            propertyToCheck.Zip,
                            this.s3Util,
                            this.logger);

                        break;

                    case DirectoryInfo di:

                        // Property value points to a directory, which must always be zipped.
                        resourceToUpload = await ArtifactPackager.PackageDirectory(
                            di,
                            workingDirectory,
                            this.s3Util,
                            this.logger);

                        break;

                    default:

                        // Should never get here, but shuts up a bunch of compiler/R# warnings
                        throw new InvalidDataException(
                                  $"Unsupported derivative of FileSystemInfo: {fsi.GetType().FullName}");
                    }
                }

                if (!resourceToUpload.HashMatch)
                {
                    resourceToUpload.KeyPrefix = this.s3Util.KeyPrefix;
                    resourceToUpload.Metadata  = this.s3Util.Metadata;

                    await this.s3Util.UploadResourceToS3Async(resourceToUpload);
                }

                if (propertyToCheck.ReplacementType == typeof(string))
                {
                    // Insert the URI directly
                    resource.UpdateResourceProperty(propertyToCheck.PropertyPath, resourceToUpload.S3Artifact.Url);
                }
                else
                {
                    // Create an instance of the new mapping
                    // ReSharper disable once StyleCop.SA1305
                    var s3Location = propertyToCheck.ReplacementType.GetConstructor(new[] { typeof(S3Artifact) })
                                     ?.Invoke(new object[] { resourceToUpload.S3Artifact });

                    if (s3Location == null)
                    {
                        throw new MissingMethodException(propertyToCheck.ReplacementType.FullName, ".ctor(S3Artifact)");
                    }

                    // and set on the resource property
                    resource.UpdateResourceProperty(propertyToCheck.PropertyPath, s3Location);
                }
            }

            return(templateModified);
        }
        protected override async Task <bool> PerformActionAsync()
        {
            string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
            string zipArchivePath  = null;
            string package         = this.GetStringValueOrDefault(this.Package, LambdaDefinedCommandOptions.ARGUMENT_PACKAGE, false);

            if (string.IsNullOrEmpty(package))
            {
                EnsureInProjectDirectory();

                string configuration     = this.GetStringValueOrDefault(this.Configuration, CommonDefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
                string targetFramework   = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                string msbuildParameters = this.GetStringValueOrDefault(this.MSBuildParameters, CommonDefinedCommandOptions.ARGUMENT_MSBUILD_PARAMETERS, false);

                ValidateTargetFrameworkAndLambdaRuntime();

                bool   disableVersionCheck = this.GetBoolValueOrDefault(this.DisableVersionCheck, LambdaDefinedCommandOptions.ARGUMENT_DISABLE_VERSION_CHECK, false).GetValueOrDefault();
                string publishLocation;
                LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, msbuildParameters, disableVersionCheck, out publishLocation, ref zipArchivePath);
                if (string.IsNullOrEmpty(zipArchivePath))
                {
                    return(false);
                }
            }
            else
            {
                if (!File.Exists(package))
                {
                    throw new LambdaToolsException($"Package {package} does not exist", LambdaToolsException.LambdaErrorCode.InvalidPackage);
                }
                if (!string.Equals(Path.GetExtension(package), ".zip", StringComparison.OrdinalIgnoreCase))
                {
                    throw new LambdaToolsException($"Package {package} must be a zip file", LambdaToolsException.LambdaErrorCode.InvalidPackage);
                }

                this.Logger.WriteLine($"Skipping compilation and using precompiled package {package}");
                zipArchivePath = package;
            }


            using (var stream = new MemoryStream(File.ReadAllBytes(zipArchivePath)))
            {
                var    s3Bucket = this.GetStringValueOrDefault(this.S3Bucket, LambdaDefinedCommandOptions.ARGUMENT_S3_BUCKET, false);
                string s3Key    = null;
                if (!string.IsNullOrEmpty(s3Bucket))
                {
                    await Utilities.ValidateBucketRegionAsync(this.S3Client, s3Bucket);

                    var functionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true);
                    var s3Prefix     = this.GetStringValueOrDefault(this.S3Prefix, LambdaDefinedCommandOptions.ARGUMENT_S3_PREFIX, false);
                    s3Key = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, functionName, stream);
                }


                var currentConfiguration = await GetFunctionConfigurationAsync();

                if (currentConfiguration == null)
                {
                    this.Logger.WriteLine($"Creating new Lambda function {this.FunctionName}");
                    var createRequest = new CreateFunctionRequest
                    {
                        FunctionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true),
                        Description  = this.GetStringValueOrDefault(this.Description, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_DESCRIPTION, false),

                        Role = this.GetRoleValueOrDefault(this.Role, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_ROLE,
                                                          Constants.LAMBDA_PRINCIPAL, LambdaConstants.AWS_LAMBDA_MANAGED_POLICY_PREFIX,
                                                          LambdaConstants.KNOWN_MANAGED_POLICY_DESCRIPTIONS, true),

                        Handler    = this.GetStringValueOrDefault(this.Handler, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_HANDLER, true),
                        Publish    = this.GetBoolValueOrDefault(this.Publish, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_PUBLISH, false).GetValueOrDefault(),
                        MemorySize = this.GetIntValueOrDefault(this.MemorySize, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_MEMORY_SIZE, true).GetValueOrDefault(),
                        Runtime    = this.GetStringValueOrDefault(this.Runtime, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_RUNTIME, true),
                        Timeout    = this.GetIntValueOrDefault(this.Timeout, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_TIMEOUT, true).GetValueOrDefault(),
                        KMSKeyArn  = this.GetStringValueOrDefault(this.KMSKeyArn, LambdaDefinedCommandOptions.ARGUMENT_KMS_KEY_ARN, false),
                        VpcConfig  = new VpcConfig
                        {
                            SubnetIds        = this.GetStringValuesOrDefault(this.SubnetIds, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_SUBNETS, false)?.ToList(),
                            SecurityGroupIds = this.GetStringValuesOrDefault(this.SecurityGroupIds, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_SECURITY_GROUPS, false)?.ToList()
                        }
                    };

                    var environmentVariables = GetEnvironmentVariables(null);

                    if (environmentVariables != null && environmentVariables.Count > 0)
                    {
                        createRequest.Environment = new Model.Environment
                        {
                            Variables = environmentVariables
                        };
                    }

                    var tags = this.GetKeyValuePairOrDefault(this.Tags, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_TAGS, false);
                    if (tags != null && tags.Count > 0)
                    {
                        createRequest.Tags = tags;
                    }

                    var deadLetterQueue = this.GetStringValueOrDefault(this.DeadLetterTargetArn, LambdaDefinedCommandOptions.ARGUMENT_DEADLETTER_TARGET_ARN, false);
                    if (!string.IsNullOrEmpty(deadLetterQueue))
                    {
                        createRequest.DeadLetterConfig = new DeadLetterConfig {
                            TargetArn = deadLetterQueue
                        };
                    }

                    var tracingMode = this.GetStringValueOrDefault(this.TracingMode, LambdaDefinedCommandOptions.ARGUMENT_TRACING_MODE, false);
                    if (!string.IsNullOrEmpty(tracingMode))
                    {
                        createRequest.TracingConfig = new TracingConfig {
                            Mode = tracingMode
                        };
                    }

                    if (s3Bucket != null)
                    {
                        createRequest.Code = new FunctionCode
                        {
                            S3Bucket = s3Bucket,
                            S3Key    = s3Key
                        };
                    }
                    else
                    {
                        createRequest.Code = new FunctionCode
                        {
                            ZipFile = stream
                        };
                    }


                    try
                    {
                        await this.LambdaClient.CreateFunctionAsync(createRequest);

                        this.Logger.WriteLine("New Lambda function created");
                    }
                    catch (Exception e)
                    {
                        throw new LambdaToolsException($"Error creating Lambda function: {e.Message}", LambdaToolsException.LambdaErrorCode.LambdaCreateFunction, e);
                    }
                }
                else
                {
                    this.Logger.WriteLine($"Updating code for existing function {this.FunctionName}");

                    var updateCodeRequest = new UpdateFunctionCodeRequest
                    {
                        FunctionName = this.GetStringValueOrDefault(this.FunctionName, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true)
                    };

                    if (s3Bucket != null)
                    {
                        updateCodeRequest.S3Bucket = s3Bucket;
                        updateCodeRequest.S3Key    = s3Key;
                    }
                    else
                    {
                        updateCodeRequest.ZipFile = stream;
                    }

                    try
                    {
                        await this.LambdaClient.UpdateFunctionCodeAsync(updateCodeRequest);
                    }
                    catch (Exception e)
                    {
                        throw new LambdaToolsException($"Error updating code for Lambda function: {e.Message}", LambdaToolsException.LambdaErrorCode.LambdaUpdateFunctionCode, e);
                    }

                    await base.UpdateConfigAsync(currentConfiguration);

                    await base.ApplyTags(currentConfiguration.FunctionArn);

                    var publish = this.GetBoolValueOrDefault(this.Publish, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_PUBLISH, false).GetValueOrDefault();
                    if (publish)
                    {
                        await base.PublishFunctionAsync(updateCodeRequest.FunctionName);
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Determine the action to be done for the local path, like building a .NET Core package, then uploading the
        /// package to S3. The S3 key is returned to be updated in the template.
        /// </summary>
        /// <param name="templateDirectory"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        /// <exception cref="LambdaToolsException"></exception>
        private async Task <UpdateResourceResults> ProcessUpdatableResourceAsync(string templateDirectory, IUpdateResourceField field, string[] args)
        {
            UpdateResourceResults results;
            var localPath = field.GetLocalPath();

            if (!Path.IsPathRooted(localPath))
            {
                localPath = Path.Combine(templateDirectory, localPath);
            }

            bool deleteArchiveAfterUploaded = false;

            // Uploading a single file as the code for the resource. If the single file is not a zip file then zip the file first.
            if (File.Exists(localPath))
            {
                if (field.IsCode && !string.Equals(Path.GetExtension(localPath), ".zip", StringComparison.OrdinalIgnoreCase))
                {
                    this.Logger.WriteLine($"Creating zip archive for {localPath} file");
                    results = new UpdateResourceResults {
                        ZipArchivePath = GenerateOutputZipFilename(field)
                    };
                    LambdaPackager.BundleFiles(results.ZipArchivePath, Path.GetDirectoryName(localPath), new string[] { localPath }, this.Logger);
                }
                else
                {
                    results = new UpdateResourceResults {
                        ZipArchivePath = localPath
                    };
                }
            }
            // If IsCode is false then the local path needs to point to a file and not a directory. When IsCode is true
            // it can point either to a file or a directory.
            else if (!field.IsCode && !File.Exists(localPath))
            {
                throw new LambdaToolsException($"File that the field {field.Resource.Name}/{field.Name} is pointing to doesn't exist", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingLocalPath);
            }
            else if (!Directory.Exists(localPath))
            {
                throw new LambdaToolsException($"Directory that the field {field.Resource.Name}/{field.Name} is pointing doesn't exist", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingLocalPath);
            }
            // To maintain compatibility if the field is point to current directory or not set at all but a prepackaged zip archive is given
            // then use it as the package source.
            else if (IsCurrentDirectory(field.GetLocalPath()) && !string.IsNullOrEmpty(this.DefaultOptions.Package))
            {
                results = new UpdateResourceResults {
                    ZipArchivePath = this.DefaultOptions.Package
                };
            }
            else if (field.IsCode)
            {
                // If the function is image upload then run the .NET tools to handle running
                // docker build even if the current folder is not a .NET project. The .NET
                // could be in a sub folder or be a self contained Docker build.
                if (IsDotnetProjectDirectory(localPath) || field.Resource.UploadType == CodeUploadType.Image)
                {
                    results = await PackageDotnetProjectAsync(field, localPath, args);
                }
                else
                {
                    results = new UpdateResourceResults {
                        ZipArchivePath = GenerateOutputZipFilename(field)
                    };
                    LambdaPackager.BundleDirectory(results.ZipArchivePath, localPath, false, this.Logger);
                }
                deleteArchiveAfterUploaded = true;
            }
            else
            {
                throw new LambdaToolsException($"Unable to determine package action for the field {field.Resource.Name}/{field.Name}", LambdaToolsException.LambdaErrorCode.ServerlessTemplateUnknownActionForLocalPath);
            }

            if (!string.IsNullOrEmpty(results.ZipArchivePath))
            {
                string s3Key;
                using (var stream = File.OpenRead(results.ZipArchivePath))
                {
                    s3Key = await Utilities.UploadToS3Async(this.Logger, this.S3Client, this.S3Bucket, this.S3Prefix, Path.GetFileName(results.ZipArchivePath), stream);

                    results.S3Key = s3Key;
                }

                // Now that the temp zip file is uploaded to S3 clean up by deleting the temp file.
                if (deleteArchiveAfterUploaded)
                {
                    try
                    {
                        File.Delete(results.ZipArchivePath);
                    }
                    catch (Exception e)
                    {
                        this.Logger?.WriteLine($"Warning: Unable to delete temporary archive, {results.ZipArchivePath}, after uploading to S3: {e.Message}");
                    }
                }
            }

            return(results);
        }
示例#17
0
        public override async Task <bool> ExecuteAsync()
        {
            try
            {
                string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, DefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
                string stackName       = this.GetStringValueOrDefault(this.StackName, DefinedCommandOptions.ARGUMENT_STACK_NAME, true);
                string s3Bucket        = this.GetStringValueOrDefault(this.S3Bucket, DefinedCommandOptions.ARGUMENT_S3_BUCKET, true);
                string s3Prefix        = this.GetStringValueOrDefault(this.S3Prefix, DefinedCommandOptions.ARGUMENT_S3_PREFIX, false);
                string templatePath    = this.GetStringValueOrDefault(this.CloudFormationTemplate, DefinedCommandOptions.ARGUMENT_CLOUDFORMATION_TEMPLATE, true);

                await Utilities.ValidateBucketRegionAsync(this.S3Client, s3Bucket);

                if (!Path.IsPathRooted(templatePath))
                {
                    templatePath = Path.Combine(Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation), templatePath);
                }

                if (!File.Exists(templatePath))
                {
                    throw new LambdaToolsException($"Template file {templatePath} cannot be found.", LambdaToolsException.ErrorCode.ServerlessTemplateNotFound);
                }


                // Build and bundle up the users project.
                string publishLocation;
                string zipArchivePath = null;
                string package        = this.GetStringValueOrDefault(this.Package, DefinedCommandOptions.ARGUMENT_PACKAGE, false);
                if (string.IsNullOrEmpty(package))
                {
                    string configuration   = this.GetStringValueOrDefault(this.Configuration, DefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
                    string targetFramework = this.GetStringValueOrDefault(this.TargetFramework, DefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
                    LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, out publishLocation, ref zipArchivePath);
                    if (string.IsNullOrEmpty(zipArchivePath))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!File.Exists(package))
                    {
                        throw new LambdaToolsException($"Package {package} does not exist", LambdaToolsException.ErrorCode.InvalidPackage);
                    }
                    if (!string.Equals(Path.GetExtension(package), ".zip", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new LambdaToolsException($"Package {package} must be a zip file", LambdaToolsException.ErrorCode.InvalidPackage);
                    }

                    this.Logger.WriteLine($"Skipping compilation and using precompiled package {package}");
                    zipArchivePath = package;
                }


                // Upload the app bundle to S3
                string s3KeyApplicationBundle;
                using (var stream = new MemoryStream(File.ReadAllBytes(zipArchivePath)))
                {
                    s3KeyApplicationBundle = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, stackName, stream);
                }

                // Read in the serverless template and update all the locations for Lambda functions to point to the app bundle that was just uploaded.
                string templateBody = File.ReadAllText(templatePath);

                // Process any template substitutions
                templateBody = Utilities.ProcessTemplateSubstitions(this.Logger, templateBody, this.GetKeyValuePairOrDefault(this.TemplateSubstitutions, DefinedCommandOptions.ARGUMENT_CLOUDFORMATION_TEMPLATE_SUBSTITUTIONS, false), Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation));

                templateBody = UpdateCodeLocationInTemplate(templateBody, s3Bucket, s3KeyApplicationBundle);

                // Upload the template to S3 instead of sending it straight to CloudFormation to avoid the size limitation
                string s3KeyTemplate;
                using (var stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(templateBody)))
                {
                    s3KeyTemplate = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, stackName + "-" + Path.GetFileName(templatePath), stream);
                }

                var existingStack = await GetExistingStackAsync(stackName);

                this.Logger.WriteLine("Found existing stack: " + (existingStack != null));
                var changeSetName = "Lambda-Tools-" + DateTime.Now.Ticks;

                // Determine if the stack is in a good state to be updated.
                ChangeSetType changeSetType;
                if (existingStack == null || existingStack.StackStatus == StackStatus.REVIEW_IN_PROGRESS || existingStack.StackStatus == StackStatus.DELETE_COMPLETE)
                {
                    changeSetType = ChangeSetType.CREATE;
                }
                // If the status was ROLLBACK_COMPLETE that means the stack failed on initial creation
                // and the resources were cleaned up. It is safe to delete the stack so we can recreate it.
                else if (existingStack.StackStatus == StackStatus.ROLLBACK_COMPLETE)
                {
                    await DeleteRollbackCompleteStackAsync(existingStack);

                    changeSetType = ChangeSetType.CREATE;
                }
                // If the status was ROLLBACK_IN_PROGRESS that means the initial creation is failing.
                // Wait to see if it goes into ROLLBACK_COMPLETE status meaning everything got cleaned up and then delete it.
                else if (existingStack.StackStatus == StackStatus.ROLLBACK_IN_PROGRESS)
                {
                    existingStack = await WaitForNoLongerInProgress(existingStack.StackName);

                    if (existingStack != null && existingStack.StackStatus == StackStatus.ROLLBACK_COMPLETE)
                    {
                        await DeleteRollbackCompleteStackAsync(existingStack);
                    }

                    changeSetType = ChangeSetType.CREATE;
                }
                // If the status was DELETE_IN_PROGRESS then just wait for delete to complete
                else if (existingStack.StackStatus == StackStatus.DELETE_IN_PROGRESS)
                {
                    await WaitForNoLongerInProgress(existingStack.StackName);

                    changeSetType = ChangeSetType.CREATE;
                }
                // The Stack state is in a normal state and ready to be updated.
                else if (existingStack.StackStatus == StackStatus.CREATE_COMPLETE ||
                         existingStack.StackStatus == StackStatus.UPDATE_COMPLETE ||
                         existingStack.StackStatus == StackStatus.UPDATE_ROLLBACK_COMPLETE)
                {
                    changeSetType = ChangeSetType.UPDATE;
                }
                // All other states means the Stack is in an inconsistent state.
                else
                {
                    this.Logger.WriteLine($"The stack's current state of {existingStack.StackStatus} is invalid for updating");
                    return(false);
                }

                CreateChangeSetResponse changeSetResponse;
                try
                {
                    var templateParameters = GetTemplateParameters(changeSetType == ChangeSetType.UPDATE ? existingStack : null);
                    if (templateParameters?.Count > 0)
                    {
                        var setParameters = templateParameters.Where(x => !x.UsePreviousValue);
                        if (setParameters?.Count() > 0)
                        {
                            this.Logger.WriteLine("Template Parameters Applied:");
                            foreach (var parameter in setParameters)
                            {
                                this.Logger.WriteLine($"\t{parameter.ParameterKey}: {parameter.ParameterValue}");
                            }
                        }
                    }

                    var capabilities        = new List <string>();
                    var disabledCapabilties = GetStringValuesOrDefault(this.DisabledCapabilities, DefinedCommandOptions.ARGUMENT_CLOUDFORMATION_DISABLE_CAPABILITIES, false);

                    if (disabledCapabilties?.FirstOrDefault(x => string.Equals(x, "CAPABILITY_IAM", StringComparison.OrdinalIgnoreCase)) == null)
                    {
                        capabilities.Add("CAPABILITY_IAM");
                    }
                    if (disabledCapabilties?.FirstOrDefault(x => string.Equals(x, "CAPABILITY_NAMED_IAM", StringComparison.OrdinalIgnoreCase)) == null)
                    {
                        capabilities.Add("CAPABILITY_NAMED_IAM");
                    }

                    var changeSetRequest = new CreateChangeSetRequest
                    {
                        StackName     = stackName,
                        Parameters    = templateParameters,
                        ChangeSetName = changeSetName,
                        ChangeSetType = changeSetType,
                        Capabilities  = capabilities,
                        RoleARN       = this.GetStringValueOrDefault(this.CloudFormationRole, DefinedCommandOptions.ARGUMENT_CLOUDFORMATION_ROLE, false),
                        Tags          = new List <Tag> {
                            new Tag {
                                Key = Constants.SERVERLESS_TAG_NAME, Value = "true"
                            }
                        }
                    };

                    if (new FileInfo(templatePath).Length < Constants.MAX_TEMPLATE_BODY_IN_REQUEST_SIZE)
                    {
                        changeSetRequest.TemplateBody = templateBody;
                    }
                    else
                    {
                        changeSetRequest.TemplateURL = this.S3Client.GetPreSignedURL(new S3.Model.GetPreSignedUrlRequest {
                            BucketName = s3Bucket, Key = s3KeyTemplate, Expires = DateTime.Now.AddHours(1)
                        });
                    }

                    // Create the change set which performs the transformation on the Serverless resources in the template.
                    changeSetResponse = await this.CloudFormationClient.CreateChangeSetAsync(changeSetRequest);


                    this.Logger.WriteLine("CloudFormation change set created");
                }
                catch (LambdaToolsException)
                {
                    throw;
                }
                catch (Exception e)
                {
                    throw new LambdaToolsException($"Error creating CloudFormation change set: {e.Message}", LambdaToolsException.ErrorCode.CloudFormationCreateStack, e);
                }

                // The change set can take a few seconds to be reviewed and be ready to be executed.
                if (!await WaitForChangeSetBeingAvailableAsync(changeSetResponse.Id, existingStack != null))
                {
                    return(false);
                }

                var executeChangeSetRequest = new ExecuteChangeSetRequest
                {
                    StackName     = stackName,
                    ChangeSetName = changeSetResponse.Id
                };

                // Execute the change set.
                DateTime timeChangeSetExecuted = DateTime.Now;
                ExecuteChangeSetResponse executeChangeSetResponse;
                try
                {
                    executeChangeSetResponse = await this.CloudFormationClient.ExecuteChangeSetAsync(executeChangeSetRequest);

                    if (changeSetType == ChangeSetType.CREATE)
                    {
                        this.Logger.WriteLine($"Created CloudFormation stack {stackName}");
                    }
                    else
                    {
                        this.Logger.WriteLine($"Initiated CloudFormation stack update on {stackName}");
                    }
                }
                catch (Exception e)
                {
                    throw new LambdaToolsException($"Error executing CloudFormation change set: {e.Message}", LambdaToolsException.ErrorCode.CloudFormationCreateChangeSet, e);
                }

                // Wait for the stack to finish unless the user opts out of waiting. The VS Toolkit opts out and
                // instead shows the stack view in the IDE, enabling the user to view progress.
                var shouldWait = GetBoolValueOrDefault(this.WaitForStackToComplete, DefinedCommandOptions.ARGUMENT_STACK_WAIT, false);
                if (!shouldWait.HasValue || shouldWait.Value)
                {
                    var updatedStack = await WaitStackToCompleteAsync(stackName, timeChangeSetExecuted);

                    if (updatedStack.StackStatus == StackStatus.CREATE_COMPLETE || updatedStack.StackStatus == StackStatus.UPDATE_COMPLETE)
                    {
                        this.Logger.WriteLine($"Stack finished updating with status: {updatedStack.StackStatus}");

                        // Display the output parameters.
                        DisplayOutputs(updatedStack);
                    }
                    else
                    {
                        this.Logger.WriteLine($"Stack update failed with status: {updatedStack.StackStatus} ({updatedStack.StackStatusReason})");
                        return(false);
                    }
                }

                if (this.GetBoolValueOrDefault(this.PersistConfigFile, DefinedCommandOptions.ARGUMENT_PERSIST_CONFIG_FILE, false).GetValueOrDefault())
                {
                    this.SaveConfigFile();
                }

                return(true);
            }
            catch (LambdaToolsException e)
            {
                this.Logger.WriteLine(e.Message);
                this.LastToolsException = e;
                return(false);
            }
            catch (Exception e)
            {
                this.Logger.WriteLine($"Unknown error executing AWS Serverless deployment: {e.Message}");
                this.Logger.WriteLine(e.StackTrace);
                return(false);
            }
        }
        private async Task <CreateLayerZipFileResult> CreateRuntimePackageStoreLayerZipFile(string layerName, string s3Prefix)
        {
            var targetFramework    = this.GetStringValueOrDefault(this.TargetFramework, CommonDefinedCommandOptions.ARGUMENT_FRAMEWORK, true);
            var projectLocation    = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
            var enableOptimization = this.GetBoolValueOrDefault(this.EnablePackageOptimization, LambdaDefinedCommandOptions.ARGUMENT_ENABLE_PACKAGE_OPTIMIZATION, false).GetValueOrDefault();

            if (string.Equals(targetFramework, "netcoreapp3.1"))
            {
                var version = DotNetCLIWrapper.GetSdkVersion();

                // .NET SDK 3.1 versions less then 3.1.400 have an issue throwing NullReferenceExceptions when pruning packages out with the manifest.
                // https://github.com/dotnet/sdk/issues/10973
                if (version < Version.Parse("3.1.400"))
                {
                    var message = $"Publishing runtime package store layers targeting .NET Core 3.1 requires at least version 3.1.400 of the .NET SDK. Current version installed is {version}.";
                    throw new LambdaToolsException(message, LambdaToolsException.LambdaErrorCode.DisabledSupportForNET31Layers);
                }
            }

#if NETCORE
            if (enableOptimization)
            {
                if (!RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                {
                    throw new LambdaToolsException($"Package optimization is only possible on Amazon Linux. To use this feature execute the command in an Amazon Linux environment.", LambdaToolsException.LambdaErrorCode.UnsupportedOptimizationPlatform);
                }
                else
                {
                    this.Logger.WriteLine("Warning: Package optimization has been enabled. Be sure to run this on an Amazon Linux environment or the optimization might not be compatbile with the Lambda runtime.");
                }
            }
#else
            // This is the case the code is run in the AWS Toolkit for Visual Studio which will never run on Amazon Linux.
            enableOptimization = false;
#endif

            // This is the manifest that list the NuGet packages via <PackageReference> elements in the msbuild project file.
            var packageManifest = this.GetStringValueOrDefault(this.PackageManifest, LambdaDefinedCommandOptions.ARGUMENT_PACKAGE_MANIFEST, false);
            // If this is null attempt to use the current directory. This is likely if the intent is to make a
            // layer from the current Lambda project.
            if (string.IsNullOrEmpty(packageManifest))
            {
                packageManifest = Utilities.DetermineProjectLocation(this.WorkingDirectory, projectLocation);
            }

            // If this is a directory look to see if there is a single csproj of fsproj in the directory in use that.
            // This is to make it easy to make a layer in the current directory of a Lambda function.
            if (Directory.Exists(packageManifest))
            {
                var files = Directory.GetFiles(packageManifest, "*.csproj");
                if (files.Length == 1)
                {
                    packageManifest = Path.Combine(packageManifest, files[0]);
                }
                else if (files.Length == 0)
                {
                    files = Directory.GetFiles(packageManifest, "*.fsproj");
                    if (files.Length == 1)
                    {
                        packageManifest = Path.Combine(packageManifest, files[0]);
                    }
                }
            }

            if (!File.Exists(packageManifest))
            {
                throw new LambdaToolsException($"Can not find package manifest {packageManifest}. Make sure to point to a file not a directory.", LambdaToolsException.LambdaErrorCode.LayerPackageManifestNotFound);
            }

            // Create second subdirectory so that when the directory is zipped the sub directory is retained in the zip file.
            // The sub directory will be created in the /opt directory in the Lambda environment.
            var tempDirectoryName = $"{layerName}-{DateTime.UtcNow.Ticks}".ToLower();
            var optDirectory      = this.GetStringValueOrDefault(this.OptDirectory, LambdaDefinedCommandOptions.ARGUMENT_OPT_DIRECTORY, false);
            if (string.IsNullOrEmpty(optDirectory))
            {
                optDirectory = LambdaConstants.DEFAULT_LAYER_OPT_DIRECTORY;
            }
            var tempRootPath         = Path.Combine(Path.GetTempPath(), tempDirectoryName);
            var storeOutputDirectory = Path.Combine(tempRootPath, optDirectory);

            {
                var convertResult = LambdaUtilities.ConvertManifestToSdkManifest(targetFramework, packageManifest);
                if (convertResult.ShouldDelete)
                {
                    this.Logger?.WriteLine("Converted ASP.NET Core project file to temporary package manifest file.");
                }
                var architecture = this.GetStringValueOrDefault(this.Architecture, LambdaDefinedCommandOptions.ARGUMENT_FUNCTION_ARCHITECTURE, false);
                var cliWrapper   = new LambdaDotNetCLIWrapper(this.Logger, this.WorkingDirectory);
                if (cliWrapper.Store(defaults: this.DefaultConfig,
                                     projectLocation: projectLocation,
                                     outputLocation: storeOutputDirectory,
                                     targetFramework: targetFramework,
                                     packageManifest: convertResult.PackageManifest,
                                     architecture: architecture,
                                     enableOptimization: enableOptimization) != 0)
                {
                    throw new LambdaToolsException($"Error executing the 'dotnet store' command", LambdaToolsException.LambdaErrorCode.StoreCommandError);
                }

                if (convertResult.ShouldDelete)
                {
                    File.Delete(convertResult.PackageManifest);
                }
            }

            // The artifact.xml file is generated by the "dotnet store" command that lists the packages that were added to the store.
            // It is required during a "dotnet publish" so the NuGet packages in the store will be filtered out.
            var artifactXmlPath = Path.Combine(storeOutputDirectory, "x64", targetFramework, "artifact.xml");
            if (!File.Exists(artifactXmlPath))
            {
                throw new LambdaToolsException($"Failed to find artifact.xml file in created local store.", LambdaToolsException.LambdaErrorCode.FailedToFindArtifactZip);
            }
            this.Logger.WriteLine($"Uploading runtime package store manifest to S3");
            var s3Key = await UploadFile(artifactXmlPath, $"{s3Prefix}artifact.xml");

            this.Logger.WriteLine($"Create zip file of runtime package store directory");
            var zipPath = Path.Combine(Path.GetTempPath(), $"{layerName}-{DateTime.UtcNow.Ticks}.zip");
            if (File.Exists(zipPath))
            {
                File.Delete(zipPath);
            }
            LambdaPackager.BundleDirectory(zipPath, tempRootPath, false, this.Logger);

            var result = new CreateLayerZipFileResult
            {
                ZipFile        = zipPath,
                LayerDirectory = optDirectory
            };

            var s3Bucket = this.GetStringValueOrDefault(this.S3Bucket, LambdaDefinedCommandOptions.ARGUMENT_S3_BUCKET, true);

            // Set the description field to the our JSON layer manifest file so when the tooling is used
            // to create a package of a Lambda function in the future the artifact.xml file can be used during "dotnet publish".
            result.Description = GeneratorRuntimePackageManifestLayerDescription(optDirectory, s3Bucket, s3Key, enableOptimization);

            var compatibleRuntime = LambdaUtilities.DetermineLambdaRuntimeFromTargetFramework(targetFramework);
            if (!string.IsNullOrEmpty(compatibleRuntime))
            {
                result.CompatibleRuntimes = new List <string>()
                {
                    compatibleRuntime
                };
            }

            return(result);
        }
示例#19
0
        public override async Task <bool> ExecuteAsync()
        {
            try
            {
                string projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, DefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);
                string zipArchivePath  = null;
                string publishLocation = null;
                string package         = this.GetStringValueOrDefault(this.Package, DefinedCommandOptions.ARGUMENT_PACKAGE, false);
                if (string.IsNullOrEmpty(package))
                {
                    string configuration   = this.GetStringValueOrDefault(this.Configuration, DefinedCommandOptions.ARGUMENT_CONFIGURATION, true);
                    string targetFramework = this.GetStringValueOrDefault(this.TargetFramework, DefinedCommandOptions.ARGUMENT_FRAMEWORK, true);

                    ValidateTargetFrameworkAndLambdaRuntime();

                    LambdaPackager.CreateApplicationBundle(this.DefaultConfig, this.Logger, this.WorkingDirectory, projectLocation, configuration, targetFramework, out publishLocation, ref zipArchivePath);
                    if (string.IsNullOrEmpty(zipArchivePath))
                    {
                        return(false);
                    }
                }
                else
                {
                    if (!File.Exists(package))
                    {
                        throw new LambdaToolsException($"Package {package} does not exist", LambdaToolsException.ErrorCode.InvalidPackage);
                    }
                    if (!string.Equals(Path.GetExtension(package), ".zip", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new LambdaToolsException($"Package {package} must be a zip file", LambdaToolsException.ErrorCode.InvalidPackage);
                    }

                    this.Logger.WriteLine($"Skipping compilation and using precompiled package {package}");
                    zipArchivePath = package;
                }


                using (var stream = new MemoryStream(File.ReadAllBytes(zipArchivePath)))
                {
                    var    s3Bucket = this.GetStringValueOrDefault(this.S3Bucket, DefinedCommandOptions.ARGUMENT_S3_BUCKET, false);
                    string s3Key    = null;
                    if (!string.IsNullOrEmpty(s3Bucket))
                    {
                        await Utilities.ValidateBucketRegionAsync(this.S3Client, s3Bucket);

                        var functionName = this.GetStringValueOrDefault(this.FunctionName, DefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true);
                        var s3Prefix     = this.GetStringValueOrDefault(this.S3Prefix, DefinedCommandOptions.ARGUMENT_S3_PREFIX, false);
                        s3Key = await Utilities.UploadToS3Async(this.Logger, this.S3Client, s3Bucket, s3Prefix, functionName, stream);
                    }


                    var currentConfiguration = await GetFunctionConfigurationAsync();

                    if (currentConfiguration == null)
                    {
                        this.Logger.WriteLine($"Creating new Lambda function {this.FunctionName}");
                        var createRequest = new CreateFunctionRequest
                        {
                            FunctionName = this.GetStringValueOrDefault(this.FunctionName, DefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true),
                            Description  = this.GetStringValueOrDefault(this.Description, DefinedCommandOptions.ARGUMENT_FUNCTION_DESCRIPTION, false),
                            Role         = this.GetStringValueOrDefault(this.Role, DefinedCommandOptions.ARGUMENT_FUNCTION_ROLE, true),
                            Handler      = this.GetStringValueOrDefault(this.Handler, DefinedCommandOptions.ARGUMENT_FUNCTION_HANDLER, true),
                            Publish      = this.GetBoolValueOrDefault(this.Publish, DefinedCommandOptions.ARGUMENT_FUNCTION_PUBLISH, false).GetValueOrDefault(),
                            MemorySize   = this.GetIntValueOrDefault(this.MemorySize, DefinedCommandOptions.ARGUMENT_FUNCTION_MEMORY_SIZE, true).GetValueOrDefault(),
                            Runtime      = this.GetStringValueOrDefault(this.Runtime, DefinedCommandOptions.ARGUMENT_FUNCTION_RUNTIME, true),
                            Timeout      = this.GetIntValueOrDefault(this.Timeout, DefinedCommandOptions.ARGUMENT_FUNCTION_TIMEOUT, true).GetValueOrDefault(),
                            KMSKeyArn    = this.GetStringValueOrDefault(this.KMSKeyArn, DefinedCommandOptions.ARGUMENT_KMS_KEY_ARN, false),
                            VpcConfig    = new VpcConfig
                            {
                                SubnetIds        = this.GetStringValuesOrDefault(this.SubnetIds, DefinedCommandOptions.ARGUMENT_FUNCTION_SUBNETS, false)?.ToList(),
                                SecurityGroupIds = this.GetStringValuesOrDefault(this.SecurityGroupIds, DefinedCommandOptions.ARGUMENT_FUNCTION_SECURITY_GROUPS, false)?.ToList()
                            }
                        };

                        var environmentVariables = this.GetKeyValuePairOrDefault(this.EnvironmentVariables, DefinedCommandOptions.ARGUMENT_ENVIRONMENT_VARIABLES, false);
                        if (environmentVariables != null && environmentVariables.Count > 0)
                        {
                            createRequest.Environment = new Model.Environment
                            {
                                Variables = environmentVariables
                            };
                        }

                        var deadLetterQueue = this.GetStringValueOrDefault(this.DeadLetterTargetArn, DefinedCommandOptions.ARGUMENT_DEADLETTER_TARGET_ARN, false);
                        if (!string.IsNullOrEmpty(deadLetterQueue))
                        {
                            createRequest.DeadLetterConfig = new DeadLetterConfig {
                                TargetArn = deadLetterQueue
                            };
                        }

                        if (s3Bucket != null)
                        {
                            createRequest.Code = new FunctionCode
                            {
                                S3Bucket = s3Bucket,
                                S3Key    = s3Key
                            };
                        }
                        else
                        {
                            createRequest.Code = new FunctionCode
                            {
                                ZipFile = stream
                            };
                        }


                        if (!this.SkipHandlerValidation && !string.IsNullOrEmpty(publishLocation))
                        {
                            createRequest.Handler = EnsureFunctionHandlerIsValid(publishLocation, createRequest.Handler);
                        }

                        try
                        {
                            await this.LambdaClient.CreateFunctionAsync(createRequest);

                            this.Logger.WriteLine("New Lambda function created");
                        }
                        catch (Exception e)
                        {
                            throw new LambdaToolsException($"Error creating Lambda function: {e.Message}", LambdaToolsException.ErrorCode.LambdaCreateFunction, e);
                        }
                    }
                    else
                    {
                        if (!this.SkipHandlerValidation && !string.IsNullOrEmpty(publishLocation))
                        {
                            if (!string.IsNullOrEmpty(this.Handler))
                            {
                                this.Handler = EnsureFunctionHandlerIsValid(publishLocation, this.Handler);
                            }
                            else
                            {
                                this.Handler = EnsureFunctionHandlerIsValid(publishLocation, currentConfiguration.Handler);
                            }
                        }

                        this.Logger.WriteLine($"Updating code for existing function {this.FunctionName}");

                        var updateCodeRequest = new UpdateFunctionCodeRequest
                        {
                            FunctionName = this.GetStringValueOrDefault(this.FunctionName, DefinedCommandOptions.ARGUMENT_FUNCTION_NAME, true)
                        };

                        if (s3Bucket != null)
                        {
                            updateCodeRequest.S3Bucket = s3Bucket;
                            updateCodeRequest.S3Key    = s3Key;
                        }
                        else
                        {
                            updateCodeRequest.ZipFile = stream;
                        }

                        try
                        {
                            await this.LambdaClient.UpdateFunctionCodeAsync(updateCodeRequest);
                        }
                        catch (Exception e)
                        {
                            throw new LambdaToolsException($"Error updating code for Lambda function: {e.Message}", LambdaToolsException.ErrorCode.LambdaUpdateFunctionCode, e);
                        }

                        await base.UpdateConfigAsync(currentConfiguration);
                    }
                }


                if (this.GetBoolValueOrDefault(this.PersistConfigFile, DefinedCommandOptions.ARGUMENT_PERSIST_CONFIG_FILE, false).GetValueOrDefault())
                {
                    this.SaveConfigFile();
                }

                return(true);
            }
            catch (LambdaToolsException e)
            {
                this.Logger.WriteLine(e.Message);
                this.LastToolsException = e;
                return(false);
            }
            catch (Exception e)
            {
                this.Logger.WriteLine($"Unknown error executing Lambda deployment: {e.Message}");
                this.Logger.WriteLine(e.StackTrace);
                return(false);
            }
        }
示例#20
0
        /// <summary>
        /// Determine the action to be done for the local path, like building a .NET Core package, then uploading the
        /// package to S3. The S3 key is returned to be updated in the template.
        /// </summary>
        /// <param name="templateDirectory"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        /// <exception cref="LambdaToolsException"></exception>
        private async Task <string> ProcessUpdatableResourceAsync(string templateDirectory, IUpdateResourceField field)
        {
            var localPath = field.GetLocalPath();

            if (!Path.IsPathRooted(localPath))
            {
                localPath = Path.Combine(templateDirectory, localPath);
            }

            bool   deleteArchiveAfterUploaded = false;
            string zipArchivePath;

            if (File.Exists(localPath))
            {
                if (field.IsCode && !string.Equals(Path.GetExtension(localPath), ".zip", StringComparison.OrdinalIgnoreCase))
                {
                    this.Logger.WriteLine($"Creating zip archive for {localPath} file");
                    zipArchivePath = GenerateOutputZipFilename(field);
                    LambdaPackager.BundleFiles(zipArchivePath, Path.GetDirectoryName(localPath), new string[] { localPath }, this.Logger);
                }
                else
                {
                    zipArchivePath = localPath;
                }
            }
            // If IsCode is false then the local path needs to point to a file and not a directory. When IsCode is true
            // it can point either to a file or a directory.
            else if (!field.IsCode && !File.Exists(localPath))
            {
                throw new LambdaToolsException($"File that the field {field.Resource.Name}/{field.Name} is pointing to doesn't exist", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingLocalPath);
            }
            else if (!Directory.Exists(localPath))
            {
                throw new LambdaToolsException($"Directory that the field {field.Resource.Name}/{field.Name} is pointing doesn't exist", LambdaToolsException.LambdaErrorCode.ServerlessTemplateMissingLocalPath);
            }
            // To maintain compatibility if the field is point to current directory or not set at all but a prepackaged zip archive is given
            // then use it as the package source.
            else if (IsCurrentDirectory(field.GetLocalPath()) && !string.IsNullOrEmpty(this.DefaultOptions.Package))
            {
                zipArchivePath = this.DefaultOptions.Package;
            }
            else if (field.IsCode)
            {
                if (IsDotnetProjectDirectory(localPath))
                {
                    zipArchivePath = await PackageDotnetProjectAsync(field, localPath);
                }
                else
                {
                    zipArchivePath = GenerateOutputZipFilename(field);
                    LambdaPackager.BundleDirectory(zipArchivePath, localPath, false, this.Logger);
                }
                deleteArchiveAfterUploaded = true;
            }
            else
            {
                throw new LambdaToolsException($"Unable to determine package action for the field {field.Resource.Name}/{field.Name}", LambdaToolsException.LambdaErrorCode.ServerlessTemplateUnknownActionForLocalPath);
            }

            string s3Key;

            using (var stream = File.OpenRead(zipArchivePath))
            {
                s3Key = await Utilities.UploadToS3Async(this.Logger, this.S3Client, this.S3Bucket, this.S3Prefix, Path.GetFileName(zipArchivePath), stream);
            }

            // Now that the temp zip file is uploaded to S3 clean up by deleting the temp file.
            if (deleteArchiveAfterUploaded)
            {
                try
                {
                    File.Delete(zipArchivePath);
                }
                catch (Exception e)
                {
                    this.Logger?.WriteLine($"Warning: Unable to delete temporary archive, {zipArchivePath}, after uploading to S3: {e.Message}");
                }
            }

            return(s3Key);
        }