// Given the zipped binary in packagePath, upload the primary code bundle // and optional S3 site resources iff they're defined. func createUploadStep(packagePath string) workflowStep { return func(ctx *workflowContext) (workflowStep, error) { var uploadErrors []error var wg sync.WaitGroup // We always need to upload the primary binary wg.Add(1) go func() { defer wg.Done() logFilesize("Lambda function deployment package size", packagePath, ctx.logger) // Create the S3 key... zipS3URL, zipS3URLErr := uploadLocalFileToS3(packagePath, "", ctx) if nil != zipS3URLErr { uploadErrors = append(uploadErrors, zipS3URLErr) } else { ctx.s3CodeZipURL = newS3UploadURL(zipS3URL) } }() // S3 site to compress & upload if nil != ctx.s3SiteContext.s3Site { wg.Add(1) go func() { defer wg.Done() tempName := fmt.Sprintf("%s-S3Site.zip", ctx.serviceName) tmpFile, err := temporaryFile(tempName) if err != nil { uploadErrors = append(uploadErrors, errors.New("Failed to create temporary S3 site archive file")) return } // Add the contents to the Zip file zipArchive := zip.NewWriter(tmpFile) absResourcePath, err := filepath.Abs(ctx.s3SiteContext.s3Site.resources) if nil != err { uploadErrors = append(uploadErrors, err) return } ctx.logger.WithFields(logrus.Fields{ "S3Key": path.Base(tmpFile.Name()), "Source": absResourcePath, }).Info("Creating S3Site archive") err = spartaZip.AddToZip(zipArchive, absResourcePath, absResourcePath, ctx.logger) if nil != err { uploadErrors = append(uploadErrors, err) return } zipArchive.Close() // Upload it & save the key s3SiteLambdaZipURL, s3SiteLambdaZipURLErr := uploadLocalFileToS3(tmpFile.Name(), "", ctx) if s3SiteLambdaZipURLErr != nil { uploadErrors = append(uploadErrors, s3SiteLambdaZipURLErr) } else { ctx.s3SiteContext.s3UploadURL = newS3UploadURL(s3SiteLambdaZipURL) } ctx.registerFileCleanupFinalizer(tmpFile.Name()) }() } wg.Wait() if len(uploadErrors) > 0 { errorText := "Encountered multiple errors during upload:\n" for _, eachError := range uploadErrors { errorText += fmt.Sprintf("%s%s\n", errorText, eachError.Error()) return nil, errors.New(errorText) } } return ensureCloudFormationStack(), nil } }
// Build and package the application func createPackageStep() workflowStep { return func(ctx *workflowContext) (workflowStep, error) { // PreBuild Hook if ctx.workflowHooks != nil { preBuildErr := callWorkflowHook(ctx.workflowHooks.PreBuild, ctx) if nil != preBuildErr { return nil, preBuildErr } } sanitizedServiceName := sanitizedName(ctx.serviceName) executableOutput := fmt.Sprintf("%s.lambda.amd64", sanitizedServiceName) buildErr := buildGoBinary(executableOutput, ctx.buildTags, ctx.linkFlags, ctx.logger) if nil != buildErr { return nil, buildErr } // Cleanup the temporary binary defer func() { errRemove := os.Remove(executableOutput) if nil != errRemove { ctx.logger.WithFields(logrus.Fields{ "File": executableOutput, "Error": errRemove, }).Warn("Failed to delete binary") } }() // Binary size logFilesize("Executable binary size", executableOutput, ctx.logger) // PostBuild Hook if ctx.workflowHooks != nil { postBuildErr := callWorkflowHook(ctx.workflowHooks.PostBuild, ctx) if nil != postBuildErr { return nil, postBuildErr } } tmpFile, err := temporaryFile(fmt.Sprintf("%s-code.zip", sanitizedServiceName)) if err != nil { return nil, err } ctx.logger.WithFields(logrus.Fields{ "TempName": tmpFile.Name(), }).Info("Creating code ZIP archive for upload") lambdaArchive := zip.NewWriter(tmpFile) // Archive Hook if ctx.workflowHooks != nil && ctx.workflowHooks.Archive != nil { archiveErr := ctx.workflowHooks.Archive(ctx.workflowHooksContext, ctx.serviceName, lambdaArchive, ctx.awsSession, ctx.noop, ctx.logger) if nil != archiveErr { return nil, archiveErr } } // File info for the binary executable readerErr := spartaZip.AddToZip(lambdaArchive, executableOutput, "", ctx.logger) if nil != readerErr { return nil, readerErr } // Add the string literal adapter, which requires us to add exported // functions to the end of index.js. These NodeJS exports will be // linked to the AWS Lambda NodeJS function name, and are basically // automatically generated pass through proxies to the golang HTTP handler. shimErr := writeNodeJSShim(ctx.serviceName, executableOutput, ctx.lambdaAWSInfos, lambdaArchive, ctx.logger) if nil != shimErr { return nil, shimErr } // Next embed the custom resource scripts into the package. ctx.logger.Debug("Embedding CustomResource scripts") customResourceErr := writeCustomResources(lambdaArchive, ctx.logger) if nil != customResourceErr { return nil, customResourceErr } archiveCloseErr := lambdaArchive.Close() if nil != archiveCloseErr { return nil, archiveCloseErr } tempfileCloseErr := tmpFile.Close() if nil != tempfileCloseErr { return nil, tempfileCloseErr } return createUploadStep(tmpFile.Name()), nil } }