SMTP email integration in cloud composer - email

I want to use SMTP email in GCP cloud composer. I have followed the GCP documentation and did the following -
Created a secret with my SMTP password (tested with secret name as airflow-variables-smtp-password & airflow-config-smtp-password)
Provided secret accessor role to my svc account
Modified email backend and all configs -
[email]
email_backend = airflow.utils.email.send_email_smtp
[smtp]
smtp_port = 587
smtp_user = ****
smtp_mail_from = ****
smtp_password_secret = smtp-password
smtp_host = smtp.office365.com
Changed secret backend -
[secrets]
backend = airflow.providers.google.cloud.secrets.secret_manager.CloudSecretManagerBackend
Below is my email dag which is subject to fail (tested with email operator as well) -
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
def print_hello():
pppp
return 'Hello Wolrd'
args = {
'owner': '***',
'depends_on_past': False,
'start_date': "2021-09-14",
'email_on_failure': True
}
with DAG(
dag_id='mail_testing',
default_args=args,
) as dag:
python_task = PythonOperator(
task_id='email_task',
python_callable=print_hello,
email_on_failure=True,
email= ['***'],
dag=dag
)
When running the dag, I am seeing it is looking for sendgrid to send the email.
[2021-09-29 17:42:20,958] {logging_mixin.py:104} WARNING - /opt/python3.8/lib/python3.8/site-packages/airflow/providers/sendgrid/utils/emailer.py:122 PendingDeprecationWarning: Fetching Sendgrid credentials from environment variables will be deprecated in a future release. Please set credentials using a connection instead.
[2021-09-29 17:42:21,081] {taskinstance.py:1503} ERROR - Task failed with exception
Traceback (most recent call last):
File "/opt/python3.8/lib/python3.8/site-packages/airflow/models/taskinstance.py", line 1158, in _run_raw_task
self._prepare_and_execute_task_with_callbacks(context, task)
File "/opt/python3.8/lib/python3.8/site-packages/airflow/models/taskinstance.py", line 1333, in _prepare_and_execute_task_with_callbacks
result = self._execute_task(context, task_copy)
File "/opt/python3.8/lib/python3.8/site-packages/airflow/models/taskinstance.py", line 1363, in _execute_task
result = task_copy.execute(context=context)
File "/opt/python3.8/lib/python3.8/site-packages/airflow/operators/email.py", line 79, in execute
send_email(
File "/opt/python3.8/lib/python3.8/site-packages/airflow/utils/email.py", line 55, in send_email
return backend(
File "/opt/python3.8/lib/python3.8/site-packages/airflow/providers/sendgrid/utils/emailer.py", line 122, in send_email
_post_sendgrid_mail(mail.get(), conn_id)
File "/opt/python3.8/lib/python3.8/site-packages/airflow/providers/sendgrid/utils/emailer.py", line 141, in _post_sendgrid_mail
response = sendgrid_client.client.mail.send.post(request_body=mail_data)
File "/opt/python3.8/lib/python3.8/site-packages/python_http_client/client.py", line 277, in http_request
self._make_request(opener, request, timeout=timeout)
File "/opt/python3.8/lib/python3.8/site-packages/python_http_client/client.py", line 184, in _make_request
raise exc
python_http_client.exceptions.UnauthorizedError: HTTP Error 401: Unauthorized
[2021-09-29 17:42:21,084] {taskinstance.py:1546} INFO - Marking task as UP_FOR_RETRY.
What am I doing wrong? Is there anything I am missing!

PendingDeprecationWarning: Fetching Sendgrid credentials from environment variables will be deprecated in a future release. Please set credentials using a connection instead.
These docs describe configuring Sendgrid credentials in a connection. I would follow them, as they include steps for troubleshooting.
The other possibility is that it's an issue with your Sendgrid credentials. You can test your sendGrid credentials are valid by sending a test-email via a curl request.

Related

SSL certificate error with Python sending email code

Im trying to use python to build an email sender.
Ive been getting an SSL certificate error, though I downloaded it.
Im also getting an SMTP error, which i think shouldn't be happening.
I've tried to fix this error but the solution I found was that of Mac and not windows.
The error:
with smtplib.SMTP_SSL( 'smtp.gmail.com' , 465, context=context) as smtp:
File "C:\Program Files\Inkscape\lib\python3.10\smtplib.py", line 1050, in __init__
SMTP.__init__(self, host, port, local_hostname, timeout,
File "C:\Program Files\Inkscape\lib\python3.10\smtplib.py", line 255, in __init__
(code, msg) = self.connect(host, port)
File "C:\Program Files\Inkscape\lib\python3.10\smtplib.py", line 341, in connect
self.sock = self._get_socket(host, port, self.timeout)
File "C:\Program Files\Inkscape\lib\python3.10\smtplib.py", line 1057, in _get_socket
new_socket = self.context.wrap_socket(new_socket,
File "C:\Program Files\Inkscape\lib\python3.10\ssl.py", line 513, in wrap_socket
return self.sslsocket_class._create(
File "C:\Program Files\Inkscape\lib\python3.10\ssl.py", line 1071, in _create
self.do_handshake()
File "C:\Program Files\Inkscape\lib\python3.10\ssl.py", line 1342, in do_handshake
self._sslobj.do_handshake()
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:997)
The code:
from email.message import EmailMessage
from emailApp import password
import ssl
try:
_create_unverified_https_context = ssl._create_unverified_context
except AttributeError:
# Legacy Python that doesn't verify HTTPS certificates by default
pass
else:
# Handle target environment that doesn't support HTTPS verification
ssl._create_default_https_context = _create_unverified_https_context
import smtplib
email_sender = 'example2#mail.com'
email_password = password
email_receiver = 'example#gmail.com'
subject = "msg"
body = """
Hello
"""
em = EmailMessage()
em['From'] = email_sender
em['To'] = email_receiver
em['Subject'] = subject
em.set_content(body)
context = ssl.create_default_context()
context = ssl.create_default_context()
with smtplib.SMTP_SSL( 'smtp.gmail.com' , 465, context=context) as smtp:
smtp.login(email_sender, email_password)
smtp.sendmail(email_sender, email_receiver, em.as_string())

exeuction_timeout is failing the task but not sending email

I want to fail the task if task duration exceeds a certain time for which I configured execution_timeout in every task of the dag. However, the task is failing, but I am not getting email notification of it. Does anyone know the reason? Any help would be appreciated.
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'start_date': datetime.today() - timedelta(days = 2),
'email':['email#company.com'],
'email_on_failure': True
}
[2022-10-27 03:06:42,587] {logging_mixin.py:112} INFO - [2022-10-27 03:06:42,585] {timeout.py:42} ERROR - Process timed out, PID: 15847
[2022-10-27 03:06:42,588] {bash_operator.py:140} INFO - Sending SIGTERM signal to bash process group
[2022-10-27 03:06:42,602] {taskinstance.py:1145} ERROR - Timeout, PID: 15847
Traceback (most recent call last):
File "/home/airflow_user/.local/lib/python3.7/site-packages/airflow/models/taskinstance.py", line 978, in _run_raw_task
result = task_copy.execute(context=context)
File "/home/airflow_user/.local/lib/python3.7/site-packages/airflow/operators/bash_operator.py", line 124, in execute
for line in iter(self.sub_process.stdout.readline, b''):
File "/home/airflow_user/.local/lib/python3.7/site-packages/airflow/utils/timeout.py", line 43, in handle_timeout
raise AirflowTaskTimeout(self.error_message)
airflow.exceptions.AirflowTaskTimeout: Timeout, PID: 15847
I can see the task turning to red. But no email is sent even with the email_on_failure config.
You have not configured your smtp in airflow.cfg, you need configure it:
[smtp]
smtp_host = #your smtp host
smtp_starttls = True
smtp_ssl = False
# Example: smtp_user = airflow
smtp_user = # your email to send
# Example: smtp_password = airflow
smtp_password = # App password
smtp_port = # your smtp port
smtp_mail_from = # your email to send
smtp_timeout = 30
smtp_retry_limit = 5
Example, if you are using gmail:
[smtp]
smtp_host = smtp.gmail.com
smtp_starttls = True
smtp_ssl = False
# Example: smtp_user = airflow
smtp_user = example#gmail.com
# Example: smtp_password = airflow
smtp_password = #app password
smtp_port = 25
smtp_mail_from = example#gmail.com
smtp_timeout = 30
smtp_retry_limit = 5
Note: you can take your app password in here: https://support.google.com/mail/answer/185833?hl=en
Gmail port can be 25, 465 or 587

JupyterHub generic.oauthenticator: 500 Internal Server Error / 400 OAuth state missing from cookies

I'm creating a Dashboard service where multiple users can access multiple notebook servers via JupyterHub.
Users log in using a separate Authentication service, and once in Dashboard, JupyterHub gets rendered inside an <iframe>.
I'm hosting JupyterHub in a Kubernetes cluster on GCP. These are the relevant portions of my config.yaml:
...
# Changes the Jupyter's notebook headers to allow it to be displayed
# inside of an iframe object.
c.JupyterHub.tornado_settings = {
"headers": {
"Content-Security-Policy": "default-src * data: blob: filesystem: about: ws: wss: 'unsafe-inline' 'unsafe-eval' 'unsafe-dynamic'; script-src * data: blob: 'unsafe-inline' 'unsafe-eval'; connect-src * data: blob: 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src * data: blob: ; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';",
}
}
...
auth:
type: custom
custom:
className: oauthenticator.generic.GenericOAuthenticator
config:
login_service: "OpenID"
client_id: "<client_id>"
client_secret: "<client_secret>"
However, when I try to sign in just via my JupyterHub endpoint I get:
500 Internal Server Error
logs:
IwN2YyNzVhNTk0MzM0ZTM5YmIyZTdmZDQzY2U2ZDI3YyIsICJuZXh0X3VybCI6IG51bGx9&session_state=641fb9aca6490d2c3332871d72ee0639b3ed46b8a9f02c363ee8f7bf46f1c989.74e37d534296
cad45421019d1d442153', version='HTTP/1.1', remote_ip='<>')
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/tornado/web.py", line 1703, in _execute
result = await result
File "/usr/local/lib/python3.6/dist-packages/oauthenticator/oauth2.py", line 213, in get
user = await self.login_user()
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/handlers/base.py", line 699, in login_user
authenticated = await self.authenticate(data)
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/auth.py", line 383, in get_authenticated_user
authenticated = await maybe_future(self.authenticate(handler, data))
File "/usr/local/lib/python3.6/dist-packages/oauthenticator/generic.py", line 136, in authenticate
resp_json = json.loads(resp.body.decode('utf8', 'replace'))
File "/usr/lib/python3.6/json/__init__.py", line 354, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.6/json/decoder.py", line 339, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.6/json/decoder.py", line 357, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 5 column 1 (char 4)
I know it actually does redirect to the Authentication Server and the callback is correct. Even still when I attempt to access via Dashboard, within the <iframe> I get:
400 OAuth state missing from cookies
logs:
400 GET /hub/oauth_callback?code=d37de16a855a49dda94fb65c2c2c8ffa&state=eyJzdGF0ZV9pZCI6ICIwN2YyNzVhNTk0MzM0ZTM5Ym
IyZTdmZDQzY2U2ZDI3YyIsICJuZXh0X3VybCI6IG51bGx9&session_state=641fb9aca6490d2c3332871d72ee0639b3ed46b8a9f02c363ee8f7bf46f1c989.74e37d534296cad45421019d1d442153
OAuth state missing from cookies
For context: I previously used the Github OAuthenticator class and registered an OAuth app within Github and got JupyterHub to work fine. However, Github does not allow their apps to be used within an <iframe> so I had to go the generic route.
Point is, the setup was very similar and at least had JupyterHub working outside of Dashboard, so I'm really stumped on what else I could be missing. Any help is appreciated.

Not able to trigger DAG from Airflow API but its working from Curl command

I am trying to trigger DAG from Airflow API through python script.
DAG is triggering from curl command but its not working from API.
import requests
url ='http://localhost:8080/api/experimental/dags/document_validation/dag_runs'
myobj = {''}
x =requests.post(url, data=myobj, headers={"Content-Type": "application/json"})
print(x.text)
Error I am getting
File "run_dag_api.py", line 6, in <module>
  `x = requests.post(url, data = myobj)`
File "/…/lib/python3.7/site-packages/requests/api.py", line 119, in post
  `return request('post', url, data=data, json=json, **kwargs)`
File "/…/lib/python3.7/site-packages/requests/api.py", line 61, in request
  `return session.request(method=method, url=url, **kwargs)`
File "/…/lib/python3.7/site-packages/requests/sessions.py", line 530, in request
  `resp = self.send(prep, **send_kwargs)`
File "/…/lib/python3.7/site-packages/requests/sessions.py", line 643, in send
  `r = adapter.send(request, **kwargs)`
File "/…/lib/python3.7/site-packages/requests/adapters.py", line 498, in send
  `raise ConnectionError(err, request=request)`
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))

python: sending a mail, fails when inside a "with" block

I am wondering why this code
test = smtplib.SMTP('smtp.gmail.com', 587)
test.ehlo()
test.starttls()
test.ehlo()
test.login('address','passw')
test.sendmail(sender, recipients, composed)
test.close()
works, but when written like this
with smtplib.SMTP('smtp.gmail.com', 587) as s:
s.ehlo()
s.starttls()
s.ehlo()
s.login('address','passw')
s.sendmail(sender, recipients, composed)
s.close()
it fails with the message
Unable to send the email. Error: <class 'AttributeError'>
Traceback (most recent call last):
File "py_script.py", line 100, in <module>
with smtplib.SMTP('smtp.gmail.com', 587) as s:
AttributeError: __exit__
Why is this happening? (python3 on a raspberry pi)
Thx
You are not using Python 3.3 or up. In your version of Python, smtplib.SMTP() is not a context manager and cannot be using in a with statement.
The traceback is directly caused because there is no __exit__ method, a requirement for context managers.
From the smptlib.SMTP() documentation:
Changed in version 3.3: Support for the with statement was added.
You can wrap the object in a context manager with #contextlib.contextmanager:
from contextlib import contextmanager
from smtplib import SMTPResponseException, SMTPServerDisconnected
#contextmanager
def quitting_smtp_cm(smtp):
try:
yield smtp
finally:
try:
code, message = smtp.docmd("QUIT")
if code != 221:
raise SMTPResponseException(code, message)
except SMTPServerDisconnected:
pass
finally:
smtp.close()
This uses the same exit behaviour as was added in Python 3.3. Use it like this:
with quitting_smtp_cm(smtplib.SMTP('smtp.gmail.com', 587)) as s:
s.ehlo()
s.starttls()
s.ehlo()
s.login('address','passw')
s.sendmail(sender, recipients, composed)
Note that it'll close the connection for you.