Using localstack to run an API
Tags: cloud, dotnet, aws
Published: 2021-01-19 10:21:45

# What is localstack?

{% github localstack/localstack %}

Let's start at the beginning - what is localstack exactly?

Localstack an open source project that provides users with api's that fulfil the api's provided by the amazon web services, what does this mean? It means that you can have your own AWS running on your own - local - machine. 

# What are the advantages of this?

There are a few reason why you might use localstack, let's quickly list them :smile:

## 💰💰 Price 💰💰

Sometimes spinning up test resources in AWS is expensive, especially if you are an individual (or maybe a corporation with a a lot of individual employees) spinning up a _for example_ dynamo db table for each developer to develop against would probably be more expensive than any service you want to create.

The base version of localstack (that includes dynamo service) is free! So in a development environment you could have people communicate with that service.

## 🏃‍♀️🏃‍♂️ Speed 🏃‍♀️🏃‍♂️

Sometimes communicating with the cloud can be expensive not for the pocket but for wait times, this way all tests and code will be talking to a local version so _should be faster_ :wink:.

I don't know about you but I work at a company that has a devops team that like to be "in charge" of all of the infrastructure - which means that if I need a new S3 bukcet, dynamo database, or (insert other services here) I have to raise a ticket to create a piece of insfrastructure for testing, which can take time. With a local version of AWS I can spin up whatever I want, so whilst I wait for actual infrastructure I can continue with my work locally.

## 🎤🎤 Testing 🎤🎤

We can create tests against the local version, which means if we've broken anything we can run the same tests as we did for the previous version and if any fail we know that something has been broken whilst developing the new feature faster.

# 📕📖📑 How do we use it? 📕📖📑

There are a few ways to "use" localstack, one of my favourite ways is to have a docker compose file (just to note I will not be talking about docker here) which look something like this:

```
version: '3.4'

services:
  my.localstack:
    image: localstack/localstack:latest
    container_name: my-localstack
    ports:
      - "4566:4566"
    environment:
      - DEFAULT_REGION=eu-west-1
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - "./setup-dev-env.sh:/docker-entrypoint-initaws.d/setup-dev-env.sh"
      - "./local/creds.json:/docker-entrypoint-initaws.d/creds.json"
```

# Working with Localstack

One of the things that I wanted to achieve with Localstack was to create a test infrastucture with a legacy application I've been working on. This particular API made use of the following AWS features:

- Dynamo DB
- S3
- SQS

Attempting to make it as simple as possible for developers to clone the code from our private repository and hit build and have a localstack instance running on their machine in a configuration that would allow them to perform every branch of code in the API.


## Running Localstack

We work with c# dotnet - our editor of choice is visual studio (my personal editor of choice is Rider), dotnet provides two event hooks to run anything that you may want to run before the build completes, this is where I added a call to powershell:

```
powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -File $(SolutionDir)setup-dev-env.ps1 $(SolutionDir)
```

*Just as a note of where this is in Visual studio* if you right click your project and click "properties" and go to build events, this particular script is entered intot he "pre-build event command line"

This script checks to see if a particular docker container has been created - if not then it will call the `docker-compose` file within the direction that is passed into the script (the solution directory):


```
param($dir)

$isLocalStackRunning = docker container ps -f Name=localstack -q

if([string]::IsNullOrEmpty($isLocalStackRunning))
{
    docker-compose --file $dir\docker-compose.yml up -d
}
```

just as a reference:

Flag| What this does|
---|---|
-f| Filters the results |
-q | "Quiet" mode (just returns the docker container id)|

full reference for this command [here](https://docs.docker.com/engine/reference/commandline/ps/#examples)


The docker file:

```
version: '3.4'

services:
  my.localstack:
    image: localstack/localstack:latest
    container_name: localstack
    ports:
      - "4566:4566"
    environment:
      - DEFAULT_REGION=eu-west-1
      - DATA_DIR=/tmp/localstack/data
    volumes:
      - "./setup-dev-env.sh:/docker-entrypoint-initaws.d/setup-dev-env.sh"
      - "./local/creds.json:/docker-entrypoint-initaws.d/creds.json"
```

## Infrastructure

You may have noticed this line in my `docker-compose` file: "./setup-dev-env.sh:/docker-entrypoint-initaws.d/setup-dev-env.sh", this is essentially copying a file into directory inside the docker container, every `.sh` file in this particular directory of localstack gets run when we run the `docker-compose up` command, this is where I create all of the infrastructure that the API need to run:

```
awslocal dynamodb create-table --table-name Table1 --attribute-definitions AttributeName=KC,AttributeType=S AttributeName=Domain,AttributeType=S --key-schema AttributeName=KC,KeyType=HASH  AttributeName=Domain,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1000,WriteCapacityUnits=1000
awslocal dynamodb create-table --table-name table2 --attribute-definitions AttributeName=KC,AttributeType=S AttributeName=Domain,AttributeType=S --key-schema AttributeName=KC,KeyType=HASH  AttributeName=Domain,KeyType=RANGE --provisioned-throughput ReadCapacityUnits=1000,WriteCapacityUnits=1000
awslocal sqs create-queue --queue-name queue1
awslocal sqs create-queue --queue-name queue2.fifo --attributes "FifoQueue=true"
awslocal sqs create-queue --queue-name queue2failover.fifo --attributes "FifoQueue=true"
awslocal kinesis create-stream --stream-name stream1 --shard-count 1
awslocal s3 mb s3://bucket --region eu-west-1
awslocal s3 cp /docker-entrypoint-initaws.d/creds.json s3://bucket/SVC/apiauth/creds.json
```

*Note* Because this files runs inside of the container keep in mind that it is a Linux exclusive container so this file will need the LF new line endings (yes this caught me out :smile:)


---

Now when I build my solution it checks to see if I have my container running if I don't it will create it with all of the things I need to exercise the API :smile: