Cookiecutter - Django: Anymail[SES] ошибка региона boto3

Я пытаюсь развернуть на AWS(EC2) проект Cookiecutter Django. Пользователь AWS с этими учетными данными имеет полные политики S3, SES и SNS. Сервер EC2 также играет роль с полными политиками SES / S3.

В производственном файле в envs у меня есть такие ключи.

      DJANGO_AWS_ACCESS_KEY_ID=xxxxxxxxx
DJANGO_AWS_SECRET_ACCESS_KEY=xxxxxxxxxx
DJANGO_AWS_STORAGE_BUCKET_NAME=xxxxxxxxxx 

В настройках у меня есть

      AWS_S3_REGION_NAME = env("DJANGO_AWS_S3_REGION_NAME", default=None)
AWS_ACCESS_KEY_ID = env("DJANGO_AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = env("DJANGO_AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = env("DJANGO_AWS_STORAGE_BUCKET_NAME")
EMAIL_BACKEND = "anymail.backends.amazon_ses.EmailBackend"
ANYMAIL = {}

Все хорошо и хорошо, пока проект не попытается отправить электронное письмо с помощью SES, и он не выйдет из строя с ошибкой ниже.

До сих пор я пробовал:

  • добавление DJANGO_AWS_S3_REGION_NAME в производственный файл в envs - нет результата
  • добавление региона в конфигурацию aws с помощью aws cli - нет результата
  • переопределение настроек в ANYMAIL = {} с учетными данными и регионом - нет результата
  • создать пустой проект, просто добавив учетные данные aws и ничего не меняя - нет результата
  • создание вручную в другом проекте boto3.session.client с теми же учетными данными и отправка почты - это работает

Это ошибка. Вторая часть с объектом «NoneType» не имеет атрибута «send_raw_email» после этого много раз повторяется.

      django_1    | [2021-08-13 13:58:14 +0000] [12] [ERROR] Error handling request /accounts/signup/
django_1    | Traceback (most recent call last):
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
django_1    |     response = get_response(request)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 181, in _get_response
django_1    |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
django_1    |   File "/usr/local/lib/python3.9/contextlib.py", line 79, in inner
django_1    |     return func(*args, **kwds)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
django_1    |     return self.dispatch(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
django_1    |     return bound_method(*args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/views/decorators/debug.py", line 89, in sensitive_post_parameters_wrapper
django_1    |     return view(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/views.py", line 230, in dispatch
django_1    |     return super(SignupView, self).dispatch(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/views.py", line 74, in dispatch
django_1    |     response = super(RedirectAuthenticatedUserMixin, self).dispatch(
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/views.py", line 204, in dispatch
django_1    |     return super(CloseableSignupMixin, self).dispatch(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
django_1    |     return handler(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/views.py", line 102, in post
django_1    |     response = self.form_valid(form)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/views.py", line 248, in form_valid
django_1    |     return complete_signup(
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/utils.py", line 209, in complete_signup
django_1    |     return perform_login(
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/utils.py", line 175, in perform_login
django_1    |     send_email_confirmation(request, user, signup=signup, email=email)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/utils.py", line 346, in send_email_confirmation
django_1    |     email_address.send_confirmation(request, signup=signup)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/models.py", line 62, in send_confirmation
django_1    |     confirmation.send(request, signup=signup)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/models.py", line 169, in send
django_1    |     get_adapter(request).send_confirmation_mail(request, self, signup)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/adapter.py", line 464, in send_confirmation_mail
django_1    |     self.send_mail(email_template, emailconfirmation.email_address.email, ctx)
django_1    |   File "/usr/local/lib/python3.9/site-packages/allauth/account/adapter.py", line 136, in send_mail
django_1    |     msg.send()
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/mail/message.py", line 284, in send
django_1    |     return self.get_connection(fail_silently).send_messages([self])
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/base.py", line 89, in send_messages
django_1    |     created_session = self.open()
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/amazon_ses.py", line 44, in open
django_1    |     self.client = boto3.session.Session(**self.session_params).client("ses", **self.client_params)
django_1    |   File "/usr/local/lib/python3.9/site-packages/boto3/session.py", line 258, in client
django_1    |     return self._session.create_client(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/session.py", line 847, in create_client
django_1    |     client = client_creator.create_client(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 86, in create_client
django_1    |     client_args = self._get_client_args(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 355, in _get_client_args
django_1    |     return args_creator.get_client_args(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/args.py", line 71, in get_client_args
django_1    |     final_args = self.compute_client_args(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/args.py", line 148, in compute_client_args
django_1    |     endpoint_config = self._compute_endpoint_config(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/args.py", line 220, in _compute_endpoint_config
django_1    |     return self._resolve_endpoint(**resolve_endpoint_kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/args.py", line 302, in _resolve_endpoint
django_1    |     return endpoint_bridge.resolve(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/client.py", line 430, in resolve
django_1    |     resolved = self.endpoint_resolver.construct_endpoint(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/regions.py", line 133, in construct_endpoint
django_1    |     result = self._endpoint_for_partition(
django_1    |   File "/usr/local/lib/python3.9/site-packages/botocore/regions.py", line 148, in _endpoint_for_partition
django_1    |     raise NoRegionError()
django_1    | botocore.exceptions.NoRegionError: You must specify a region.
django_1    |
django_1    | During handling of the above exception, another exception occurred:
django_1    |
django_1    | Traceback (most recent call last):
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
django_1    |     response = get_response(request)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/utils/deprecation.py", line 114, in __call__
django_1    |     response = response or self.get_response(request)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 49, in inner
django_1    |     response = response_for_exception(request, exc)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 104, in response_for_exception
django_1    |     log_response(
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/utils/log.py", line 224, in log_response
django_1    |     getattr(logger, level)(
django_1    |   File "/usr/local/lib/python3.9/logging/__init__.py", line 1475, in error
django_1    |     self._log(ERROR, msg, args, **kwargs)
django_1    |   File "/usr/local/lib/python3.9/logging/__init__.py", line 1589, in _log
django_1    |     self.handle(record)
django_1    |   File "/usr/local/lib/python3.9/logging/__init__.py", line 1599, in handle
django_1    |     self.callHandlers(record)
django_1    |   File "/usr/local/lib/python3.9/logging/__init__.py", line 1661, in callHandlers
django_1    |     hdlr.handle(record)
django_1    |   File "/usr/local/lib/python3.9/logging/__init__.py", line 952, in handle
django_1    |     self.emit(record)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/utils/log.py", line 122, in emit
django_1    |     self.send_mail(subject, message, fail_silently=True, html_message=html_message)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/utils/log.py", line 125, in send_mail
django_1    |     mail.mail_admins(subject, message, *args, connection=self.connection(), **kwargs)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/mail/__init__.py", line 104, in mail_admins
django_1    |     mail.send(fail_silently=fail_silently)
django_1    |   File "/usr/local/lib/python3.9/site-packages/django/core/mail/message.py", line 284, in send
django_1    |     return self.get_connection(fail_silently).send_messages([self])
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/base.py", line 94, in send_messages
django_1    |     sent = self._send(message)
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/base.py", line 124, in _send
django_1    |     response = self.post_to_esp(payload, message)
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/amazon_ses.py", line 67, in post_to_esp
django_1    |     response = payload.call_send_api(self.client)
django_1    |   File "/usr/local/lib/python3.9/site-packages/anymail/backends/amazon_ses.py", line 127, in call_send_api
django_1    |     return ses_client.send_raw_email(**self.params)
django_1    | AttributeError: 'NoneType' object has no attribute 'send_raw_email'

Буду признателен за любой вклад. У меня нет идей. Спасибо!

1 ответ

Как вы, наверное, догадались, проблема в том, что boto3 не знает, в каком регионе AWS вы пытаетесь работать:

botocore.exceptions.NoRegionError: You must specify a region.

Название региона взято из конфигурации boto3. Из документов Anymail SES :

вы должны убедиться, что boto3 настроен с учетными данными AWS, имеющими необходимые разрешения IAM. Есть несколько способов сделать это; см учетных данных в документации Boto опций. Обычно подходит роль IAM для инстансов EC2, стандартные переменные среды Boto или общий файл учетных данных AWS. В более сложных случаях используйте Anymail's настройка для настройки сеанса Boto.

Похоже, вы можете попробовать смешать некоторые из «нескольких способов» предоставления учетных данных boto3, что может вызвать путаницу.

Обратите внимание, что ваши настройки Django не используются. boto3 не знает о настройках Django. Документация по настройкам SES Anymail описывает, какие настройки Django Anymail передаст в boto3. (И AWS_S3_REGION_NAMEв любом случае не будет иметь значения, потому что S3 - это не то же самое, что и SES. Я предполагаю те AWS_* Настройки Django могут быть для какого-то другого приложения, может быть, django-storerages.)

Если вы хотите предоставить учетные данные AWS специально для Anymail в settings.py, вы можете сделать это с помощью Anymail AMAZON_SES_CLIENT_PARAMSпараметр. Например:

      # (Be sure to add DJANGO_AWS_REGION_NAME to your env to use this example)
ANYMAIL = {
    "AMAZON_SES_CLIENT_PARAMS": {
        "aws_access_key_id": env("DJANGO_AWS_ACCESS_KEY_ID"),
        "aws_secret_access_key": env("DJANGO_AWS_SECRET_ACCESS_KEY"),
        "region_name": env("DJANGO_AWS_REGION_NAME"),
    },
}
Другие вопросы по тегам