Repository: mdn/kuma

Issue:
T - SMTPConnectError when trying to send verification email
https://sentry.prod.mozaws.net/operations/mdn-prod/issues/8817911/?referrer=github_plugin

```
Resolver404: {'tried': [[<URLPattern '^media/(?:redesign/)?css/(?P<doc>.*)-min.css$'>], [<URLPattern '^media/(?:redesign/)?js/(?P<doc>.*)-min.js$'>], [<URLPattern '^media/(?:redesign/)?img(?P<suffix>.*)$'>], [<URLPattern '^media/(?:redesign/)?css(?P<suffix>.*)$'>], [<URLPattern '^media/(?:redesign/)?js(?P<suffix>.*)$'>], [<URLPattern '^media/(?:redesign/)?fonts(?P<suffix>.*)$'>], [<URLPattern '^media/uploads/demos/(?:.*)$'>], [<URLPattern '(?i)^(?P<one>.*)//(?P<two>.*)//(?P<three>.*)$'>], [<URLPattern '(?i)^(?P<one>.*)//(?P<two>.*)$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_1_canvas_rect.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_2_canvas_moveto.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_3_canvas_lineto.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_4_canvas_arc.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_5_canvas_quadraticcurveto.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/2_6_canvas_beziercurveto.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/3_1_canvas_drawimage.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/3_2_canvas_drawimage.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/3_3_canvas_drawimage.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/3_4_canvas_gallery.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_1_canvas_fillstyle.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_2_canvas_strokestyle.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_3_canvas_globalalpha.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_4_canvas_rgba.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_5_canvas_linewidth.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_6_canvas_linecap.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_7_canvas_linejoin.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_8_canvas_miterlimit.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_9_canvas_lineargradient.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_10_canvas_radialgradient.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/4_11_canvas_createpattern.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/5_1_canvas_savestate.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/5_2_canvas_translate.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/5_3_canvas_rotate.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/5_4_canvas_scale.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/6_1_canvas_composite.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/6_2_canvas_clipping.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/globalCompositeOperation.html$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/backdrop.png$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/bg_gallery.png$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_1.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_2.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_3.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_4.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_5.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_6.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_7.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/gallery_8.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/picture_frame.png$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/rhino.jpg$'>], [<URLPattern '(?i)^samples/canvas-tutorial/images/wallpaper.png$'>], [<URLPattern '(?i)^samples/domref/mozGetAsFile.html$'>], [<URLPattern '(?i)^samples/raycaster/input.js$'>], [<URLPattern '(?i)^samples/raycaster/Level.js$'>], [<URLPattern '(?i)^samples/raycaster/Player.js$'>], [<URLPattern '(?i)^samples/raycaster/RayCaster.html$'>], [<URLPattern '(?i)^samples/raycaster/RayCaster.js$'>], [<URLPattern '(?i)^samples/raycaster/trace.css$'>], [<URLPattern '(?i)^samples/raycaster/trace.js$'>], [<URLPattern '(?i)^samples/webgl/sample1$'>], [<URLPattern '(?i)^samples/webgl/sample1/index.html$'>], [<URLPattern '(?i)^samples/webgl/sample1/webgl-demo.js$'>], [<UR...
  File "redirect_urls/middleware.py", line 14, in __call__
    resolver_match = self.resolver.resolve(request.path_info)
  File "newrelic/hooks/framework_django.py", line 601, in wrapper
    return _wrapped(*args, **kwargs)
  File "newrelic/hooks/framework_django.py", line 589, in _wrapped
    result = wrapped(path)
  File "django/urls/resolvers.py", line 567, in resolve
    raise Resolver404({'tried': tried, 'path': new_path})

SMTPConnectError: (420, b'Resources unavailable temporarily. Please try later. [SLLB01]')
(22 additional frame(s) were not displayed)
...
  File "allauth/account/managers.py", line 19, in add_email
    email_address.send_confirmation(request, signup=signup)
  File "allauth/account/models.py", line 58, in send_confirmation
    confirmation.send(request, signup=signup)
  File "allauth/account/models.py", line 163, in send
    get_adapter(request).send_confirmation_mail(request, self, signup)
  File "allauth/account/adapter.py", line 448, in send_confirmation_mail
    self.send_mail(email_template,
  File "allauth/account/adapter.py", line 137, in send_mail
    msg.send()

SMTPConnectError: (420, b'Resources unavailable temporarily. Please try later. [SLLB01]')
```


Discussion:
Seems it's happening where when you edit your email. 

Whatever we do with the `SMTPConnectionError` (or whatever it's called) we should add `SMTPConnectError` to that list of operational exceptions. 

Resolving patch:
diff --git a/kuma/core/utils.py b/kuma/core/utils.py
--- a/kuma/core/utils.py
+++ b/kuma/core/utils.py
@@ -3,7 +3,7 @@
 import logging
 import os
 from itertools import islice
-from smtplib import SMTPServerDisconnected
+from smtplib import SMTPConnectError, SMTPServerDisconnected
 from urllib.parse import parse_qsl, ParseResult, urlparse, urlsplit, urlunsplit
 
 import requests
@@ -586,6 +586,7 @@ class EmailMultiAlternativesRetrying(EmailMultiAlternatives):
     we're very explicit about the of exceptions we treat as retry'able.
     The list of exceptions we use to trigger a retry are:
 
+        * smtplib.SMTPConnectError
         * smtplib.SMTPServerDisconnected
 
     Only list exceptions that have been known to happen and are safe.
@@ -596,7 +597,7 @@ def send(self, *args, retry_options=None, **kwargs):
         # for a list of the default options to the redo.retry function
         # which the redo.retrying context manager wraps.
         retry_options = retry_options or {
-            "retry_exceptions": (SMTPServerDisconnected,),
+            "retry_exceptions": (SMTPConnectError, SMTPServerDisconnected),
             # The default in redo is 60 seconds. Let's tone that down.
             "sleeptime": 3,
             "attempts": 10,
diff --git a/kuma/users/adapters.py b/kuma/users/adapters.py
--- a/kuma/users/adapters.py
+++ b/kuma/users/adapters.py
@@ -1,4 +1,4 @@
-from smtplib import SMTPServerDisconnected
+from smtplib import SMTPConnectError, SMTPServerDisconnected
 
 from allauth.account.adapter import DefaultAccountAdapter, get_adapter
 from allauth.account.models import EmailAddress
@@ -157,7 +157,7 @@ def save_user(self, request, user, form, commit=True):
 
     def send_mail(self, *args, **kwargs):
         retry_options = {
-            "retry_exceptions": (SMTPServerDisconnected,),
+            "retry_exceptions": (SMTPConnectError, SMTPServerDisconnected),
             # The default in redo is 60 seconds. Let's tone that down.
             "sleeptime": 3,
             "attempts": 10,


Test patch: