Added Send mail functionality & Dockerized application #1

Merged
PeterSurda merged 11 commits from cis-kuldeep/influx-smtp-gateway:master into master 2022-02-21 06:41:14 +00:00
6 changed files with 149 additions and 103 deletions
Showing only changes of commit 7c4be18a6d - Show all commits

129
.gitignore vendored Normal file
View File

@ -0,0 +1,129 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/

View File

@ -1,3 +1,10 @@
# influx-smtp-gateway
SMTP gateway accessible from InfluxDB for sending alerts.
SMTP gateway accessible from InfluxDB for sending alerts.
# create .env file with following parameters
server_host = 0.0.0.0
PeterSurda marked this conversation as resolved Outdated

remove as it's matched with docker service

remove as it's matched with docker service
server_port = 8081
to_mail = test111@mailinator.com
PeterSurda marked this conversation as resolved Outdated

add smtp server name

add smtp server name
from_mail = test@gmail.com
from_mail_password = test@123

View File

@ -1,9 +0,0 @@
[server]
server_host = 0.0.0.0
server_port = 8081
[app]
to_mail = test111@mailinator.com
from_mail = test@gmail.com
from_mail_password = test@123

View File

@ -3,8 +3,7 @@ version: '3.8'
services:
web:
PeterSurda marked this conversation as resolved Outdated

smtp-gateway:

`smtp-gateway:`
build: .
command: python main.py
ports:
PeterSurda marked this conversation as resolved Outdated

perhaps this should be inside the Dockerfile

perhaps this should be inside the `Dockerfile`
- 8081:8081
PeterSurda marked this conversation as resolved Outdated

If we only use this internally inside a docker service, we should use expose, not port.

If we only use this internally inside a docker service, we should use `expose`, not `port`.
env_file:
- ./config.ini
- ./.env

99
main.py
View File

@ -12,98 +12,14 @@ import sys
from email.header import Header
from email.mime.text import MIMEText
import uuid as uuid_module
from ipaddress import AddressValueError, IPv4Address, IPv6Address
import cherrypy
PeterSurda marked this conversation as resolved Outdated

class name

class name
PATH = os.path.dirname(os.path.abspath(__file__))
CONFIG = configparser.ConfigParser()
CONFIG.read(os.path.join(PATH, "config.ini"))
TO_MAIL = CONFIG["app"].get("to_mail")
FROM_MAIL = CONFIG["app"].get("from_mail")
FROM_MAIL_PASSWORD = CONFIG["app"].get("from_mail_password")
print("TO_MAIL: ", TO_MAIL)
print("FROM_MAIL: ", FROM_MAIL)
print("FROM_MAIL_PASSWORD: ", FROM_MAIL_PASSWORD)
class CloudInitRequest:
"""
Request data for persistence across methods
"""
def __init__(self, request, uuid=None):
self.remoteip = None
self.hostinfo = ('localhost', )
self.request = request
self.meta_data = None
self.meta_data_loaded = False
self.user_data = None
try:
self.uuid = str(uuid_module.UUID('{' + uuid + '}'))
# ValueError is wrong UUID syntax
# TypeError is None
except (ValueError, TypeError):
self.uuid = None
self._init_ip()
def _can_ip_be_proxy(self):
"""
Assuming the connection is through a proxy, is this proxy permitted?
Can't proxy from a publicly reachable IP.
"""
self.remoteip = self.request.remote.ip
try:
ipobj = IPv4Address(self.remoteip)
except AddressValueError:
try:
ipobj = IPv6Address(self.remoteip)
except AddressValueError:
return False
return not ipobj.is_global
def _init_ip(self):
"""
Get remote IP
"""
if self._can_ip_be_proxy():
try:
self.remoteip = self.request.headers.get(
'X-Real-Ip',
self.request.remote.ip
)
except KeyError:
pass
try:
self.hostinfo = socket.gethostbyaddr(self.remoteip)
forward_lookup = socket.gethostbyname(self.hostinfo[0])
if forward_lookup != self.remoteip:
self.hostinfo = ('localhost', )
except socket.herror:
self.hostinfo = ('localhost', )
except socket.gaierror:
self.hostinfo = (self.remoteip, )
PeterSurda marked this conversation as resolved Outdated

should log to stdout or stderr (research which one is more appropriate)

should log to stdout or stderr (research which one is more appropriate)
class CloudInitApp:
PeterSurda marked this conversation as resolved Outdated

docstring

docstring
"""
Serve cloud init files
PeterSurda marked this conversation as resolved
Review

this should go under __main__

this should go under `__main__`
"""
PeterSurda marked this conversation as resolved Outdated

remove CONFIG and only use environment variables
if some variable missing, display a helpful error message and quit with non-zero error code

remove `CONFIG` and only use environment variables if some variable missing, display a helpful error message and quit with non-zero error code
@staticmethod
def _content_type(data):
if not data:
return "text/cloud-config"
if data.startswith("#include"):
return "text/x-include-url"
if data.startswith("## template: jinja"):
return "text/jinja2"
return "text/cloud-config"
def _send_mail(self):
PeterSurda marked this conversation as resolved Outdated

override from env variables

override from env variables
try:
# pylint: disable=deprecated-lambda
@ -139,10 +55,17 @@ class CloudInitApp:
ROOT = CloudInitApp()
if __name__ == "__main__":
PeterSurda marked this conversation as resolved Outdated

SERVER_PORT should default to 587 and be an integer.

`SERVER_PORT` should default to `587` and be an integer.
cherrypy.server.socket_host = \
CONFIG["server"].get("server_host", "127.0.0.1")
cherrypy.server.socket_port = \
CONFIG["server"].getint("server_port", 8081)
try:
SERVER_HOST = os.environ["server_host"]
SERVER_PORT = int(os.environ["server_port"])
TO_MAIL = os.environ["to_mail"]
FROM_MAIL = os.environ["from_mail"]
PeterSurda marked this conversation as resolved Outdated

except KeyError

`except KeyError`
FROM_MAIL_PASSWORD = os.environ["from_mail_password"]
except:
raise "Please check missing environment variables: to_mail, from_mail, from_mail_password"
cherrypy.server.socket_host = SERVER_HOST
PeterSurda marked this conversation as resolved Outdated

except smtplib.SMTPException

`except smtplib.SMTPException`
cherrypy.server.socket_port = SERVER_PORT
ENGINE = cherrypy.engine
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))

View File

@ -6,12 +6,9 @@ jaraco.collections==3.5.1
jaraco.context==4.1.1
jaraco.functools==3.5.0
jaraco.text==3.7.0
Jinja2==3.0.3
MarkupSafe==2.0.1
more-itertools==8.12.0
portend==3.1.0
pytz==2021.3
PyYAML==6.0
six==1.16.0
tempora==5.0.1
zc.lockfile==2.0