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