Blog

AWS Lambda – How to create a custom bootstrap in Go

02 Dec, 2018
Xebia Background Header Wave

At re:Invent 2018, AWS announced Lambda Custom Runtimes where you can implement an AWS Lambda runtime in any programming language. In this blog we are going to create our own bootstrap in Go.

Bootstrap

Bootstrap is a file in the deployment archive, that is responsible for reading the ‘AWS_LAMBDA_RUNTIME_API’ environment variable, and interacting with the Lambda runtime API by getting the next event and sending a response. The Lambda runtime API is available as a webservice which makes it very easy to create a bootstrap application in Go. Below we see an example of a bootstrap application that responds with an API Gateway Proxy response.
The bootstrap first gets the value from the ‘AWS_LAMBDA_RUNTIME_API’ environment variable which contains the endpoint to use for this function. Using the endpoint, it gets the next available event by calling the Lambda runtime API.
The response contains the event as a body value and the the ‘Lambda-Runtime-Aws-Request-Id’ as a header value. The value of ‘Lambda-Runtime-Aws-Request-Id’ must be used to post a response back to the Lambda runtime API.

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"
)

func main() {
    awsLambdaRuntimeApi := os.Getenv("AWS_LAMBDA_RUNTIME_API")
    if awsLambdaRuntimeApi == "" {
            panic("Missing: 'AWS_LAMBDA_RUNTIME_API'")
    }
    for {
        // get the next event
       requestUrl := fmt.Sprintf("http://%s/2018-06-01/runtime/invocation/next", awsLambdaRuntimeApi)
        resp, err := http.Get(requestUrl)
        if err != nil {
            log.Fatal(fmt.Errorf("Expected status code 200, got %d", resp.StatusCode))
        }

        requestId := resp.Header.Get("Lambda-Runtime-Aws-Request-Id")
        // print the next event
       eventData, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Fatal(fmt.Errorf("Error: %s"), err)
        }
        fmt.Println("Received event:", string(eventData))

        // Assume API Gateway and respond with Hello World
       responseUrl := fmt.Sprintf("http://%s/2018-06-01/runtime/invocation/%s/response", awsLambdaRuntimeApi, requestId)
        responsePayload := []byte(`{"statusCode": 200, "body": "Hello World!"}`)
        req, err := http.NewRequest("POST", responseUrl, bytes.NewBuffer(responsePayload))
        req.Header.Set("Content-Type", "application/json")

        client := &http.Client{}
        client.Timeout = time.Second * 1
        postResp, err := client.Do(req)
        if err != nil {
            log.Fatal(fmt.Errorf("Error %s", err))
        }
        body, _ := ioutil.ReadAll(postResp.Body)
        fmt.Println("Received response:", string(body))
    }
}

Example

The example shows how to create, build, package and deploy a native Go binary as bootstrap to AWS as an API Gateway handler. To deploy the example type ‘make dep[oy’ and to delete type ‘make delete’.

Conclusion

Lambda can execute any Linux static binary which is great! Lambda still supports the managed lambda runtimes, but to get extra performance out of our lambda handlers we can create highly optimized binaries, have the fastest processing and lowest cost.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts