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á!
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:
- AWS SAM CLI;
- AWS CLI;
- Serverless;
- Localstack;
- Using Serverless Framework & Localstack to test your AWS applications locally.
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!