Added Send mail functionality & Dockerized application #1
129
.gitignore
vendored
Normal file
129
.gitignore
vendored
Normal 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/
|
|
@ -1,3 +1,10 @@
|
||||||
# influx-smtp-gateway
|
# 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
|
|||||||
|
server_port = 8081
|
||||||
|
to_mail = test111@mailinator.com
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
add smtp server name add smtp server name
|
|||||||
|
from_mail = test@gmail.com
|
||||||
|
from_mail_password = test@123
|
|
@ -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
|
|
||||||
|
|
|
@ -3,8 +3,7 @@ version: '3.8'
|
||||||
services:
|
services:
|
||||||
web:
|
web:
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
`smtp-gateway:`
|
|||||||
build: .
|
build: .
|
||||||
command: python main.py
|
|
||||||
ports:
|
ports:
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
perhaps this should be inside the perhaps this should be inside the `Dockerfile`
|
|||||||
- 8081:8081
|
- 8081:8081
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
If we only use this internally inside a docker service, we should use If we only use this internally inside a docker service, we should use `expose`, not `port`.
|
|||||||
env_file:
|
env_file:
|
||||||
- ./config.ini
|
- ./.env
|
||||||
|
|
99
main.py
99
main.py
|
@ -12,98 +12,14 @@ import sys
|
||||||
from email.header import Header
|
from email.header import Header
|
||||||
from email.mime.text import MIMEText
|
from email.mime.text import MIMEText
|
||||||
|
|
||||||
import uuid as uuid_module
|
|
||||||
from ipaddress import AddressValueError, IPv4Address, IPv6Address
|
|
||||||
|
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
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
PeterSurda
commented
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:
|
class CloudInitApp:
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
docstring docstring
|
|||||||
"""
|
"""
|
||||||
Serve cloud init files
|
Serve cloud init files
|
||||||
PeterSurda marked this conversation as resolved
PeterSurda
commented
this should go under this should go under `__main__`
|
|||||||
"""
|
"""
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
remove 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):
|
def _send_mail(self):
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
override from env variables override from env variables
|
|||||||
try:
|
try:
|
||||||
# pylint: disable=deprecated-lambda
|
# pylint: disable=deprecated-lambda
|
||||||
|
@ -139,10 +55,17 @@ class CloudInitApp:
|
||||||
ROOT = CloudInitApp()
|
ROOT = CloudInitApp()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
`SERVER_PORT` should default to `587` and be an integer.
|
|||||||
cherrypy.server.socket_host = \
|
try:
|
||||||
CONFIG["server"].get("server_host", "127.0.0.1")
|
SERVER_HOST = os.environ["server_host"]
|
||||||
cherrypy.server.socket_port = \
|
SERVER_PORT = int(os.environ["server_port"])
|
||||||
CONFIG["server"].getint("server_port", 8081)
|
TO_MAIL = os.environ["to_mail"]
|
||||||
|
FROM_MAIL = os.environ["from_mail"]
|
||||||
PeterSurda marked this conversation as resolved
Outdated
PeterSurda
commented
`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
PeterSurda
commented
`except smtplib.SMTPException`
|
|||||||
|
cherrypy.server.socket_port = SERVER_PORT
|
||||||
ENGINE = cherrypy.engine
|
ENGINE = cherrypy.engine
|
||||||
|
|
||||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
|
@ -6,12 +6,9 @@ jaraco.collections==3.5.1
|
||||||
jaraco.context==4.1.1
|
jaraco.context==4.1.1
|
||||||
jaraco.functools==3.5.0
|
jaraco.functools==3.5.0
|
||||||
jaraco.text==3.7.0
|
jaraco.text==3.7.0
|
||||||
Jinja2==3.0.3
|
|
||||||
MarkupSafe==2.0.1
|
|
||||||
more-itertools==8.12.0
|
more-itertools==8.12.0
|
||||||
portend==3.1.0
|
portend==3.1.0
|
||||||
pytz==2021.3
|
pytz==2021.3
|
||||||
PyYAML==6.0
|
|
||||||
six==1.16.0
|
six==1.16.0
|
||||||
tempora==5.0.1
|
tempora==5.0.1
|
||||||
zc.lockfile==2.0
|
zc.lockfile==2.0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user
remove as it's matched with docker service