Concepts

This is a brief overview of Sparta’s core concepts. Additional information regarding specific features is available from the menu.

Terms and Concepts

At a high level, Sparta transforms a go binary’s registered lambda functions into a set of independently addressable AWS Lambda functions . Additionally, Sparta provides microservice authors an opportunity to satisfy other requirements such as defining the IAM Roles under which their function will execute in AWS, additional infrastructure requirements, and telemetry and alerting information (via CloudWatch).

Service Name

Sparta applications are deployed as a single unit, using the ServiceName as a stable logical identifier. The ServiceName is used as your application’s CloudFormation StackName

stackName := "MyUniqueServiceName"
sparta.Main(stackName,
  "Simple Sparta application",
  myLambdaFunctions,
  nil,
  nil)

Lambda Function

A Sparta-compatible lambda is a standard AWS Lambda Go function. The following function signatures are supported:

  • func ()
  • func () error
  • func (TIn), error
  • func () (TOut, error)
  • func (context.Context) error
  • func (context.Context, TIn) error
  • func (context.Context) (TOut, error)
  • func (context.Context, TIn) (TOut, error)

where the TIn and TOut parameters represent encoding/json un/marshallable types. Supplying an invalid signature will produce a run time error as in:

ERRO[0000] Lambda function (Hello World) has invalid returns: handler
returns a single value, but it does not implement error exit status 1

Privileges

To support accessing other AWS resources in your go function, Sparta allows you to define and link IAM Roles with narrowly defined sparta.IAMRolePrivilege values. This allows you to define the minimal set of privileges under which your go function will execute. The Privilege.Resource field value may also be a StringExpression referencing a dynamically provisioned CloudFormation resource.

lambdaFn.RoleDefinition.Privileges = append(lambdaFn.RoleDefinition.Privileges,
  sparta.IAMRolePrivilege{
    Actions:  []string{"s3:GetObject", "s3:HeadObject"},
    Resource: "arn:aws:s3:::MyS3Bucket",
})

Permissions

To configure AWS Lambda Event Sources, Sparta provides both sparta.LambdaPermission and service-specific Permission types like sparta.CloudWatchEventsPermission. The service-specific Permission types automatically register your lambda function with the remote AWS service, using each service’s specific API.

cloudWatchEventsPermission := sparta.CloudWatchEventsPermission{}
cloudWatchEventsPermission.Rules = make(map[string]sparta.CloudWatchEventsRule, 0)
cloudWatchEventsPermission.Rules["Rate5Mins"] = sparta.CloudWatchEventsRule{
  ScheduleExpression: "rate(5 minutes)",
}
lambdaFn.Permissions = append(lambdaFn.Permissions, cloudWatchEventsPermission)

Decorators

Decorators are associated with either Lambda functions or the larger service workflow via WorkflowHooks. They are user-defined functions that provide an opportunity for your service to perform secondary actions such as automatically generating a CloudFormation Dashboard or automatically publish an S3 Artifact from your service.

Decorators are applied at provision time.

Interceptors

Interceptors are the runtime analog to Decorators. They are user-defined functions that are executed in the context of handling an event. They provide an opportunity for you to support cross-cutting concerns such as automatically registering XRayTraces that can capture service performance and log messages in the event of an error.

%%{init: {'themeVariables': { 'fontFamily': 'Inconsolata, Avenir, Helvetica, sans-serif', 'primaryColor': '#ff0000'}}}%% graph TD classDef stdOp fill:#FFF,stroke:#A00,stroke-width:2px; classDef userHook fill:#aaa,stroke:#A00,stroke-width:2px; execute[Execute] class execute stdOp; lookup[Lookup Function] class lookup stdOp; call[Call Function] class call stdOp; interceptorBegin[Interceptor Begin] class interceptorBegin userHook; populateLogger[Logger into context] class populateLogger stdOp; interceptorBeforeSetup[Interceptor BeforeSetup] class interceptorBeforeSetup userHook; interceptorAfterSetup[Interceptor AfterSetup] class interceptorAfterSetup userHook; populateContext[Populate context] class populateContext stdOp; unmarshalArgs[Introspect Arguments] class unmarshalArgs stdOp; interceptorBeforeDispatch[Interceptor BeforeDispatch] class interceptorBeforeDispatch userHook; callFunction[Call Function] class callFunction stdOp; interceptorAfterDispatch[Interceptor AfterDispatch] class interceptorAfterDispatch userHook; extractReturn[Extract Function Return] class extractReturn stdOp; interceptorComplete[Interceptor Complete] class interceptorComplete userHook; done[Done] class done stdOp; execute-->lookup lookup-->call call-->interceptorBegin interceptorBegin-->populateLogger populateLogger-->interceptorBeforeSetup interceptorBeforeSetup-->populateContext populateContext-->interceptorAfterSetup interceptorAfterSetup-->unmarshalArgs unmarshalArgs-->interceptorBeforeDispatch interceptorBeforeDispatch-->callFunction callFunction-->interceptorAfterDispatch interceptorAfterDispatch-->extractReturn extractReturn-->interceptorComplete interceptorComplete-->done
This diagram is rendered with Mermaid. Please open an issue if it doesn't render properly.

Dynamic Resources

Sparta applications can specify other AWS Resources (eg, SNS Topics) as part of their application. The dynamic resource outputs can be referenced by Sparta lambda functions via gocf.Ref and gocf.GetAtt functions.

snsTopicName := sparta.CloudFormationResourceName("SNSDynamicTopic")
snsTopic := &gocf.SNSTopic{
  DisplayName: gocf.String("Sparta Application SNS topic"),
})
lambdaFn, _ := sparta.NewAWSLambda(sparta.LambdaName(echoDynamicSNSEvent),
  echoDynamicSNSEvent,
  sparta.IAMRoleDefinition{})

lambdaFn.Permissions = append(lambdaFn.Permissions, sparta.SNSPermission{
  BasePermission: sparta.BasePermission{
    SourceArn: gocf.Ref(snsTopicName),
  },
})

Discovery

To support Sparta lambda functions discovering dynamically assigned AWS resource values, Sparta provides sparta.Discover. This function returns information about resources that a given entity specifies a DependsOn relationship.

func echoS3DynamicBucketEvent(ctx context.Context,
  s3Event awsLambdaEvents.S3Event) (*awsLambdaEvents.S3Event, error) {

  discoveryInfo, discoveryInfoErr := sparta.Discover()
  logger.Info().
    Interface("Event", s3Event).
    Interface("Discovery", discoveryInfo).
    Interface("DiscoveryErr", discoveryInfoErr).
  .Msg("Event received")

  // Use discoveryInfo to determine the bucket name to which RawMessage should be stored
  ...
}

Summary

Given a set of registered Sparta lambda functions, a typical provision build to create a new service follows this workflow. Items with dashed borders are opt-in user behaviors.

%%{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.

During provisioning, Sparta uses AWS Lambda-backed Custom Resources to support operations for which CloudFormation doesn’t yet support (eg, API Gateway creation).

Next Steps

Walk through a starting Sparta Application.