LocalStack入門

ローカルスタックはコンテナで動作するクラウドサービスエミュレータで、awscliやboto3からAWSのように操作することができます。
バックエンドでboto3を利用するときに、いちいちモックの実装を組まなくていいのでテストが楽になります。


- [Dashboad| LocalStack](https://app.localstack.cloud/dashboard)
- [Docs | LocalStack](https://docs.localstack.cloud/overview/)

# ■ 起動

https://docs.localstack.cloud/getting-started/installation/#docker

## Dockerで起動する方法
```bash
docker run \
  --rm -it \
  -p 4566:4566 \
  -p 4510-4559:4510-4559 \
  localstack/localstack
```

## Docker Composeで起動する方法

https://docs.localstack.cloud/getting-started/installation/#docker-compose

```yaml:docker-compose.yml
version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack-main}"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"            # LocalStack Gateway
      - "127.0.0.1:4510-4559:4510-4559"  # external services port range
    environment:
      - DEBUG=1  # デバッグレベルのログを出力する
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./localstack-volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
```

```bash
 docker-compose up -d
```

# ■ 操作
## awscli

`--endpoint-url` オプションに  `http://localhost:4566` を設定するとローカルスタックにリクエストするようになります。

```bash
LOCALSTACK_ENDPOINT_URL=http://localhost:4566

# トピック作成
aws \
  --endpoint-url=$LOCALSTACK_ENDPOINT_URL \
  --region ap-northeast-1 \
  sns create-topic \
  --name sample-topic-01

# パブリッシュ
aws \
  --endpoint-url=$LOCALSTACK_ENDPOINT_URL \
  --region ap-northeast-1 \
  sns publish \
  --message "hello" \
  --topic-arn "arn:aws:sns:ap-northeast-1:000000000000:sample-topic-01"
```

## boto3

boto3でローカルスタックを操作する場合も同様に、 `endpoint_url` に `http://localhost:4566` を設定します。

```python
>>> import boto3
>>> client = boto3.client("sns", region_name="ap-northeast-1", endpoint_url="http://localhost:4566")
# トピック作成
>>> client.create_topic(Name="sample-topic-02")
{'TopicArn': 'arn:aws:sns:ap-northeast-1:000000000000:sample', 'ResponseMetadata': {'RequestId': '846c86f6-7998-4b51-ae56-a69156151579', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'text/xml', 'content-length': '333', 'connection': 'close', 'date': 'Wed, 29 Nov 2023 08:50:29 GMT', 'server': 'hypercorn-h11'}, 'RetryAttempts': 0}}
# パブリッシュ
>>> client.publish(TopicArn="arn:aws:sns:ap-northeast-1:000000000000:sample-topic-02", Message="hello")
{'MessageId': 'bec5715a-45f4-4651-b7ff-711960388b90', 'ResponseMetadata': {'RequestId': '82d4e133-fef3-4ecd-a05c-9325892b755c', 'HTTPStatusCode': 200, 'HTTPHeaders': {'content-type': 'text/xml', 'content-length': '309', 'connection': 'close', 'date': 'Wed, 29 Nov 2023 08:51:15 GMT', 'server': 'hypercorn-h11'}, 'RetryAttempts': 0}}
```

## awscli-local

awslocalはローカルスタックで利用するためのawscliの薄いラッパーです。

- [awscli-local | GitHub](https://github.com/localstack/awscli-local)

```bash
sudo pip install awscli-local
```

```bash
# トピック作成
awslocal \
  --region ap-northeast-1 \
  sns create-topic \
  --name sample-topic-03

# パブリッシュ
awslocal \
  --region ap-northeast-1 \
  sns publish \
  --message "hello" \
  --topic-arn "arn:aws:sns:ap-northeast-1:000000000000:sample-topic-03"
```

# ■ 初期化
[Initialization Hooks | LocalStack](https://docs.localstack.cloud/references/init-hooks/)

コンテナ起動時に自動的に指定のリソースを作成するための方法です。  
`/etc/localstack/init/ready.d/` 配下に実行可能なpythonスクリプトもしくはshellスクリプトを配置すると、コンテナ起動時に実行されます。

`/etc/localstack/init/ready.d/` 配下に配置する 初期化スクリプトを作成します。
```bash
touch init-aws.sh
chmod 755 init-aws.sh

vim init-aws.sh
```

```init-aws.sh
#!/bin/bash

awslocal sns create-topic \
  --region ap-northeast-1 \
  --name sample-topic
```

docker-compose.ymlを作成します。

```bash
vim docker-compose.yml
```

```yaml:docker-compose.yml
version: "3.8"

services:
  localstack:
    container_name: "${LOCALSTACK_DOCKER_NAME-localstack-main}"
    image: localstack/localstack
    ports:
      - "127.0.0.1:4566:4566"            # LocalStack Gateway
      - "127.0.0.1:4510-4559:4510-4559"  # external services port range
    environment:
      - DEBUG=1  # デバッグレベルのログを出力する
      - DOCKER_HOST=unix:///var/run/docker.sock
    volumes:
      - "${LOCALSTACK_VOLUME_DIR:-./localstack-volume}:/var/lib/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
      - ./init-aws.sh:/etc/localstack/init/ready.d/init-aws.sh  # 初期化スクリプトをマウント
```

ローカルスタックを起動して、初期化スクリプト通りにsnsトピックが作成されているかを確認します。

```bash
# localstackを起動
docker-compose up -d

# トピックにメッセージを送信
LOCALSTACK_ENDPOINT_URL=http://localhost:4566
awslocal \
  --region ap-northeast-1 \
  sns publish \
  --message "hello" \
  --topic-arn "arn:aws:sns:ap-northeast-1:000000000000:sample-topic"

# ログの閲覧
docker-compose logs
```


初期化スクリプトの実行ステータスは下記のAPIでチェックできます。

```bash
$ curl -s localhost:4566/_localstack/init | jq .

{
  "completed": {
    "BOOT": false,
    "START": true,
    "READY": true,
    "SHUTDOWN": false
  },
  "scripts": [
    {
      "stage": "BOOT",
      "name": "booting.sh",
      "state": "UNKNOWN"
    },
    {
      "stage": "READY",
      "name": "pre_seed.py",
      "state": "SUCCESSFUL"
    }
  ]
}
```