Skip to content

Instantly share code, notes, and snippets.

@IamFaizanKhalid
Last active June 24, 2023 07:11
Show Gist options
  • Save IamFaizanKhalid/cb88837846339a897c1854d5b8c02bd8 to your computer and use it in GitHub Desktop.
Save IamFaizanKhalid/cb88837846339a897c1854d5b8c02bd8 to your computer and use it in GitHub Desktop.
Golang with Lambda + Redis setup with Localstack using Docker

This demonstrates how to configure your Golang Lambda function (that uses Redis) on localstack using docker.

Prerequisites:

Usage

  • Run ./setup.sh to set up.
  • Run ./update.sh to update lambda with your latest code.

Remember

For connection to the Redis docker container from the localstack docker container, use container name instead of localhost.

Based on

Code available at

version: "3.3"
services:
# not using elasticache from localstack as it is a pro feature
my-cache:
container_name: my-cache
restart: unless-stopped
image: redis:alpine
# using a bridge network to make it accessible to localstack container
networks:
- my-local-network
ports:
- "6379:6379"
my-localstack:
container_name: my-localstack
image: localstack/localstack
networks:
- my-local-network
ports:
- "4566-4620:4566-4620"
- "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
environment:
- SERVICES=lambda
- DEFAULT_REGION=us-west-2
- LAMBDA_EXECUTOR=docker
- LAMBDA_REMOTE_DOCKER=true
- LAMBDA_REMOVE_CONTAINERS=true
- DATA_DIR=${DATA_DIR- }
- DEBUG=1
- DOCKER_HOST=unix:///var/run/docker.sock
- DEBUG=${DEBUG- }
- PORT_WEB_UI=${PORT_WEB_UI- }
- KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
volumes:
- "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
- /var/run/docker.sock:/var/run/docker.sock
networks:
my-local-network:
driver: bridge
export AWS_REGION=us-west-2
export AWS_ACCESS_KEY_ID=
export AWS_SECRET_ACCESS_KEY=
API_NAME=my-api
LAMBDA_NAME=my-lambda
STAGE=test
alias awslocal="aws --endpoint-url=http://localhost:4566"
# This will read .env file in following format:
# VAR1='VAL1',VAR2='VAL2',VAR3='VAL3'
ENVS=$(awk 'NF' .env | sed -e "s/\(.*\)=\(.*\)/\1='\2'/g" | paste -sd "," -)
# Current time in RFC3339 format
TIMESTAMP=$(date -Iseconds)
function fail() {
echo $2
exit $1
}
#!/bin/sh
source ./env.sh
awslocal logs tail /aws/lambda/${LAMBDA_NAME} \
--follow \
--since ${TIMESTAMP}
package main
import (
"encoding/json"
"log"
"net/http"
"os"
"time"
"github.com/apex/gateway"
"github.com/gomodule/redigo/redis"
)
var cache redis.Conn
func init() {
//connectionUrl := os.Getenv("REDIS_CONN_URL")
connectionUrl := "redis://:@my-cache:6379/0"
var err error
cache, err = redis.DialURL(connectionUrl, redis.DialUseTLS(false))
if err != nil {
log.Fatalln(err)
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", ping)
err := gateway.ListenAndServe("", mux)
if err != nil {
log.Fatalln(err)
}
}
func ping(w http.ResponseWriter, _ *http.Request) {
resp := &response{
Timestamp: time.Now().UTC(),
}
body, _ := json.Marshal(resp)
_, _ = cache.Do("SET", "lastResponse", body)
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
w.Write(body)
}
type response struct {
Timestamp time.Time `json:"timestamp"`
}
#!/bin/sh
source ./env.sh
# This will create docker container for localstack
docker-compose up -d
[ $? == 0 ] || fail 1 "Failed: Docker / compose / up"
# Building for lambda's architecture: linux/amd64
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${API_NAME} .
[ $? == 0 ] || fail 2 "Failed: Go / build / ${API_NAME}"
zip -r ${API_NAME}.zip ${API_NAME}
[ $? == 0 ] || fail 3 "Failed: zip / ${API_NAME}"
OUTPUT=$(awslocal lambda create-function \
--region ${AWS_REGION} \
--function-name ${LAMBDA_NAME} \
--runtime go1.x \
--handler ${API_NAME} \
--timeout 30 \
--memory-size 128 \
--zip-file fileb://${API_NAME}.zip \
--role "arn:aws:iam::000000000000:role/irrelevant" \
--environment "Variables={$ENVS}") # Passing variables read from .env
[ $? == 0 ] || fail 4 "Failed: AWS / lambda / create-function"
LAMBDA_ARN=$(awslocal lambda list-functions --query "Functions[?FunctionName==\`${LAMBDA_NAME}\`].FunctionArn" --output text --region ${AWS_REGION})
OUTPUT=$(awslocal apigateway create-rest-api \
--region ${AWS_REGION} \
--name ${API_NAME})
[ $? == 0 ] || fail 5 "Failed: AWS / apigateway / create-rest-api"
API_ID=$(awslocal apigateway get-rest-apis --query "items[?name==\`${API_NAME}\`].id" --output text --region ${AWS_REGION})
PARENT_RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/`].id' --output text --region ${AWS_REGION})
OUTPUT=$(awslocal apigateway create-resource \
--region ${AWS_REGION} \
--rest-api-id ${API_ID} \
--parent-id ${PARENT_RESOURCE_ID} \
--path-part "{any+}")
[ $? == 0 ] || fail 6 "Failed: AWS / apigateway / create-resource"
RESOURCE_ID=$(awslocal apigateway get-resources --rest-api-id ${API_ID} --query 'items[?path==`/{any+}`].id' --output text --region ${AWS_REGION})
OUTPUT=$(awslocal apigateway put-method \
--region ${AWS_REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method ANY \
--authorization-type "NONE")
[ $? == 0 ] || fail 7 "Failed: AWS / apigateway / put-method"
OUTPUT=$(awslocal apigateway put-integration \
--region ${AWS_REGION} \
--rest-api-id ${API_ID} \
--resource-id ${RESOURCE_ID} \
--http-method ANY \
--type AWS_PROXY \
--integration-http-method POST \
--uri arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${LAMBDA_ARN}/invocations \
--passthrough-behavior WHEN_NO_MATCH)
[ $? == 0 ] || fail 8 "Failed: AWS / apigateway / put-integration"
OUTPUT=$(awslocal apigateway create-deployment \
--region ${AWS_REGION} \
--rest-api-id ${API_ID} \
--stage-name ${STAGE})
[ $? == 0 ] || fail 9 "Failed: AWS / apigateway / create-deployment"
ENDPOINT=http://localhost:4566/restapis/${API_ID}/${STAGE}/_user_request_
echo "API available at: ${ENDPOINT}"
echo
echo "Testing: GET ${ENDPOINT}/ping"
curl -i ${ENDPOINT}/ping
[ $? == 0 ] || fail 10 "Failed: curl / GET"
#!/bin/sh
source ./env.sh
GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o ${API_NAME} .
[ $? == 0 ] || fail 1 "Failed: Go / build / ${API_NAME}"
zip -r ${API_NAME}.zip ${API_NAME}
[ $? == 0 ] || fail 2 "Failed: zip / ${API_NAME}"
OUTPUT=$(awslocal lambda update-function-code \
--function-name ${LAMBDA_NAME} \
--zip-file fileb://${API_NAME}.zip)
[ $? == 0 ] || fail 3 "Failed: AWS / lambda / update-function-code"
echo "Lambda code updated..."
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment