O que torna um manipulador AWS Lambda resiliente, rastreável e fácil de manter? Como você escreve tal código?
Nesta série de blogs, tentarei responder a essas perguntas compartilhando meu conhecimento e as melhores práticas do AWS Lambda, para que você não cometa os mesmos erros que eu cometi.
Esta série de blogs apresenta progressivamente as melhores práticas e utilitários, adicionando um utilitário por vez.
Parte 1 focada em Registro.
A Parte 2 focou na Observabilidade: monitoramento e rastreamento.
A Parte 3 focou na Observabilidade do Domínio de Negócios .
A Parte 4 focou em Variáveis de Ambiente .
A Parte 6 focou em Configuração e Sinalizadores de Recursos.
A Parte 7 focou em como iniciar seu próprio serviço Serverless em dois cliques.
Parte 8 focado nas melhores práticas do AWS CDK .
Este blog se concentra na validação de entrada e nas melhores práticas de análise.
Fornecerei um projeto Python de modelo de manipulador AWS Lambda funcional e de código aberto.
Este manipulador incorpora as melhores práticas do Serverless e tem todos os recursos necessários para um manipulador adequado e pronto para produção.
Durante esta série de blogs, abordarei registro, observabilidade, validação de entrada, sinalizadores de recursos, configuração dinâmica e como usar variáveis de ambiente com segurança.
Embora os exemplos de código sejam escritos em Python, os princípios são válidos para todas as linguagens de programação suportadas pelas funções do AWS Lambda.
Você pode encontrar todos os exemplos neste repositório do GitHub , incluindo o código de implantação do CDK.
O caso da validação de entrada
Os desenvolvedores tendem a se concentrar na implementação da lógica de negócios do manipulador do AWS Lambda e prestam menos atenção à validade do parâmetro de entrada 'event'.
O algoritmo deles é simples: extrai a carga útil da lógica de negócios da entrada e a processa. Fácil.
No entanto, esse comportamento excessivamente otimista pode levar a travamentos, comportamentos indefinidos, bugs e até mesmo problemas de segurança.
Neste blog, você aprenderá a importância da validação de entrada na nuvem, as armadilhas que ela evita e como superar os desafios e a complexidade inerentes que você encontra ao desenvolver funções do AWS Lambda.
Você aprenderá a processar suas entradas de eventos de maneira segura e resiliente para que possa se concentrar no que mais importa: sua lógica de negócios.
A abordagem otimista
Vamos examinar o seguinte código otimista do manipulador AWS Lambda:
O manipulador do AWS Lambda ' my_handler ' recebe o parâmetro ' event ', um dicionário Python.
A linha 6, que pode parecer inocente, é, na verdade, bastante perigosa e demonstra várias suposições ocultas.
Ele pressupõe que:
O argumento do dicionário ' event ' tem uma chave ' R ecords ' com um valor de lista.
A lista tem pelo menos dois itens.
Cada item da lista é um dicionário e o segundo item da lista contém uma chave ' name '.
' my_name ' é uma string não vazia que representa um nome válido.
Todos os valores são seguros e higienizados e não expõem o código a ameaças de segurança (XSS, injeções de entrada, etc., conforme discutido aqui ).
Validação Sintática
As três primeiras suposições estão relacionadas à falha na validação sintática : não validar a sintaxe de campos estruturados (JSON, Lista, etc.).
Essas suposições podem causar o surgimento de exceções ' KeyError ', ' TypeError ' ou ' IndexError '.
O que acontece se uma exceção for gerada e passar despercebida?
Bem, o manipulador Lambda irá travar desajeitadamente. Além disso, se um API Gateway acionar a função AWS Lambda, um código de erro HTTP 5XX retorna ao chamador, e a experiência do usuário é prejudicada, pois você perde o controle sobre a mensagem de erro.
E se você manipular um lote de registros SQS e, após processar dois registros com sucesso, o terceiro registro causar uma exceção não manipulada? Bem, isso é uma pena, já que todo o seu lote (registros processados e não processados) será retornado para a fila, pronto para ser processado novamente (e falhar novamente), custando dinheiro para as invocações extras.
Validação Semântica
A quarta e quinta suposições estão relacionadas à validação de restrições de valor, também conhecida como validação semântica.
'my_name' é um nome válido? É um valor não vazio? Ele corresponde a uma regex esperada? Quem sabe, não é verificado no exemplo, mas presume-se que esteja ok.
Essa suposição oculta pode levar a comportamentos indefinidos e a bugs ou exceções difíceis de depurar.
"Seu código irá falhar algumas vezes, e isso é normal, desde que ele falhe da maneira "certa".
O enigma do esquema de eventos da AWS
Quando um serviço da AWS envia um evento que aciona sua função do AWS Lambda, informações de metadados são adicionadas ao evento e a carga útil da lógica de negócios é encapsulada.
Vamos chamar essas informações de metadados de "envelope". O envelope contém informações valiosas, cabeçalhos interessantes e os dados mais importantes, a carga útil da lógica de negócios que você deseja processar.
É aí que fica complicado.
Cada serviço AWS tem sua própria estrutura de envelope e pode encapsular o payload da lógica de negócios de forma diferente. Alguns serviços salvam como uma string codificada, outros como um dicionário.
É tudo muito diferente e nem sempre bem documentado.
Essa camada de complexidade extra deve ser abordada antes de validar a entrada da carga útil do negócio.
Nosso principal objetivo aqui é focar na lógica de negócios e não queremos nos preocupar com diferentes esquemas de serviço da AWS. Queremos que o tipo de envelope seja o mais transparente possível.
Vamos dar uma olhada em várias estruturas de eventos de serviços da AWS.
AWS EventBridge
A carga útil da lógica de negócios é enviada como um dicionário no campo ' detalhe '.
Todos os outros campos são considerados como o envelope.
Gateway de API (REST)
No API Gateway, a carga útil da lógica de negócios é enviada como uma string codificada em JSON no campo ' body '.
SQS
Um evento SQS é uma lista de registros. O payload da lógica de negócios é enviado como uma string JSON codificada no campo ' body ' de cada registro interno.
A linha de fundo
As variações e inconsistências do esquema de serviço da AWS aumentam o esforço de validação do esquema. Precisamos entender qual esquema de serviço da AWS esperar, onde encontrar o payload e como decodificá-lo antes de podermos validá-lo.
A solução
" A validação de entrada deve ser aplicada tanto no nível sintático quanto no semântico . - OWASP
Validaremos o evento de entrada, extrairemos o payload de negócios de entrada, decodificaremos e validaremos de acordo com um esquema predefinido. Este esquema verificará se todos os parâmetros necessários existem e se seu tipo é o esperado e validará todas as restrições de valor.
O esquema cobrirá validações sintáticas e semânticas.
Tudo isso será alcançado com uma única linha de código .
Analisador AWS Lambda Powertools
Usaremos um utilitário de análise.
Tive o prazer de escrever e contribuir com o utilitário Parser para um projeto fantástico no Github chamado AWS Lambda Powertools . Usamos esse repositório anteriormente na série de blogs (partes um a três) para registro, rastreamento e métricas.
O utilitário analisador ajudará você a atingir a validação de próximo nível.
Primeiro, precisamos definir nossa carga útil de lógica de negócios como um esquema Pydantic.
Vamos supor que ' my_handler ' processe pedidos de clientes, um cliente por vez.
Ele espera um documento JSON que contenha os dois parâmetros: ' my_name ' e ' order_item_count '. Vamos definir as validações semântica e sintática:
' my_name ' - nome do cliente, uma string não vazia com até 20 caracteres.
' order_item_count ' - um número inteiro positivo que representa o número de itens pedidos que ' my_name ' colocou.
E o esquema Pydantic correspondente:
Pydantic é uma biblioteca de parser poderosa que permite definir validação personalizada na forma de funções 'validator'. Leia mais sobre isso aqui .
Tempo Mágico de Validação
Vamos adicionar o utilitário Parser a ' my_handler ' e usar o esquema ' Input '. Assumimos que o manipulador é acionado por um AWS API Gateway, o que significa que o envelope conterá campos de metadados do AWS API Gateway.
Vamos dar uma olhada no código de validação:
Na linha 3, importamos a função ' parse ' e a exceção ' ValidationError' .
Na linha 4, importamos ' ApiGatewayEnvelope', que se correlaciona com o serviço da AWS que aciona esse manipulador, o AWS API Gateway.
Na linha 7, importamos o esquema ' Input ' que definimos na etapa anterior. Todos os esquemas de manipuladores são colocados na pasta ' service/handlers/schema '.
A mágica acontece na linha 14. Dizemos ao analisador para extrair e validar nosso modelo de esquema ' Input ' (o esquema de carga útil da lógica de negócios) do dicionário ' event '.
Também informamos que o evento que ele espera tem uma estrutura de envelope que corresponde à de um AWS API Gateway. O parser retornará uma instância de classe de dados válida do tipo ' Input .'
Na linha 18, o handler pode processar com segurança o payload da lógica de negócios. Por exemplo, podemos acessar ' my_name ' escrevendo 'input.my_name'.
Em caso de erro de validação, uma exceção detalhada é gerada.
As exceções do Pydantic contêm informações detalhadas sobre o motivo da falha da validação e quais valores ou campos causaram a falha.
Neste exemplo, é melhor registrar a exceção e retornar um código de erro HTTP BAD Request (400).
E quanto a outros serviços da AWS?
O Parser oferece suporte aos serviços de integração mais comuns do AWS Lambda, incluindo SNS, SQS, Kinesis, S3, EventBridge, etc.
Leia mais sobre isso aqui .
Juntando tudo
Vamos adicionar o utilitário Parser aos outros utilitários apresentados nesta série de blogs: o logger, o tracer, as métricas e o analisador de variáveis de ambiente.
O manipulador agora ficará assim:
Na linha 34, analisamos e validamos a entrada.
Na linha 35, registramos os detalhes válidos da solicitação. Não registramos ' my_name ', pois é considerado informação pessoalmente identificável.
Na linha 40, usamos a classe de dados analisados para enviar a entrada para a função do manipulador de lógica de negócios interna.
Práticas recomendadas para lidar com exceções de validação
A melhor prática para lidar com exceções de validação é registrar a exceção e retornar um erro detalhado ao chamador.
A exceção de validação do Parser conterá informações detalhadas sobre os campos malformados no exemplo acima.
Na Linha 36, se a entrada estiver malformada, uma exceção será capturada e registrada.
Por exemplo, se a chave ' my_name ' estiver faltando no evento de entrada, o código imprimirá o seguinte log de erro da exceção do Pydantic:
"erro: 1 erro de validação para o campo Input\nmy_name\n obrigatório (type=value_error.missing)".
Na linha 38, um status HTTP Bad Request é enviado de volta ao chamador.
Usando parâmetros de metadados de envelope
Este é um caso de uso avançado.
Ao usar a função ' parse ' com o parâmetro envelope, você não pode acessar os parâmetros de envelope analisados. No entanto, em alguns casos, esses parâmetros contêm dados que você achará úteis.
Você pode usar o utilitário Parser sem especificar o argumento de envelope e analisar somente de acordo com o argumento do modelo.
Você precisará fornecer um esquema Pydantic COMPLETO contendo o esquema de entrada do seu modelo de negócios E o parâmetro de metadados.
A maneira mais fácil de fazer isso é criar uma nova classe que estenda uma classe de modelo Parser existente (modelo de gateway de API, modelo SQS, etc.) e adicionar seu esquema de carga útil de lógica de negócios.
Veja exemplos detalhados aqui .
Validação? Não apenas para entrada!
Você deve executar a validação de esquema para qualquer objeto de dicionário que seu manipulador AWS Lambda usa. Pode ser uma resposta boto3, arquivo de configuração JSON, resposta HTTP de um serviço ou qualquer objeto que você possa mapear para um esquema.
É melhor prevenir do que remediar.
Próximo
Isso conclui a quinta parte da série.
Junte-se a mim na próxima parte, onde implementarei configuração dinâmica e sinalizadores de recursos.
Comments