Chapter 7: Implementing a CI/CD Pipeline
- Implement a CI/CD pipeline for other Lambda functions with CodeBuild and CodePipeline.
Answer: The CI/CD pipeline for FindAllMovies
Lambda function can be implemented as follows:
version: 0.2 env: variables: S3_BUCKET: "movies-api-deployment-packages" PACKAGE: "github.com/mlabouardy/lambda-codepipeline" phases: install: commands: - mkdir -p "/go/src/$(dirname ${PACKAGE})" - ln -s "${CODEBUILD_SRC_DIR}" "/go/src/${PACKAGE}" - go get -u github.com/golang/lint/golint pre_build: commands: - cd "/go/src/${PACKAGE}" - go get -t ./... - golint -set_exit_status - go vet . - go test . build: commands: - GOOS=linux go build -o main - zip $CODEBUILD_RESOLVED_SOURCE_VERSION.zip main - aws s3 cp $CODEBUILD_RESOLVED_SOURCE_VERSION.zip s3://$S3_BUCKET/ post_build: commands: - aws lambda update-function-code --function-name FindAllMovies --s3-bucket $S3_BUCKET --s3-key $CODEBUILD_RESOLVED_SOURCE_VERSION.zip
The CI/CD pipeline for InsertMovie
Lambda function can be implemented as follows:
version: 0.2 env: variables: S3_BUCKET: "movies-api-deployment-packages" PACKAGE: "github.com/mlabouardy/lambda-codepipeline" phases: install: commands: - mkdir -p "/go/src/$(dirname ${PACKAGE})" - ln -s "${CODEBUILD_SRC_DIR}" "/go/src/${PACKAGE}" - go get -u github.com/golang/lint/golint pre_build: commands: - cd "/go/src/${PACKAGE}" - go get -t ./... - golint -set_exit_status - go vet . - go test . build: commands: - GOOS=linux go build -o main - zip $CODEBUILD_RESOLVED_SOURCE_VERSION.zip main - aws s3 cp $CODEBUILD_RESOLVED_SOURCE_VERSION.zip s3://$S3_BUCKET/ post_build: commands: - aws lambda update-function-code --function-name InsertMovie --s3-bucket $S3_BUCKET --s3-key $CODEBUILD_RESOLVED_SOURCE_VERSION.zip
The CI/CD pipeline for Updatemovie
Lambda function can be implemented as follows:
version: 0.2 env: variables: S3_BUCKET: "movies-api-deployment-packages" PACKAGE: "github.com/mlabouardy/lambda-codepipeline" phases: install: commands: - mkdir -p "/go/src/$(dirname ${PACKAGE})" - ln -s "${CODEBUILD_SRC_DIR}" "/go/src/${PACKAGE}" - go get -u github.com/golang/lint/golint pre_build: commands: - cd "/go/src/${PACKAGE}" - go get -t ./... - golint -set_exit_status - go vet . - go test . build: commands: - GOOS=linux go build -o main - zip $CODEBUILD_RESOLVED_SOURCE_VERSION.zip main - aws s3 cp $CODEBUILD_RESOLVED_SOURCE_VERSION.zip s3://$S3_BUCKET/ post_build: commands: - aws lambda update-function-code --function-name UpdateMovie --s3-bucket $S3_BUCKET --s3-key $CODEBUILD_RESOLVED_SOURCE_VERSION.zip
The CI/CD pipeline for DeleteMovie
Lambda function can be implemented as follows:
version: 0.2 env: variables: S3_BUCKET: "movies-api-deployment-packages" PACKAGE: "github.com/mlabouardy/lambda-codepipeline" phases: install: commands: - mkdir -p "/go/src/$(dirname ${PACKAGE})" - ln -s "${CODEBUILD_SRC_DIR}" "/go/src/${PACKAGE}" - go get -u github.com/golang/lint/golint pre_build: commands: - cd "/go/src/${PACKAGE}" - go get -t ./... - golint -set_exit_status - go vet . - go test . build: commands: - GOOS=linux go build -o main - zip $CODEBUILD_RESOLVED_SOURCE_VERSION.zip main - aws s3 cp $CODEBUILD_RESOLVED_SOURCE_VERSION.zip s3://$S3_BUCKET/ post_build: commands: - aws lambda update-function-code --function-name DeleteMovie --s3-bucket $S3_BUCKET --s3-key $CODEBUILD_RESOLVED_SOURCE_VERSION.zip
- Implement a similar workflow using Jenkins Pipeline.
Answer: We can use Jenkins parallel stages feature to run chunks of code in parallel as follows:
def bucket = 'movies-api-deployment-packages' node('slave-golang'){ stage('Checkout'){ checkout scm sh 'go get -u github.com/golang/lint/golint' sh 'go get -t ./...' } stage('Test'){ parallel { stage('FindAllMovies') { sh 'cd findAll' sh 'golint -set_exit_status' sh 'go vet .' sh 'go test .' } stage('DeleteMovie') { sh 'cd delete' sh 'golint -set_exit_status' sh 'go vet .' sh 'go test .' } stage('UpdateMovie') { sh 'cd update' sh 'golint -set_exit_status' sh 'go vet .' sh 'go test .' } stage('InsertMovie') { sh 'cd insert' sh 'golint -set_exit_status' sh 'go vet .' sh 'go test .' } } } stage('Build'){ parallel { stage('FindAllMovies') { sh 'cd findAll' sh 'GOOS=linux go build -o main main.go' sh "zip findAll-${commitID()}.zip main" } stage('DeleteMovie') { sh 'cd delete' sh 'GOOS=linux go build -o main main.go' sh "zip delete-${commitID()}.zip main" } stage('UpdateMovie') { sh 'cd update' sh 'GOOS=linux go build -o main main.go' sh "zip update-${commitID()}.zip main" } stage('InsertMovie') { sh 'cd insert' sh 'GOOS=linux go build -o main main.go' sh "zip insert-${commitID()}.zip main" } } } stage('Push'){ parallel { stage('FindAllMovies') { sh 'cd findAll' sh "aws s3 cp findAll-${commitID()}.zip s3://${bucket}" } stage('DeleteMovie') { sh 'cd delete' sh "aws s3 cp delete-${commitID()}.zip s3://${bucket}" } stage('UpdateMovie') { sh 'cd update' sh "aws s3 cp update-${commitID()}.zip s3://${bucket}" } stage('InsertMovie') { sh 'cd insert' sh "aws s3 cp insert-${commitID()}.zip s3://${bucket}" } } } stage('Deploy'){ parallel { stage('FindAllMovies') { sh 'cd findAll' sh "aws lambda update-function-code --function-name FindAllMovies \ --s3-bucket ${bucket} \ --s3-key findAll-${commitID()}.zip \ --region us-east-1" } stage('DeleteMovie') { sh 'cd delete' sh "aws lambda update-function-code --function-name DeleteMovie \ --s3-bucket ${bucket} \ --s3-key delete-${commitID()}.zip \ --region us-east-1" } stage('UpdateMovie') { sh 'cd update' sh "aws lambda update-function-code --function-name UpdateMovie \ --s3-bucket ${bucket} \ --s3-key update-${commitID()}.zip \ --region us-east-1" } stage('InsertMovie') { sh 'cd insert' sh "aws lambda update-function-code --function-name InsertMovie \ --s3-bucket ${bucket} \ --s3-key insert-${commitID()}.zip \ --region us-east-1" } } } } def commitID() { sh 'git rev-parse HEAD > .git/commitID' def commitID = readFile('.git/commitID').trim() sh 'rm .git/commitID' commitID }
- Implement the same pipeline with CircleCI.
Answer: CircleCI workflow option can be used to define a collection of build jobs:
version: 2 jobs: build_findall: docker: - image: golang:1.8 working_directory: /go/src/github.com/mlabouardy/lambda-circleci build_dir: findAll environment: S3_BUCKET: movies-api-deployment-packages steps: - checkout - run: name: Install AWS CLI & Zip command: | apt-get update apt-get install -y zip python-pip python-dev pip install awscli - run: name: Test command: | go get -u github.com/golang/lint/golint go get -t ./... golint -set_exit_status go vet . go test . - run: name: Build command: | GOOS=linux go build -o main main.go zip $CIRCLE_SHA1.zip main - run: name: Push command: aws s3 cp $CIRCLE_SHA1.zip s3://$S3_BUCKET - run: name: Deploy command: | aws lambda update-function-code --function-name FindAllMovies \ --s3-bucket $S3_BUCKET \ --s3-key $CIRCLE_SHA1.zip --region us-east-1 build_insert: docker: - image: golang:1.8 working_directory: /go/src/github.com/mlabouardy/lambda-circleci build_dir: insert environment: S3_BUCKET: movies-api-deployment-packages steps: - checkout - run: name: Install AWS CLI & Zip command: | apt-get update apt-get install -y zip python-pip python-dev pip install awscli - run: name: Test command: | go get -u github.com/golang/lint/golint go get -t ./... golint -set_exit_status go vet . go test . - run: name: Build command: | GOOS=linux go build -o main main.go zip $CIRCLE_SHA1.zip main - run: name: Push command: aws s3 cp $CIRCLE_SHA1.zip s3://$S3_BUCKET - run: name: Deploy command: | aws lambda update-function-code --function-name InsertMovie \ --s3-bucket $S3_BUCKET \ --s3-key $CIRCLE_SHA1.zip --region us-east-1 build_update: ... build_delete: ... workflows: version: 2 build_api: jobs: - build_findall - build_insert - build_update - build_delete
- Add a new stage to the existing pipeline to publish a new version if the current git branch is the master.
Answer:
version: 2 jobs: build: docker: - image: golang:1.8 working_directory: /go/src/github.com/mlabouardy/lambda-circleci environment: S3_BUCKET: movies-api-deployment-packages steps: - checkout - run: name: Install AWS CLI & Zip ... - run: name: Test ... - run: name: Build ... - run: name: Push ... - run: name: Deploy ... - run: name: Publish command: | if [ $CIRCLE_BRANCH = 'master' ]; then aws lambda publish-version --function-name FindAllMovies \ --description $GIT_COMMIT_DESC --region us-east-1 fi environment: GIT_COMMIT_DESC: git log --format=%B -n 1 $CIRCLE_SHA1
- Configure the pipeline to send a notification on a Slack channel every time a new Lambda function is deployed or updated.
Answer: You can use the Slack API to post a message to a Slack channel at the end of the deployment step:
- run: name: Deploy command: | aws lambda update-function-code --function-name FindAllMovies \ --s3-bucket $S3_BUCKET \ --s3-key $CIRCLE_SHA1.zip --region us-east-1 curl -X POST -d '{"token":"$TOKEN", "channel":"$CHANNEL", "text":"FindAllMovies has been updated"}' \ http://slack.com/api/chat.postMessage