отладка Python 3.7 AWS лямбда локально - PullRequest
0 голосов
/ 14 июня 2019

Я создал zip-файл с функцией main.py и точкой входа handler для AWS Lambda с использованием Python времени выполнения 3.7.

Файл zip был упакован в образ Amazon Linux на EC2 с использованием Python 3.7.3.

У меня возникли некоторые ошибки при работе с лямбдой AWS, поэтому я решил, есть ли способ запустить функцию локально.

Мой main.py ниже:

import datetime
import logging
import os
import re
import subprocess

import boto3
import certbot.main
import raven

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def list_files(folder_path):
  onlyfiles = [f for f in listdir(folder_path) if path.isfile(path.join(folder_path, f))]
  logger.info('## path')
  logger.info(onlyfiles)


def read_and_delete_file(path):
  with open(path, 'r') as file:
    contents = file.read()
  os.remove(path)
  return contents


def provision_cert(email, domains):
  certbot.main.main([
    'certonly',                             # Obtain a cert but don't install it
    '-n',                                   # Run in non-interactive mode
    '--agree-tos',                          # Agree to the terms of service,
    '--email', email,                       # Email
    '--dns-route53',                        # Use dns challenge with route53
    '-d', domains,                          # Domains to provision certs for
    # Override directory paths so script doesn't have to be run as root
    '--config-dir', '/tmp/config-dir/',
    '--work-dir', '/tmp/work-dir/',
    '--logs-dir', '/tmp/logs-dir/',
  ])

  first_domain = domains.split(',')[0]
  first_domain_cert_folder = re.sub('\*\.', '', first_domain)
  path = '/tmp/config-dir/live/' + first_domain_cert_folder + '/'
  logger.info('## path')
  logger.info(path)

  list_files(path)

  return {
    'certificate': read_and_delete_file(path + 'cert.pem'),
    'private_key': read_and_delete_file(path + 'privkey.pem'),
    'certificate_chain': read_and_delete_file(path + 'fullchain.pem')
  }

def should_provision(domains):
  existing_cert = find_existing_cert(domains)
  if existing_cert:
    now = datetime.datetime.now(datetime.timezone.utc)
    not_after = existing_cert['Certificate']['NotAfter']
    return (not_after - now).days <= 30
  else:
    return True

def find_existing_cert(domains):
  domains = frozenset(domains.split(','))

  client = boto3.client('acm')
  paginator = client.get_paginator('list_certificates')
  iterator = paginator.paginate(PaginationConfig={'MaxItems':1000})

  for page in iterator:
    for cert in page['CertificateSummaryList']:
      cert = client.describe_certificate(CertificateArn=cert['CertificateArn'])
      sans = frozenset(cert['Certificate']['SubjectAlternativeNames'])
      if sans.issubset(domains):
        return cert

  return None

def notify_via_sns(topic_arn, domains, certificate):
  process = subprocess.Popen(['openssl', 'x509', '-noout', '-text'],
    stdin=subprocess.PIPE, stdout=subprocess.PIPE, encoding='utf8')
  stdout, stderr = process.communicate(certificate)

  client = boto3.client('sns')
  client.publish(TopicArn=topic_arn,
    Subject='Issued new LetsEncrypt certificate',
    Message='Issued new certificates for domains: ' + domains + '\n\n' + stdout,
  )

def upload_cert_to_acm(cert, domains):
  existing_cert = find_existing_cert(domains)
  certificate_arn = existing_cert['Certificate']['CertificateArn'] if existing_cert else None

  client = boto3.client('acm')
  acm_response = client.import_certificate(
    CertificateArn=certificate_arn,
    Certificate=cert['certificate'],
    PrivateKey=cert['private_key'],
    CertificateChain=cert['certificate_chain']
  )

  return None if certificate_arn else acm_response['CertificateArn']

def handler(event, context):
  try:
    domains = os.environ['LETSENCRYPT_DOMAINS']
    if should_provision(domains):
      cert = provision_cert(os.environ['LETSENCRYPT_EMAIL'], domains)
      upload_cert_to_acm(cert, domains)
      notify_via_sns(os.environ['NOTIFICATION_SNS_ARN'], domains, cert['certificate'])
  except:
    client = raven.Client(os.environ['SENTRY_DSN'], transport=raven.transport.http.HTTPTransport)
    client.captureException()
    raise

Почтовый файл составляет около 20 МБ.Я нашел ресурсов на AWS о локальной отладке, но, честно говоря, я немного растерялся, как начать.

Я не очень хорошо знаком с AWS и Lambda в целом, хотя ядовольно комфортно с Python.

Я работаю в MacOS и использую визуальный студийный код в качестве редактора.Я могу создать virtualenv на моем Mac, если это поможет.

Как мне отладить лямбду на моем локальном MacBook Pro?

1 Ответ

1 голос
/ 14 июня 2019

Как вы обнаружили, вы можете использовать AWS SAM (с Docker) для локальной отладки.

Ниже приведено пошаговое руководство по началу работы:

Предварительные условия

  1. Установка Docker: https://docs.docker.com/install/
  2. Установить AWS CLI: https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html
  3. Установить AWS SAM CLI: https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html
  4. Установить pipenv: https://docs.pipenv.org/en/latest/install/

Создать примерproject

Полезно для того, чтобы дать вам представление о структуре и отладке локально - вы можете заменить пример кода своим собственным.

sam init --runtime python3.7

Добавление зависимостей

pipenv shell
pipenv install package-names

Локальный запуск и отладка

pipenv lock -r > requirements.txt
sam build --manifest requirements.txt
sam local invoke HelloWorldFunction --event event.json

Развертывание в AWS Lambda

При необходимости создайте новое ведро, в котором будет храниться код функции:

aws s3 mb s3://bucket-name

Создайте и запустите сценарий .sh:

#!/bin/bash

pipenv lock -r > requirements.txt && sam build --manifest requirements.txt

sam package \
    --output-template-file packaged.yaml \
    --s3-bucket bucket-name

sam deploy \
    --template-file packaged.yaml \
    --stack-name name-of-lambda-stack \
    --capabilities CAPABILITY_IAM \
    --region us-east-1

Замена:

  • bucket-name именем корзины S3 для хранения кода функции
  • name-of-lambda-stack именем лямбда-стека AWS для развертыванияна
  • us-east-1 с другим регионом при желании

Ваша функция теперь развернута в AWS Lambda.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...