Example #1
0
// 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
	}
}
Example #2
0
// 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
	}
}