Simulando Serviço Amazon Kinesis Data Firehose com o LocalStack
Tags: aws, localstack, firehose, docker
Published: 2022-08-02 19:25:00

Localstack permite emular serviços de cloud (AWS) offline. Serviços como Lambda, S3, Dynamodb, Firehose, SQS, SNS, entre outros podem ser configurados localmente. Auxiliando no desenvolvimento e teste da sua aplicação.

Recentemente, tive que implementar o envio de certos dados para o serviço [Amazon Kinesis Data Firehose](https://aws.amazon.com/pt/kinesis/data-firehose/) e utilizei o localstack para emular esse serviço localmente.

pré-requisitos:

- [Docker](https://www.docker.com/)
- [Docker Compose](https://docs.docker.com/compose/install/)
- [AWS CLI](https://aws.amazon.com/pt/cli/)
- [Definir e visualizar definições de configuração](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)

Vamos iniciar criando um contêiner com a partir da imagem do [localstack/localstack](https://hub.docker.com/r/localstack/localstack)
```bash
docker run --rm -it -p 4566:4566 -p 4510-4559:4510-4559 \
	--name localstack-dev --network default \
	-e "SERVICES=s3,firehose,iam" \
	-e "DOCKER_HOST=unix:///var/run/docker.sock" \
	-e "DEBUG=1" \
	-e "DATA_DIR=/tmp/localstack/data" \
	-e "DEFAULT_REGION=us-east-2" \
	-v "${TMPDIR:-/tmp}/localstack:/tmp/localstack" \
	-v "/var/run/docker.sock:/var/run/docker.sock" localstack/localstack
```
- docker run: criar e inicia um novo contêiner;
- --rm: contêiner vai existir enquanto estiver em execução;
- -p 4566:4566: Publica a porta 4566 de um contêiner no host;
- --name: Atribui o nome localstack-dev ao contêiner;
- --network: Conecta um contêiner a uma rede chamada default;
- -e: Definir variáveis de ambiente;
- -v: Vincula um volume.

Ao mesmo tempo que 

Ao mesmo tempo que o contêiner estiver em execução vamos poder usar a aws cli para executar comandos para interagir com alguns serviços. Porém, antes vamos a url http://localhost:4566/health e verificar se o localstack está realmente disponível.

```json
# saída esperada
{"features": {"initScripts": "initialized"}, "services": {"acm": "available", "apigateway": "available", "cloudformation": "available", "cloudwatch": "available", "config": "available", "dynamodb": "available", "dynamodbstreams": "available", "ec2": "available", "es": "available", "events": "available", "firehose": "available", "iam": "available", "kinesis": "available", "kms": "available", "lambda": "available", "logs": "available", "opensearch": "available", "redshift": "available", "resource-groups": "available", "resourcegroupstaggingapi": "available", "route53": "available", "route53resolver": "available", "s3": "available", "s3control": "available", "secretsmanager": "available", "ses": "available", "sns": "available", "sqs": "available", "ssm": "available", "stepfunctions": "available", "sts": "available", "support": "available", "swf": "available", "transcribe": "available"}, "version": "1.2.1.dev"}
```
Então agora podemos começar a executar alguns comamndos.

Vamos iniciar criando um bucket:
```bash
aws --endpoint-url=http://localhost:4566 s3 mb s3://sample-bucket --region us-east-2
```

Agora podemos também listar os buckets:
```bash
aws --endpoint-url=http://localhost:4566 s3 ls
```

Vamos então torná-lo público:
```bash
aws --endpoint-url=http://localhost:4566 s3api put-bucket-acl --bucket sample-bucket --acl public-read
```

Em seguida vamos criar um arquivo chamado iam_policy.json com o conteúdo abaixo:
```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "*",
      "Resource": "*"
    }
  ]
}
```

Vamos usar o arquivo criado para criar um política chamada super-role:
```bash
aws --endpoint-url=http://localhost:4566 iam create-role --role-name super-role --assume-role-policy-document file://$PWD/iam_policy.json
```
 
```json
#saída esperada
{
    "Role": {
        "Path": "/",
        "RoleName": "super-role",
        "RoleId": "dckali3ia8xf0cfjgw09",
        "Arn": "arn:aws:iam::000000000000:role/super-role",
        "CreateDate": "2022-07-21T13:48:35.612000+00:00",
        "AssumeRolePolicyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Effect": "Allow",
                    "Action": "*",
                    "Resource": "*"
                }
            ]
        },
        "MaxSessionDuration": 3600
    }
}
```

Após a criação do bucket e das regras criamos uma arquivo firehose_conf.json com as configurações necessárias para criação do serviço firehose:
```json
{
  "DeliveryStreamName": "sample-firehose",
  "DeliveryStreamType": "DirectPut",
  "S3DestinationConfiguration": {
    "RoleARN": "arn:aws:iam::000000000000:role/super-role",
    "BucketARN": "arn:aws:s3:::sample-bucket",
    "Prefix": "sample-firehose-log",
    "ErrorOutputPrefix": "sample-firehose-error-log",
    "BufferingHints": {
      "SizeInMBs": 10,
      "IntervalInSeconds": 300
    },
    "CompressionFormat": "UNCOMPRESSED",
    "CloudWatchLoggingOptions": {
      "Enabled": false,
      "LogGroupName": "",
      "LogStreamName": ""
    }
  },
  "Tags": [
    {
      "Key": "tagKey",
      "Value": "tagValue"
    }
  ]
}
```

Note que o arn do bucket e da role que criamos foram usados no arquivo.

Agora podemos usar o comando para criar o serviço do firehose:
```bash
aws --endpoint-url=http://localhost:4566 firehose create-delivery-stream --cli-input-json file://$PWD/firehose_conf.json
```

```json
# saída esperada
{
    "DeliveryStreamARN": "arn:aws:firehose:us-east-2:000000000000:deliverystream/sample-firehose"
}
```

Enviamos então algum conteúdo para o serviço com o comando abaixo:
```bash
aws --endpoint-url=http://localhost:4566 firehose put-record --delivery-stream-name sample-firehose --record '{"Data":"SGVsbG8gd29ybGQ="}'
```