Get in touch
or send us a question?
CONTACT

Localstack – A fully functional local AWS cloud stack

LocalStack provides an easy-to-use test/mocking framework for developing Cloud applications.

  1. Installation
  • Read document at https://github.com/localstack/localstack.
    At this tutorial, we will use docker and docker compose
  1. Running using docker compose
  • Create a docker-compose.yml file with content
version: '2.1'
services:
  localstack:
    image: localstack/localstack-full
    network_mode: bridge
    ports:
      - "4566:4566"
      - "4571:4571"
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"
    environment:
      - SERVICES=${SERVICES- }
      - DEBUG=${DEBUG- }
      - DATA_DIR=/home/silverlight/Projects/localstack
      - PORT_WEB_UI=${PORT_WEB_UI- }
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }
      - DOCKER_HOST=unix:///var/run/docker.sock
      - HOST_TMP_FOLDER=${TMPDIR}
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/home/silverlight/Projects/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
  • Service health check http://localhost:4566/health
  • Accessing S3 via CLI
aws --endpoint-url=http://localhost:4566 s3api create-bucket --bucket localstack
aws --endpoint-url=http://localhost:4566 s3api list-buckets




{
    "Buckets": [
      {
          "Name": "localstack",
          "CreationDate": "2021-03-31T08:52:32.000Z"
      }

    ],
    "Owner": {
        "DisplayName": "webfile",
        "ID": "bcaf1ffd86f41161ca5fb16fd081034f"
    }
}
  • Install awscli-local pip install awscli-local.
    Now you can use awslocal instead of aws --endpoint-url=http://localhost:4566
  1. Create Dynamodb Table using Cloudformation
  • Create a create_table.json file
{
      "AWSTemplateFormatVersion" : "2010-09-09",
      "Resources" : {
        "di2DynamoDBTable" : {
          "Type" : "AWS::DynamoDB::Table",
          "Properties" : {
            "AttributeDefinitions" : [
              {"AttributeName" : "Code", "AttributeType" : "S"}
            ],
            "KeySchema" : [
              {"AttributeName" : "Code", "KeyType" : "HASH"}
            ],
            "ProvisionedThroughput" : {
              "ReadCapacityUnits" : 5,
              "WriteCapacityUnits" : 5
            },
            "TableName" : "LocalStackTable",
            "GlobalSecondaryIndexes": [
              {
                "IndexName": "Code-index",
                "KeySchema": [
                  {"AttributeName": "Code", "KeyType": "HASH"},
                ],
                "ProvisionedThroughput": {
                  "ReadCapacityUnits": 5,
                  "WriteCapacityUnits": 5
                },
                "Projection": {"ProjectionType": "ALL"}
              }
            ]
          }
        }
      }
}
  • Create via CLI
awslocal cloudformation create-stack --stack-name LocalCreateTable --template-body file:///home/silverlight/Projects/localstack/create_table.json
  • Check in dashboard http://localhost:8080
  1. Build Lambda using Cloudformation
  • Make a build folder mkdir build
  • Install libraries pip install boto3 -t build
  • Create role awslocal iam create-role --role-name LocalStackRole --assume-role-policy-document file:///home/silverlight/Projects/localstack/role.json
  • Create a lambda_function.py
import os
import boto3


def lambda_handler(event, context):
    endpoint_url = os.environ.get('DYNAMODB_BACKEND', None)
    region = os.environ.get('REGION', 'us-east-1')
    dynamodb = boto3.client('dynamodb', region_name=region, endpoint_url=endpoint_url)
    table = dynamodb.Table('LocalStackTable')
    
    records = [
      {'Code': 'Test', 'Name': 'Test name'}
      {'Code': 'Test2', 'Name': 'Test name 2'}
    ]
    
    for record in records:
      table.put_item(Item=record)
   
    return {
      "statusCode": 200,
      "body": json.dumps('Successfully!')
    }
  • Create template.yaml




AWSTemplateFormatVersion: '2010-09-09'
Description: Localstack function.

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: lambda_function.lambda_handler
      Role: arn:aws:iam::000000000000:role/LocalStackRole
      Runtime: python3.8
  • Build and deploy lambda to AWS (Localstack)
  cd build
  awslocal cloudformation package --template-file template.yaml --output-template-file serverless-output.yaml --s3-bucket localstack
  awslocal cloudformation deploy --template-file serverless-output.yaml --stack-name LocalStackDeploy --capabilities CAPABILITY_IAM --s3-bucket localstack
  • Invoke lambda
awslocal lambda invoke --function-name test_lambda --payload '{}' response.json
  1. Build API using Cloudformation
  • Add function get_records to lambda_function.py
def get_records(event, context):
    endpoint_url = os.environ.get('DYNAMODB_BACKEND', None)
    region = os.environ.get('REGION', 'us-east-1')
    dynamodb = boto3.resource('dynamodb', region_name=region, endpoint_url=endpoint_url)
    table = dynamodb.Table('LocalStackTable')
    
    code = event['queryStringParameters'].get('Code', 'Test')
    limit = event['queryStringParameters'].get('Limit', 100)
    query_dict = dict()
    query_dict['Limit'] = limit
    query_dict['IndexName'] = 'Code-index'
    query_dict['KeyConditionExpression'] = Key('Code').eq(code)
    
    response = table.query(**query_dict)
    
    return {
      "statusCode": 200,
      "headers": {"Content-Type": "application/json"},
      "body": json.dumps(response['Items'])
    }
  • Update template.yaml
GetRecords:
Type: AWS::Serverless::Function
Properties:
FunctionName: test_get_records
Handler: lambda_function.get_records
Role: arn:aws:iam::000000000000:role/LocalStackRole
Runtime: python3.8
Events:
  Record:
    Type: Api
    Properties:
      RestApiId: !Ref Api
      Path: /api/v1/records/
      Method: ANY

Api:
Type: AWS::Serverless::Api
Properties:
DefinitionBody:
  swagger: "2.0"
  info:
    title: "ApiLocalstack"
  schemes:
  - "https"
  paths:
    "/api/v1/records":
      get:
        responses:
          200:
            description: "200 response"
            headers:
              Access-Control-Allow-Origin:
                type: "string"
        security:
          - ApiKey: []
        x-amazon-apigateway-integration:
          uri:
            Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetRecords.Arn}/invocations"
          responses:
            default:
              statusCode: "200"
          passthroughBehavior: "when_no_match"
          httpMethod: "GET"
          contentHandling: "CONVERT_TO_TEXT"
          type: "aws_proxy"
  definitions:
    Empty:
      type: "object"
      title: "Empty Schema"
StageName: test
  • Build and deploy lambda to AWS (Localstack).
    With localstack, it may not finish when update template but it’s fine.
  • Get RestApiId
awslocal apigateway get-rest-apis
{
    "items": [
        {
            "id": "ywlcyobwy6",
            "name": "LocalStackDeploy-Api-cae68be2",
            "createdDate": 1617242934,
            "apiKeySource": "HEADER",
            "endpointConfiguration": {
                "types": [
                    "EDGE"
                ]
            },
            "tags": {},
            "disableExecuteApiEndpoint": false
        }
    ]
}
  • Checkout API by Postman
http://localhost:4566/restapis/ywlcyobwy6/test/_user_request_/api/v1/records/