Skip to main content

AWS Lambda & Custom Webhook Stages

Introduction

Note: This document is obsolete.

As of Spinnaker 1.23 (Armory 2.23), there is now a Lambda plugin available - see the plugin GitHub repo for documentation. Published Feb 2020, Enabling Lambda in the UI: Refer to the AWS blogpost for enabling Lambda with Spinnaker   -- Original KB Follows -- Back in December of 2018, AWS added supported for Lambda and it was released in Spinnaker OSS v1.12. The challenge, however, was that there was no UI (Deck) components to support the usage of Lambda. Instead, a README.md was published that specified the API to make changes to Lambda. This document will show you how to create a custom webhook stage that utilizes the Lambda API built into Clouddriver. This document also assumes that you have configured an AWS account. If you need more instructions on configuring your AWS account please refer to either Deploying to AWS from Spinnaker (using IAM instance roles) or Deploying to AWS from Spinnaker (using IAM credentials) **Note: **This is a proof of concept implementation and is not recommended for production use!

Prerequisites

Access to your AWS Environment

Instructions

Enable AWS Lambda in Spinnaker

First, we need to enable Lambda by adding a clouddriver-local.yml file to your hal config profiles directory e.g. .hal/default/profiles/clouddriver-local.yml (As of Halyard OSS version 1.20, there is no support for adding Lambda configurations via hal commands.) 

aws:
lambda:
enabled: true
accounts:
- name: aws
lambdaEnabled: true
providerVersion: V1
accountId: '555692138000'
regions:
- name: us-east-1
- name: us-west-2
assumeRole: role/awaylambda05242019-managedrole

You can check your configuration by running hal deploy apply and check the clouddriver logs (e.g. kubectl logs -f -n spinnaker spin-clouddriver-xxxxx) for any start-up errors. Refer to the Debugging section for checking your deployment.  Adding Custom Webhook Stage Next, we need to add a custom webhook stage. Alternatively, you could use a simple webhook stage. Following this guide on Custom Webhook Stages, you should add .hal/default/profiles/orca-local.yml with the following content:

webhook:
preconfigured:
- label: Lambda - Get Functions
type: lambdaGetFunctions
enabled: true
description: Get Lambda Functions
method: GET
url: http://spin-clouddriver:7002/functions
customHeaders:
Accept:
- "application/json"
- label: Lambda - Update Function Code
type: lambdaUpdateFunctionCode
enabled: true
description: Update Lambda Function Code
method: POST
url: http://spin-clouddriver:7002/aws/ops/updateLambdaFunctionCode
customHeaders:
Accept:
- "application/json"
Content-Type:
- "application/json"
payload: |-
{
"credentials": "${#root['parameterValues']['account']}",
"region": "${#root['parameterValues']['region']}",
"functionName": "${#root['parameterValues']['functionName']}",
"s3bucket": "${#root['parameterValues']['bucketname']}",
"s3key": "${#root['parameterValues']['key']}",
"publish": "${#root['parameterValues']['publish']}"
}
parameters:
- label: Spinnaker Account Name
name: account
type: string
- label: Region
name: region
type: string
- label: Function Name
name: functionName
type: string
- label: S3 Bucket Name
name: bucketname
type: string
- label: S3 Key
name: key
type: string
- label: Publish
name: publish
type: string
- label: Lambda - Update Function Configuration
type: lambdaUpdateFunctionConfig
enabled: true
description: Update Lambda Function Configuration
method: POST
url: http://spin-clouddriver:7002/aws/ops/updateLambdaFunctionConfiguration
customHeaders:
Accept:
- "application/json"
Content-Type:
- "application/json"
payload: |-
{
"region": "${#root['parameterValues']['region']}",
"functionName": "${#root['parameterValues']['functionName']}",
"description": "${#root['parameterValues']['description']}",
"credentials": "${#root['parameterValues']['account']}",
"role": "${#root['parameterValues']['roleARN']}",
"timeout": "${#root['parameterValues']['timeout']}"
}
parameters:
- label: Region
name: region
type: string
- label: Function Name
name: functionName
type: string
- label: Description
name: description
type: string
- label: Spinnaker Account Name
name: account
type: string
- label: Role ARN
name: roleARN
type: string
- label: Timeout (secs)
name: timeout
type: string

You might notice that the parameterValues are being referenced with a #root helper function. This is to help ensure that Orca can evaluate the expressions using the parameter values from within the stage. Run hal deploy apply and you should see a new orca pod. And if you want to verify you can exec into orca, and find the orca-local.yml file under /opt/spinnaker/config/l

Creating your Pipeline

After you’ve deployed your changes to orca, you should now be able to see the new stages when configuring your pipeline. Simply select the stage, and provide the values: example below: Referencing values from Get Functions You can use SpEL to get values from the response to the Lambda - Get Functions stage. Here is an example SpEL expression to get a function name. ${#stage("Lambda - Get Functions").context.webhook.body[0].functionName} Check the ‘source’ of the pipeline to see the contents of the webhook - or reference the Get Functions output below in the curl section.  Debugging For debugging your aws lambda setup, check the clouddriver logs: e.g. kubectl logs -f -n spinnaker spin-clouddriver-xxxxx For debugging your custom webhook, check the logs for both orca and clouddriver. Debugging Tools You can deploy a debugging pod into the same cluster and namespace where Spinnaker lives. This pod contains useful tools that are missing in the Spinnaker micro-services to help you debug your deployment. Check out the debugging-tools repo. curl With the debugging pod deployed, you can exec into the pod and run the following calls to test if Lambda is properly configured in Spinnaker. The key point to note here is that the base URL is pointing to clouddriver (e.g. http://spin-clouddriver:7002)

curl -X GET --header 'Accept: application/json' 'http://spin-clouddriver:7002/functions'; | jq .

This command requests for the lambda functions in the AWS account and pipes the output to jquery to create a nice human readable format. You should expect an output similar to the following:

{
"account": "aws",
"codeSha256": "gxyL5FY9DLxavA+MMBAFrsnhL7SRL/CiSwciLGGWBCI=",
"codeSize": 262,
"description": "",
"eventSourceMappings": [],
"functionArn": "arn:aws:lambda:us-west-2:555692138000:function:helloArmoryfx",
"functionName": "helloArmoryfx",
"handler": "index.handler",
"lastModified": "2019-05-28T15:12:40.330+0000",
"layers": [],
"memorySize": 128,
"region": "us-west-2",
"revisionId": "4a799ab5-9cf2-491c-a07f-129247a27b4e",
"revisions": {
"4a799ab5-9cf2-491c-a07f-129247a27b4e": "$LATEST"
},
"role": "arn:aws:iam::555692138000:role/service-role/helloArmoryfx-role-y9j7d2ll",
"runtime": "nodejs10.x",
"timeout": 10,
"tracingConfig": {
"mode": "PassThrough"
},
"version": "$LATEST"
}

From here you can test other commands such as Lambda update code.

curl -X POST \
http://spin-clouddriver:7002/aws/ops/updateLambdaFunctionCode \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"region": "us-west-2",
"functionName": "helloArmoryfx",
"credentials": "aws",
"s3bucket": "armory-sales-away",
"s3key": "lambdacode/lambdacode-v0.1.zip",
"publish": "true"
}'