aws-lambda-python

Como testar um AWS Lambda na sua máquina com Python e Localstack

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?”

Então, para estes casos e outros do tipo, 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á!

10 Livros de Python para ser um especialista

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.

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 instalá-lo desta forma e pensando nisto, aqui está um outro jeito de instalar o AWS SAM CLI, é possível pegar o código fonte do AWS SAM CLI no Github também.

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

A saída final:

SAM CLI, version 0.47.0

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

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"

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

$ docker-compose up

A partir deste momento é possível acessar diferentes serviços da AWS por meio das diferentes portas no servidor local.

Vamos testar o serviço do AWS S3.

$ curl -v http://localhost:4572

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>

Este teste pode ser realizado também no browser. Apenas copie o endereço e cole na url do seu navegador.

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

AWS Lambda com Python PyCharm

Assim que o “docker-compose” estiver funcionando é hora de criar o projeto.

Neste momento vamos criar um projeto novo no PyCharm:

Criando um no projeto

É muito importante checar se o “SAM CLI” foi automáticamente reconhecido.

Criando o arquivo principal

Chegou a hora de criar 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

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
  • 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"
    }
   }
  }
 ]
}

Uma vez que os passos anteriores estivem terminados, o próximo passo será configurar o “run configuration”.

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\""
}

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

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

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>

4 – Criando um fila no SQS

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

O retorno será algo como:

{
 "QueueUrl": "http://localhost:4576/queue/lambda-tutorial"
}

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'”.

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.

Concluído!

Este pode ser somente o ponto inicial e eu espero que este texto ajude-os a testar seus serviços na AWS Lambda com Python.

Eu espero que todos vocês alcancem suas metas.

Obrigado por ter lido até o fim!

Referências:

Ah, agora um recado muito importante: a GeekHunter possui uma página de vagas com uma série oportunidades para desenvolvedores Python. Dá uma passada lá, seja para conhecer o mercado, ou até mesmo para se candidatar a alguma delas. Conte conosco!

Compartilhar
You May Also Like