Build-Time Decorators

Sparta uses build-time decorators to annotate the CloudFormation template with additional functionality.

While Sparta tries to provide workflows common across service lifecycles, it may be the case that an application requires additional functionality or runtime resources.

To support this, Sparta allows you to customize the build pipeline via WorkflowHooks structure. These hooks are called at specific points in the provision lifecycle and support augmenting the standard pipeline:

%%{init: {'themeVariables': { 'fontFamily': 'Inconsolata, Avenir, Helvetica, sans-serif', 'primaryColor': '#ff0000'}}}%% graph TD classDef stdOp fill:#FFF,stroke:#A00,stroke-width:2px; classDef userHook fill:#B5B2A1,stroke:#A00,stroke-width:2px,stroke-dasharray: 5, 5; iam[Verify Static IAM Roles] class iam stdOp; preBuild[WorkflowHook - PreBuild] class preBuild userHook; compile[Compile for AWS Lambda Container] postBuild[WorkflowHook - PostBuild] class postBuild userHook; package[ZIP archive] class package stdOp; package_oci[OCI Build] class package_oci stdOp; userArchive[WorkflowHook - Archive] class userArchive userHook; upload[Upload Archive to S3] uploadAssets[Upload S3 Assets] packageAssets[Conditionally ZIP S3 Site Assets] class upload,packageAssets,uploadAssets stdOp; preMarshall[WorkflowHook - PreMarshall] class preMarshall userHook; generate[Marshal to CloudFormation] class generate stdOp; decorate[Call Lambda Decorators - Dynamic AWS Resources] class decorate stdOp; serviceDecorator[Service Decorator] class serviceDecorator userHook; postMarshall[WorkflowHook - PostMarshall] class postMarshall stdOp; uploadTemplate[Upload Template to S3] updateStack[Create/Update Stack] inplaceUpdates[In-place λ code updates] wait[Wait for Complete/Failure Result] class uploadTemplate,updateStack,inplaceUpdates,wait stdOp; iam-->preBuild preBuild-->|go|compile compile-->postBuild postBuild-->package postBuild-->package_oci package-->packageAssets package_oci-->packageAssets package-->userArchive package_oci-->upload userArchive-->upload packageAssets-->uploadAssets uploadAssets-->generate upload-->generate generate-->preMarshall preMarshall-->decorate decorate-->serviceDecorator serviceDecorator-->postMarshall postMarshall-->uploadTemplate uploadTemplate-->|standard|updateStack uploadTemplate-->|inplace|inplaceUpdates updateStack-->wait
This diagram is rendered with Mermaid. Please open an issue if it doesn't render properly.

The following sections describe the types of WorkflowHooks available. All hooks accept a context map[string]interface{} as their first parameter. Sparta treats this as an opaque property bag that enables hooks to communicate state.

WorkflowHook Types

Builder Hooks

BuilderHooks share the WorkflowHook signature:

type WorkflowHook func(ctx context.Context,
  serviceName string,
  S3Bucket gocf.Stringable,
  buildID string,
  awsSession *session.Session,
  noop bool,
  logger *zerolog.Logger) (context.Context, error)

These functions include:

  • PreBuild
  • PostBuild
  • PreMarshall
  • PostMarshall

Archive Hook

The ArchiveHook allows a service to add custom resources to the ZIP archive and have the signature:

type ArchiveHook func(ctx context.Context,
  serviceName string,
  zipWriter *zip.Writer,
  awsSession *session.Session,
  noop bool,
  logger *zerolog.Logger) (context.Context, error)

This function is called after Sparta has written the standard resources to the *zip.Writer stream.

Rollback Hook

The RollbackHook is called iff the provision operation fails and has the signature:


type RollbackHook func(context map[string]interface{},
  serviceName string,
  awsSession *session.Session,
  noop bool,
  logger *zerolog.Logger)

Using WorkflowHooks

To use the Workflow Hooks feature, initialize a WorkflowHooks structure with 1 or more hook functions and call sparta.MainEx.

Available Decorators

  • Application Load Balancer
  • The ApplicationLoadBalancerDecorator allows you to expose lambda functions as Application Load Balancer targets. This can be useful to provide HTTP(S) access to one or more Lambda functions without requiring an API-Gateway service. Lambda Function Application Load Balancer (ALB) lambda targets must satisfy a prescribed Lambda signature: import ( awsEvents "github.com/aws/aws-lambda-go/events" ) func(context.Context, awsEvents.ALBTargetGroupRequest) awsEvents.ALBTargetGroupResponse See the ALBTargetGroupRequest and ALBTargetGroupResponse godoc entries for more information. An example ALB-eligible target function might look like:

  • CloudMap Service Discovery
  • The CloudMapServiceDecorator allows your service to register a service instance for your application. For example, an application that provisions a SQS queue and an AWS Lambda function that consumes messages from that queue may need a way for the Lambda function to discover the dynamically provisioned queue. Sparta supports an environment-based discovery service but that discovery is limited to a single Service. The CloudMapServiceDecorator leverages the CloudMap service to support intra- and inter-service resource discovery.

  • CloudFront Distribution
  • The CloudFrontDistributionDecorator associates a CloudFront Distribution with your S3-backed website. It is implemented as a ServiceDecoratorHookHandler as a single service can only provision one CloudFront distribution. Sample usage: //////////////////////////////////////////////////////////////////////////////// // CloudFront settings const subdomain = "mySiteSubdomain" // The domain managed by Route53. const domainName = "myRoute53ManagedDomain.net" // The site will be available at // https://mySiteSubdomain.myRoute53ManagedDomain.net // The S3 bucketname must match the subdomain.domain // name pattern to serve as a CloudFront Distribution target var bucketName = fmt.

  • Lambda Versioning Decorator
  • TODO: LambdaVersioningDecorator

  • Publishing Outputs
  • CloudFormation stack outputs can be used to advertise information about a service. Sparta provides different publishing output decorators depending on the type of CloudFormation resource output: Ref: PublishRefOutputDecorator Fn::Att: PublishAttOutputDecorator Publishing Resource Ref Values For example, to publish the dynamically lambda resource name for a given AWS Lambda function, use PublishRefOutputDecorator such as: lambdaFunctionName := "Hello World" lambdaFn, _ := sparta.NewAWSLambda(lambdaFunctionName, helloWorld, sparta.IAMRoleDefinition{}) lambdaFn.Decorators = append(lambdaFn.Decorators, spartaDecorators.PublishRefOutputDecorator(fmt.Sprintf("%s FunctionName", lambdaFunctionName), fmt.

  • S3 Artifact Publisher
  • The S3ArtifactPublisherDecorator enables a service to publish objects to S3 locations as part of the service lifecycle. This decorator is implemented as a ServiceDecoratorHookHandler which is supplied to MainEx. For example: hooks := &sparta.WorkflowHooks{} payloadData := map[string]interface{}{ "SomeValue": gocf.Ref("AWS::StackName"), } serviceHook := spartaDecorators.S3ArtifactPublisherDecorator(gocf.String("MY-S3-BUCKETNAME"), gocf.Join("", gocf.String("metadata/"), gocf.Ref("AWS::StackName"), gocf.String(".json")), payloadData) hooks.ServiceDecorators = []sparta.ServiceDecoratorHookHandler{serviceHook}

  • Dynamic Infrastructure
  • In addition to provisioning AWS Lambda functions, Sparta supports the creation of other CloudFormation Resources. This enables a service to move towards immutable infrastructure, where the service and its infrastructure requirements are treated as a logical unit. For instance, consider the case where two developers are working in the same AWS account. Developer 1 is working on analyzing text documents. Their lambda code is triggered in response to uploading sample text documents to S3.

Notes

  • Workflow hooks can be used to support Dockerizing your application
  • You may need to add custom CLI commands to fully support Docker
  • Enable --level debug for detailed workflow hook debugging information