Beispiel #1
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);
        }
        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 projectLocation = this.GetStringValueOrDefault(this.ProjectLocation, CommonDefinedCommandOptions.ARGUMENT_PROJECT_LOCATION, false);

            Lambda.PackageType packageType = LambdaUtilities.DeterminePackageType(this.GetStringValueOrDefault(this.PackageType, LambdaDefinedCommandOptions.ARGUMENT_PACKAGE_TYPE, false));
            if (packageType == Lambda.PackageType.Image)
            {
                var pushResults = await PushLambdaImageAsync();

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

                    return(false);
                }
            }
            else
            {
                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();
                }

                // 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));

                    // 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);
                    }
                }

                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);
        }