kanarya

package module
v1.1.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 4, 2023 License: MIT Imports: 11 Imported by: 0

README

Kanarya

kanarya

GoDoc Supported Version Go Report Card Maintainability License

Kanarya is canary in Turkish.

kanarya is a Go module that takes care of canary deployments in AWS Lambda. This module acts as a wrapper on top of AWS Go SDK and makes easier to implement canary deployments in your lambda projects. kanarya can be used locally in your Go projects, can be implemented as a CLI tool, or can be used on CI, depending on your needs.

Install

go get github.com/msdundar/kanarya@latest

kanarya uses Go Modules to manage dependencies, and supports Go versions >=1.14.x.

Usage

Credentials

kanarya relies on AWS Shared Configuration. You need to set your credentials before using the package.

  • Set AWS_REGION environment variable to the default region.
  • Either set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY - or define your credentials in ~/.aws/credentials file.

For more details follow official AWS Guidelines.

Implementation

Create S3 and Lambda instances to use in AWS operations:

s3Client     := kanarya.S3Client("AWS_REGION_TO_DEPLOY")
lambdaClient := kanarya.LambdaClient("AWS_REGION_TO_DEPLOY")

Create a deployment package by using kanarya.LambdaPackage struct:

lambdaPackage := kanarya.LambdaPackage{
  Location: "file/path/for/lambda/package/index.zip",
  Function: kanarya.LambdaFunction{
    Name: "YOUR_LAMBDA_NAME",
  },
  Bucket: kanarya.LambdaBucket{
    Name: "YOUR_BUCKET_NAME",
    Key:  "upload/folder/in/s3bucket/index.zip",
  },
  Alias: kanarya.LambdaAlias{
    Name: "YOUR_LAMBDA_ALIAS", // alias used by clients
  },
}

Upload deployment package to S3:

_, err := kanarya.UploadToS3(s3Client, lambdaPackage)

Update function code located in $LATEST:

_, err := kanarya.UpdateFunctionCode(lambdaClient, lambdaPackage)

Publish a new version for shifting traffic later:

resp, err := kanarya.PublishNewVersion(lambdaClient, lambdaPackage)
newVersion := resp.Version

Create a JSON payload to use in health check requests:

request := yourRequestStruct{Something: "some value"}
payload, err := json.Marshal(request)

Adjust this JSON according to the request-body expectations of your lambda.

Start gradual deployment:

oldVersion, err := kanarya.GradualRollOut(
  lambdaClient,
  lambdaPackage,
  newVersion,
  0.1000000, // roll out rate in each step. 0.1 equals to 10%.
  10, // number of health checks you would like to run on each step.
  60, // number of seconds to sleep on each step.
  payload,
)

If there are errors during the gradual rollout, auto-rollback to the previous healthy version:

if err != nil {
  kanarya.FullRollOut(lambdaClient, lambdaPackage, oldVersion)
  os.Exit(1)
}

If gradual rollout is successful, then fully roll out the new version:

_, err := kanarya.FullRollOut(lambdaClient, lambdaPackage, newVersion)

And that's it! You can combine the example above for cross-regional deployments, by updating the s3Client or lambdaClient on the fly with a new region.

Development

Local testing
  • Test environment can be set up with Terraform and Docker Compose. Configuration for each can be found in the repository.
  • First, run docker-compose up to start localstack.
  • Then run terraform init & terraform apply to create resources locally on localstack.
  • Finally, run go test to run unit tests.
Linter
  • golangci-lint is integrated in the CI. Run golangci-lint run locally to make sure no linting issues exist.

Contributions

  1. Fork the repo
  2. Clone the fork (git clone git@github.com:YOUR_USERNAME/kanarya.git && cd kanarya)
  3. Create your feature branch (git checkout -b my-new-feature)
  4. Make changes and add them (git add --all)
  5. Commit your changes (git commit -m 'Add some feature')
  6. Push to the branch (git push origin my-new-feature)
  7. Create a pull request

License

See LICENSE.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GradualRollOut

func GradualRollOut(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
	version string,
	traffic float64,
	runs int,
	sleep time.Duration,
	payload []byte,
) (string, error)

GradualRollOut is the main function for gradual rollouts. It takes a version argument that refers to the new version that traffic is going to be shifted for. traffic stands for the amount of traffic to be shifted on each step. For example, 0.05 stands for 5%, and 20 steps is going to be required for a full rollout (100/5=20). runs argument stands for number of health checks you would like to run on each step. sleep is number of seconds to sleep on each step. And finally, payload can be used as a request body to send when running health checks.

func HealthCheck

func HealthCheck(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
	version string,
	payload []byte,
) ([]int64, error)

HealthCheck runs health checks on a given lambda version or alias. It can take a payload argument that be used to satisfy request body expectations of a lambda.

func LambdaClient

func LambdaClient(region string) *lambda.Lambda

LambdaClient initializes a new lambda client that can be used in lambda actions. Endpoint will be set to a localstack endpoint when running tests, otherwise it will use the default AWS location.

func S3Client

func S3Client(region string) *s3.S3

S3Client initializes a new S3 client that can be used in S3 actions. Endpoint will be set to a localstack endpoint when running tests, otherwise it will use the default AWS location. Localstack requires S3ForcePathStyle is set to true, however, on production it will be set to false.

Types

type HealthCheckResponse

type HealthCheckResponse struct {
	StatusCode int64  `json:"statusCode"`
	Body       string `json:"body"`
}

A HealthCheckResponse is used to reflect structure of a health check call targeting a lambda function. StatusCode stands for the HTTP status code returned by AWS. On the other hand, there can be an additional status code returned by applications in the response body, but this is optional.

type LambdaAlias

type LambdaAlias struct {
	Name string
}

A LambdaAlias is used to reflect structure of a lambda alias. Lambda aliases are used in traffic shifthing and gradual deployments.

type LambdaBucket

type LambdaBucket struct {
	Name string
	Key  string
}

A LambdaBucket is used to reflect structure of an AWS S3 bucket that is used to store lambda functions.

type LambdaFunction

type LambdaFunction struct {
	Name string
}

A LambdaFunction is used to reflect structure of an AWS Lambda function.

type LambdaNewVersionResponse

type LambdaNewVersionResponse struct {
	FunctionArn      string
	LastModified     string
	LastUpdateStatus string
	State            string
	Version          string
}

LambdaNewVersionResponse is used to represent the response returned from PublishNewVersion. The main use case of this struct is to check version number of the latest published version.

func PublishNewVersion

func PublishNewVersion(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
) (LambdaNewVersionResponse, error)

PublishNewVersion publishes a new lambda version and returns a LambdaNewVersionResponse. One of the most important fields in the response is Version, that is the new published version and is used in later gradual deployment steps.

type LambdaPackage

type LambdaPackage struct {
	Location string
	Function LambdaFunction
	Bucket   LambdaBucket
	Alias    LambdaAlias
}

A LambdaPackage is an internal representation of a lambda function that can be deployed gradually. LambdaPackage is a composite struct made of other structs.

type LambdaUpdateAliasResponse

type LambdaUpdateAliasResponse struct {
	AliasArn       string
	AliasName      string
	CurrentVersion string
	NewVersion     string
	CurrentWeight  float64
}

LambdaUpdateAliasResponse is used to represent the response returned from UpdateAlias. The main use case of this struct is to track traffic shifted from one version to another.

func FullRollOut

func FullRollOut(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
	version string,
) (LambdaUpdateAliasResponse, error)

FullRollOut resets the routing config and shifts whole traffic from the new version to the alias. It returns a LambdaUpdateAliasResponse struct.

func UpdateAlias

func UpdateAlias(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
	version string,
	traffic float64,
) (LambdaUpdateAliasResponse, error)

UpdateAlias is used to shift traffic from one version to another. version argument is the version to shift some traffic, and traffic argument stands for the amount of traffic to be shifted. For example, 0.2 means 20% traffic shift to the specified version.

type LambdaUpdateFunctionResponse

type LambdaUpdateFunctionResponse struct {
	FunctionArn      string
	FunctionName     string
	LastUpdateStatus string
}

LambdaUpdateFunctionResponse is used to reflect the response returned from UpdateFunctionCode function.

func UpdateFunctionCode

func UpdateFunctionCode(
	client *lambda.Lambda,
	lambdaPackage LambdaPackage,
) (LambdaUpdateFunctionResponse, error)

UpdateFunctionCode updates the function code of a lambda located on $LATEST. In later stages, new versions can be created from the updated $LATEST.

type S3UploadResponse

type S3UploadResponse struct {
	ETag string
}

An S3UploadResponse is used to represent structure of a response returned from an UploadToS3 call. It supports S3 buckets with versioning enabled or disabled.

func UploadToS3

func UploadToS3(client *s3.S3, lambdaPackage LambdaPackage) (S3UploadResponse, error)

UploadToS3 uploads a given lambda package to an S3 bucket. Later, the object in the bucket can be used for deploying the lambda function.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL