DEV Community

Mairon Costa
Mairon Costa

Posted on

AWS Lambda com Localstack

for english, click here.

Hoje em dia é muito comum ver as pessoas fazendo uso de plataformas de serviços online como a AWS, Azure, GoogleCloud. Estes são as mais famosas "Cloud". E, como qualquer outro desenvolvedor, é muito importante testar seus códigos antes de colocá-lo em produção, e neste caso, algumas questões surgem, "Como é possível testar o código que está em produção?", "Como eu posso atualizar o código que já funciona sem quebrá-lo?" ou algo do tipo. Então, para estes casos, este trabalho foi pensado. A meta principal é explicar como é possível testar um AWS Lambda na sua máquina usando PyCharm com Python e Localstack.

Então... vamos lá!!!!!

Setup

  • Ubuntu 20.04 LTS;
  • IDE: PyCharm Professional;
  • Python 3.8;
  • Docker;
  • AWS SAM CLI: é necessário para IDE funcionar com AWS Services;
  • AWS CLI: você pode usar suas credenciais verdadeiras (como descrito aqui), ou pode fazer uma ficticia. O Localstack verifica se as credenciais estão presentes e não se elas são válidas;
  • Serverless framework;

Configuração

AWS CLI

Sobre o AWS CLI, é possível checar o material para instalar no site da AWS, clique aqui.

AWS SAM CLI

Para instalar o AWS SAM CLI é informado nos documentos da AWS que é para instalá-lo pelo Homebrew, mas em alguns casos é difícil instalar o Homebrew e pensando nisto, aqui está um outro jeito de instalar o AWS SAM CLI, é possíve pegar o código fonte no Github clicando aqui.

$ git clone https://github.com/awslabs/aws-sam-cli.git
$ cd aws-sam-cli
$ sudo python3.8 ./setup.py build install
$ sam --version
Enter fullscreen mode Exit fullscreen mode

A saída final:

SAM CLI, version 0.47.0
Enter fullscreen mode Exit fullscreen mode

Localstack Docker

Neste momento o "docker-compose.yaml" deve ser criado na raíz do projeto, com isto será possível rodar o localstack de um docker container.

$ touch docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

docker-compose.yml

version: '2.1'
services:    
  localstack:  
    image: localstack/localstack  
    ports:  
      - "4567-4597:4567-4597"  
      - "${PORT_WEB_UI-8080}:${PORT_WEB_UI-8080}"  
    environment:  
      - SERVICES=${SERVICES- }  
      - DEBUG=${DEBUG- }  
      - DATA_DIR=${DATA_DIR- }  
      - PORT_WEB_UI=${PORT_WEB_UI- }  
      - LAMBDA_EXECUTOR=${LAMBDA_EXECUTOR- }  
      - KINESIS_ERROR_PROBABILITY=${KINESIS_ERROR_PROBABILITY- }  
      - DOCKER_HOST=unix:///var/run/docker.sock  
    volumes:  
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"  
      - "/var/run/docker.sock:/var/run/docker.sock"
Enter fullscreen mode Exit fullscreen mode

E agora vamos rodar o "docker-compose" com o seguinte comando:

$ docker-compose up
Enter fullscreen mode Exit fullscreen mode

A partir deste momento é possível acessar diferentes serviços da AWS atráves das diferentes portas no servidor local. Por enquanto vamos testar o serviço do AWS S3.

$ curl -v http://localhost:4572
Enter fullscreen mode Exit fullscreen mode

Parte da saída:

<ListAllMyBucketsResult xmlns="http://s3.amazonaws.com/doc/2006-03-01">
    <Owner><ID>bcaf1ffd86f41161ca5fb16fd081034f</ID>                
    <DisplayName>webfile</DisplayName></Owner><Buckets></Buckets>
</ListAllMyBucketsResult>
Enter fullscreen mode Exit fullscreen mode

Este teste pode ser realizado também no browser. Apenas copie o endereço e cole na url do browser ou apenas click aqui.

Algumas portas do AWS Service com Localstack

  • S3: 4572
  • DynamoDB: 4570
  • CloudFormation: 4581
  • Elasticsearch: 4571
  • ES: 4578
  • SNS: 4575
  • SQS: 4576
  • Lambda: 4574
  • Kinesis: 4568

PyCharm com AWS Lambda

Uma vez o "docker-compose" estiver funcionando é hora de criar o projeto. Neste momento vamos criar um projeto novo no PyCharm:

  • Criando um no projeto:

image 01

No passo número 3 é muito importante checar se o "SAM CLI" foi automáticamente reconhecido.

image 02

  • Criando o arquivo principal, o lambda_function.py , na raíz do projeto.
import urllib.parse
import boto3
import json

# print('Loading function')

HOST = "http://[YOUR_IP]"
# Get the service resource
# To production it's not necessary inform the "endpoint_url" and "region_name"
s3 = boto3.client('s3',
                  endpoint_url= HOST + ":4572",
                  region_name="us-east-1")
sqs = boto3.client('sqs',
                  endpoint_url= HOST + ":4576",
                  region_name="us-east-1")

def lambda_handler(event, context):
    # print("Received event: " + json.dumps(event, indent=2))

    # Get the object from the event and show its content type
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    url_queue = HOST + ":4576/queue/lambda-tutorial"

    try:

        response = s3.get_object(Bucket=bucket, Key=key)

        deb = {
            "request_id": response['ResponseMetadata']['RequestId'],
            "queue_url": url_queue,
            "key": key,
            "bucket": bucket,
            "message": "aws lambda with localstack..."
        }

        print("#########################################################")
        print("Send Message")
        #Send message to SQS queue
        response = sqs.send_message(
                QueueUrl=deb["queue_url"],
                MessageBody=json.dumps(deb)
        )

        print("response: {}".format(response))

        print("#########################################################")
        print("Receive 10 Messages From SQS Queue")
        response = sqs.receive_message(
            QueueUrl=deb["queue_url"],
            MaxNumberOfMessages=10,
            VisibilityTimeout=0,
            WaitTimeSeconds=0
        )

        print("#########################################################")
        print("Read All Messages From Response")
        messages = response['Messages']
        for message in messages:
            print("Message: {}".format(message))

        print("Final Output: {}".format(json.dumps(response)))
        return json.dumps(response)

    except Exception as e:
        print(e)
        raise e
Enter fullscreen mode Exit fullscreen mode

Se o "boto3" não estiver instalado a IDE irá notificar, assim sendo, abra o terminal e execute o seguinte comando:

$ pip3.8 install boto3

  • Criando os arquivos que darão suporte ao projeto.

Neste momento algumas pastas e arquivos serão criados, as pastas conterão os arquivos utilizados no teste. Vamos criar as pastas "test/files" na raíz do projeto e então criar os arquivo para rodar o projeto.

aws-lambda-localstack
|test/
|- file/
|- - test_file.log
|- - input-event-test.json
|requirements.txt
Enter fullscreen mode Exit fullscreen mode

test_file.log: Este arquivo será usado como exemplo vindo do bucket. Pode ser um arquivo vazio.

requirements.txt: Este arquivo é exigido pelo "SAM CLI".

input-event-test.json: Este arquivo será executado para rodar o projeto. Ele contém as informações do bucket.

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "1970-01-01T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "userIdentity": {
        "principalId": "EXAMPLE"
      },
      "requestParameters": {
        "sourceIPAddress": "127.0.0.1"
      },
      "responseElements": {
        "x-amz-request-id": "EXAMPLE123456789",
        "x-amz-id-2": "EXAMPLE123/5678abcdefghijklambdaisawesome/mnopqrstuvwxyzABCDEFGH"
      },
      "s3": {
        "s3SchemaVersion": "1.0",
        "configurationId": "testConfigRule",
        "bucket": {
          "name": "tutorial",
          "ownerIdentity": {
            "principalId": "EXAMPLE"
          },
          "arn": "arn:aws:s3:::example-bucket"
        },
        "object": {
          "key": "lambda/test_file.log",
          "size": 1024,
          "eTag": "0123456789abcdef0123456789abcdef",
          "sequencer": "0A1B2C3D4E5F678901"
        }
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode
  • Uma vez que os passos anteriores estivem terminados, o próximo passo será configurar o "run configuration".

image 03-pycharm-run-configuration

image-03-pycharm-run-configuration-aws-connection

Agora a configuração de IDE está feita, hora de configurar as entradas. Para configurar as entradas é obrigatório rodar alguns comando para criar os arquivos no S3 e criar a fila no SQS.

1 - Criando o bucket no S3: o bucket será nomeado como "tutorial".

$ aws --endpoint-url=http://localhost:4572 s3 mb s3://tutorial

2 - Criando a pasta no S3: será criada uma pasta chamada "lambda".

$ aws --endpoint-url=http://localhost:4572 s3api put-object --bucket tutorial --key lambda

O Response será alguma como:

{
    "ETag": "\"d41d8cd98f00b204e9800998ecf8427e\""
}
Enter fullscreen mode Exit fullscreen mode

3 - Copiando arquivos para o bucket: copiando os arquivos de "test/files" para "s3://tutorial/lambda/".

$ aws --endpoint-url=http://localhost:4572 s3 cp ./test/files/ s3://tutorial/lambda/ --recursive
Enter fullscreen mode Exit fullscreen mode

Deve-se checar se o bucket foi criado. Copie o valor de "endpoint-url" e execute um "curl" ou cole no browser, por exemplo:

$ curl -v http://localhost:4572/tutorial
Enter fullscreen mode Exit fullscreen mode

A saída será algo como:

<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
   <Name>tutorial</Name>
   <MaxKeys>1000</MaxKeys>
   <Delimiter>None</Delimiter>
   <IsTruncated>false</IsTruncated>
   <Contents>
      <Key>lambda</Key>
      <LastModified>2020-04-28T01:36:04.128Z</LastModified>
      <ETag>"d41d8cd98f00b204e9800998ecf8427e"</ETag>
      <Size>0</Size>
      <StorageClass>STANDARD</StorageClass>
      <Owner>
         <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
         <DisplayName>webfile</DisplayName>
      </Owner>
   </Contents>
   <Contents>
      <Key>lambda/input-event-test.json</Key>
      <LastModified>2020-04-28T01:40:27.882Z</LastModified>
      <ETag>"4e114da7aa17878f62bf4485a90a97a2"</ETag>
      <Size>1011</Size>
      <StorageClass>STANDARD</StorageClass>
      <Owner>
         <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
         <DisplayName>webfile</DisplayName>
      </Owner>
   </Contents>
   <Contents>
      <Key>lambda/test_file.log</Key>
      <LastModified>2020-04-28T01:40:27.883Z</LastModified>
      <ETag>"4ac646c9537443757aff7ebd0df4f448"</ETag>
      <Size>29</Size>
      <StorageClass>STANDARD</StorageClass>
      <Owner>
         <ID>75aa57f09aa0c8caeab4f8c24e99d10f8e7faeebf76c078efc7c6caea54ba06a</ID>
         <DisplayName>webfile</DisplayName>
      </Owner>
   </Contents>
</ListBucketResult>
Enter fullscreen mode Exit fullscreen mode

4 - Criando um fila no SQS.

$ aws --endpoint-url=http://localhost:4576 sqs create-queue --queue-name lambda-tutorial
Enter fullscreen mode Exit fullscreen mode

O retorno será algo como:

{
    "QueueUrl": "http://localhost:4576/queue/lambda-tutorial"
}
Enter fullscreen mode Exit fullscreen mode

Uma vez que tudo estiver feito e todas as saídas estão okays, é hora de rodar o projeto. Note que, no código foi inscrito a entrada e a saída pra este texto. Assim sendo, para rodar o projeto clique no botão "Run '[Local] lambda_function.lambda_handler'".

image-04-pycharm-run-project

Depois disto, é possível ver a saída do processo no console. É exibido as entradas e ao final dez mensagens serão recuperadas da fila e exibidas no console.

BOA!

Concluído!

Este pode ser somente o ponto inicial e eu espero que este texto ajude-os a testar seus serviços na AWS... Eu espero que todos vocês alcancem suas metas.

Repositório: aws-lambda-localstack.

Obrigado por ter lido até o fim!

Referências

Algumas referências que foram utilizadas para concluir este texto.

Algumas Cloud Computing

Top comments (0)