diff --git a/.env b/.env index 14e070594156cefa334ce3410afd074b34000c0c..f9c934b3c494623cc8b704f3e1cb31e9446e6938 100644 --- a/.env +++ b/.env @@ -34,3 +34,10 @@ CERN_OIDC_CLIENT_SECRET=fill-me #EMAIL_BACKEND=vendor.django_mail.backends.smimesmtp.EmailBackend #EMAIL_SMIME_CERT_FILE_PATH=/etc/notifications-noreply.pem #EMAIL_SMIME_CERT_KEY_FILE_PATH=/etc/notifications-noreply-key.pem + +# Auditing +# ETCD_HOST=localhost +# ETCD_PORT=2379 +# AUDITING=true +# ETCD_USER=audituser +# ETCD_PASSWORD=fill-me \ No newline at end of file diff --git a/.isort.cfg b/.isort.cfg index 918f3dd28c505839262a666e62be15db69e29191..310e5588152be10113c0167b9061ab357d58fa4d 100644 --- a/.isort.cfg +++ b/.isort.cfg @@ -4,4 +4,4 @@ multi_line_output=3 include_trailing_comma=True lines_after_imports=2 not_skip=__init__.py -known_third_party = Crypto,apns2,cachetools,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml +known_third_party = Crypto,apns2,cachetools,etcd3,jinja2,mattermostdriver,megabus,pytest,pywebpush,requests,smail,sqlalchemy,stomp,yaml diff --git a/Makefile b/Makefile index bb11a9ecbb186d05fbdda4a446f8c7e741ff6d83..72d29c95e7e35825594365511eaccde9df569c8f 100644 --- a/Makefile +++ b/Makefile @@ -56,6 +56,7 @@ docker-build-full: .PHONY: docker-build-env docker-build-env-local: + docker-compose -f docker-compose.local.yml build docker-compose -f docker-compose.local.yml up --remove-orphans .PHONY: docker-build-env-local diff --git a/docker-compose.full.yml b/docker-compose.full.yml index 107cb9e79064820145a26ad93cb636d8c32ea183..56a2996bf55d235e8e640d79e9d3677eedab2215 100644 --- a/docker-compose.full.yml +++ b/docker-compose.full.yml @@ -251,9 +251,25 @@ services: timeout: 5s retries: 5 + etcd: + image: bitnami/etcd:3.5.1 + volumes: + - etcd-data:/bitnami/etcd/data:rw + entrypoint: /opt/bitnami/etcd/bin/etcd + command: + - '--listen-client-urls=http://0.0.0.0:2379' + - '--advertise-client-urls=http://etcd:2379' + - '--data-dir=/bitnami/etcd/data' + networks: + - default + ports: + - 2379:2379 + - 2380:2380 + volumes: pgsql-data: name: pgsql-data - + etcd-data: + name: etcd-data networks: default: diff --git a/docker-compose.local.yml b/docker-compose.local.yml index 923b7b912f5a315956f66b3eaf22b81ce403b96e..35bf64c3eabc358dff6f541cb0567da8b2d7f093 100644 --- a/docker-compose.local.yml +++ b/docker-compose.local.yml @@ -41,6 +41,7 @@ services: extra_hosts: - "activemq:127.0.0.1" - "pg_db:127.0.0.1" + - "etcd:127.0.0.1" networks: shared: diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 3fa813fb1778d9c8e45af93caf15d8147af4b9d3..bfe142d985cff7436f680774a5b404c2581fd681 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -78,9 +78,26 @@ services: timeout: 5s retries: 5 + etcd: + image: bitnami/etcd:3.5.1 + volumes: + - etcd-data:/bitnami/etcd/data:rw + entrypoint: /opt/bitnami/etcd/bin/etcd + command: + - '--listen-client-urls=http://0.0.0.0:2379' + - '--advertise-client-urls=http://etcd:2379' + - '--data-dir=/bitnami/etcd/data' + networks: + - default + ports: + - 2379:2379 + - 2380:2380 + volumes: pgsql-data: name: pgsql-data + etcd-data: + name: etcd-data networks: default: diff --git a/docker-compose.yml b/docker-compose.yml index b0f24b3c2e5f2bf22b90eb97ce702d51307012d0..d82d2298f278a8d2bace94744df243d075f78db7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -82,9 +82,25 @@ services: timeout: 5s retries: 5 + etcd: + image: bitnami/etcd:3.5.1 + volumes: + - etcd-data:/bitnami/etcd/data:rw + entrypoint: /opt/bitnami/etcd/bin/etcd + command: + - '--listen-client-urls=http://0.0.0.0:2379' + - '--advertise-client-urls=http://etcd:2379' + - '--data-dir=/bitnami/etcd/data' + networks: + - default + ports: + - 2379:2379 + - 2380:2380 + volumes: pgsql-data: name: pgsql-data - + etcd-data: + name: etcd-data networks: default: diff --git a/notifications_consumer/auditing.py b/notifications_consumer/auditing.py new file mode 100644 index 0000000000000000000000000000000000000000..7b39f96e5f18d1723b3428f46367c1a6af5c23e7 --- /dev/null +++ b/notifications_consumer/auditing.py @@ -0,0 +1,52 @@ +"""Auditing definition.""" +import json +import logging +import uuid +from datetime import datetime + +import etcd3 + +from notifications_consumer.config import Config + + +client = etcd3.client( + host=Config.ETCD_HOST, port=Config.ETCD_PORT, user=Config.ETCD_USER, password=Config.ETCD_PASSWORD +) + + +def audit_notification(notification_id, value, key=None, user_id=None): + """Put audit notification information into audit DB.""" + if Config.AUDITING is False: + logging.info("Audit disabled") + return + + if not key: + key = uuid.uuid4() + try: + client.put( + ( + f"/notifications/{notification_id}/{Config.AUDIT_ID}" + f"/{'target_users/' + user_id + '/' if user_id else ''}{key}" + ), + json.dumps({"date": datetime.now().strftime("%d/%m/%Y %H:%M:%S"), **value}), + ) + except Exception: + logging.exception("Error auditing to etcd3:") + + +def get_audit_notification(notification_id, key, user_id=None): + """Get audit notification information from audit DB.""" + if Config.AUDITING is False: + logging.info("Audit disabled") + return None + + try: + return client.get( + ( + f"/notifications/{notification_id}/{Config.AUDIT_ID}" + f"/{'target_users/' + user_id + '/' if user_id else ''}{key}" + ) + )[0] + except Exception: + logging.exception("Error getting from etcd") + return None diff --git a/notifications_consumer/config.py b/notifications_consumer/config.py index b07196a34caaac18ef5222d7527773aff75182ad..4b442959f9274e5407074bc950874be2a4d46e08 100644 --- a/notifications_consumer/config.py +++ b/notifications_consumer/config.py @@ -134,6 +134,14 @@ class Config: # Cache CACHE_TTL = int(os.getenv("CACHE_TTL", 86400)) + # Etcd auditing + ETCD_HOST = os.getenv("ETCD_HOST", "localhost") + ETCD_PORT = os.getenv("ETCD_PORT", 2379) + AUDITING = os.getenv("AUDITING", False) + ETCD_USER = os.getenv("ETCD_USER", None) + ETCD_PASSWORD = os.getenv("ETCD_PASSWORD", None) + AUDIT_ID = os.getenv("AUDIT_SERVICE_NAME", "consumer") + class DevelopmentConfig(Config): """Development configuration overrides.""" diff --git a/notifications_consumer/processors/email/processor.py b/notifications_consumer/processors/email/processor.py index 8827a1130115351874eeb0db41aefc7d715f932b..664d7b3754f3146b54b2ea853714b28c95ac7bed 100644 --- a/notifications_consumer/processors/email/processor.py +++ b/notifications_consumer/processors/email/processor.py @@ -3,6 +3,7 @@ import logging from datetime import datetime from typing import Dict +from notifications_consumer.auditing import audit_notification, get_audit_notification from notifications_consumer.config import ENV_DEV, Config from notifications_consumer.email_whitelist import get_email_whitelist from notifications_consumer.processors.email.utils import create_email, send_emails @@ -76,7 +77,15 @@ class EmailProcessor(Processor): context, ) - return send_emails([email]) + if get_audit_notification(kwargs["notification_id"], self.__id, recipient_email): + logging.warning( + "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], recipient_email + ) + return + + ret = send_emails([email]) + audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, recipient_email) + return ret def read_message(self, message: Dict): """Read the message.""" diff --git a/notifications_consumer/processors/email_feed/processor.py b/notifications_consumer/processors/email_feed/processor.py index a3bd0e46e48418664cfb0ba7dbe1938b626c0c40..47060f8e071fdafe51d3609e2e4b30f3560cd8bf 100644 --- a/notifications_consumer/processors/email_feed/processor.py +++ b/notifications_consumer/processors/email_feed/processor.py @@ -4,6 +4,7 @@ import logging from datetime import datetime from typing import Dict +from notifications_consumer.auditing import audit_notification, get_audit_notification from notifications_consumer.config import ENV_DEV, Config from notifications_consumer.email_whitelist import get_email_whitelist from notifications_consumer.processors.email.utils import create_email, send_emails @@ -74,7 +75,15 @@ class EmailFeedProcessor(Processor): context, ) - return send_emails([email]) + if get_audit_notification(kwargs["notification_id"], self.__id, recipient_email): + logging.warning( + "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], recipient_email + ) + return + + ret = send_emails([email]) + audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, recipient_email) + return ret def read_message(self, message: Dict): """Read the message.""" diff --git a/notifications_consumer/processors/mattermost/processor.py b/notifications_consumer/processors/mattermost/processor.py index 182e3aed697b7d4163f44f01a99ff945dcaf4357..ad6b9895e69fdeb5089d536120dd8554659d106b 100644 --- a/notifications_consumer/processors/mattermost/processor.py +++ b/notifications_consumer/processors/mattermost/processor.py @@ -2,9 +2,10 @@ import logging from typing import Dict +from notifications_consumer.auditing import audit_notification, get_audit_notification +from notifications_consumer.processors.mattermost.utils import create_message, send_message from notifications_consumer.processors.processor import Processor from notifications_consumer.processors.registry import ProcessorRegistry -from notifications_consumer.processors.mattermost.utils import create_message, send_message @ProcessorRegistry.register @@ -36,7 +37,15 @@ class MattermostProcessor(Processor): kwargs["img_url"], ) - return send_message(wpmessage, device_token) + if get_audit_notification(kwargs["notification_id"], self.__id, device_token): + logging.warning( + "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token + ) + return + + ret = send_message(wpmessage, device_token) + audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token) + return ret def read_message(self, message: Dict): """Read the message.""" diff --git a/notifications_consumer/processors/safaripush/processor.py b/notifications_consumer/processors/safaripush/processor.py index 1b7031001a588eba395995d3b7dd64e64695bfb7..d0495f4056963ea8f2a25967986337e088810ed0 100644 --- a/notifications_consumer/processors/safaripush/processor.py +++ b/notifications_consumer/processors/safaripush/processor.py @@ -2,6 +2,7 @@ import logging from typing import Dict +from notifications_consumer.auditing import audit_notification, get_audit_notification from notifications_consumer.processors.processor import Processor from notifications_consumer.processors.registry import ProcessorRegistry from notifications_consumer.processors.safaripush.utils import create_message, send_message @@ -36,7 +37,15 @@ class SafariPushProcessor(Processor): kwargs["img_url"], ) - return send_message(spmessage, device_token) + if get_audit_notification(kwargs["notification_id"], self.__id, device_token): + logging.warning( + "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token + ) + return + + ret = send_message(spmessage, device_token) + audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token) + return ret def read_message(self, message: Dict): """Read the message.""" diff --git a/notifications_consumer/processors/webpush/processor.py b/notifications_consumer/processors/webpush/processor.py index 94fef94f9e2470b311134f22037293364485f45c..506563770a4f395fe61f78dc27e779799a10ab1b 100644 --- a/notifications_consumer/processors/webpush/processor.py +++ b/notifications_consumer/processors/webpush/processor.py @@ -2,6 +2,7 @@ import logging from typing import Dict +from notifications_consumer.auditing import audit_notification, get_audit_notification from notifications_consumer.processors.processor import Processor from notifications_consumer.processors.registry import ProcessorRegistry from notifications_consumer.processors.webpush.utils import create_message, send_message @@ -37,7 +38,15 @@ class WebPushProcessor(Processor): kwargs["img_url"], ) - return send_message(wpmessage, device_token, encoding) + if get_audit_notification(kwargs["notification_id"], self.__id, device_token): + logging.warning( + "%s is already sent to %s according ot etcd, skipping", kwargs["notification_id"], device_token + ) + return + + ret = send_message(wpmessage, device_token, encoding) + audit_notification(kwargs["notification_id"], {"event": "Sent"}, self.__id, device_token) + return ret def read_message(self, message: Dict): """Read the message.""" diff --git a/poetry.lock b/poetry.lock index 64031fe06d412d353d2a3c251e5cd2fe0224a3cd..b15c9b14d2312d4527cd138f56f163fb8e8158ac 100644 --- a/poetry.lock +++ b/poetry.lock @@ -162,6 +162,20 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "etcd3" +version = "0.12.0" +description = "Python client for the etcd3 API" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +grpcio = ">=1.27.1" +protobuf = ">=3.6.1" +six = ">=1.12.0" +tenacity = ">=6.1.0" + [[package]] name = "filelock" version = "3.0.12" @@ -204,6 +218,20 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "grpcio" +version = "1.43.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +six = ">=1.5.2" + +[package.extras] +protobuf = ["grpcio-tools (>=1.43.0)"] + [[package]] name = "h2" version = "2.6.2" @@ -515,6 +543,14 @@ python-versions = ">=3.6.1" [package.dependencies] wcwidth = "*" +[[package]] +name = "protobuf" +version = "3.19.1" +description = "Protocol Buffers" +category = "main" +optional = false +python-versions = ">=3.5" + [[package]] name = "psycopg2-binary" version = "2.8.6" @@ -775,6 +811,17 @@ python-versions = ">=3.6,<4.0" [package.dependencies] docopt = ">=0.6.2,<0.7.0" +[[package]] +name = "tenacity" +version = "8.0.1" +description = "Retry code until it succeeds" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.extras] +doc = ["reno", "sphinx", "tornado (>=4.5)"] + [[package]] name = "toml" version = "0.10.2" @@ -879,7 +926,7 @@ testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pyt [metadata] lock-version = "1.1" python-versions = "^3.6.1" -content-hash = "27d4bfdf8b68e1142fe5433e499d2b540e4b484b68ed7e0ee20fb92c7464b3d3" +content-hash = "05231d3b1abbd4147ef58dca014d751df4ec651861a1d10c618b8dbbf551fb46" [metadata.files] apns2 = [ @@ -1008,6 +1055,9 @@ distlib = [ docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] +etcd3 = [ + {file = "etcd3-0.12.0.tar.gz", hash = "sha256:89a704cb389bf0a010a1fa050ce19342d23bf6371ebda1c21cfe8ff3ed488726"}, +] filelock = [ {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"}, {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"}, @@ -1023,6 +1073,52 @@ flake8-docstrings = [ flake8-logging-format = [ {file = "flake8-logging-format-0.6.0.tar.gz", hash = "sha256:ca5f2b7fc31c3474a0aa77d227e022890f641a025f0ba664418797d979a779f8"}, ] +grpcio = [ + {file = "grpcio-1.43.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:a4e786a8ee8b30b25d70ee52cda6d1dbba2a8ca2f1208d8e20ed8280774f15c8"}, + {file = "grpcio-1.43.0-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:af9c3742f6c13575c0d4147a8454da0ff5308c4d9469462ff18402c6416942fe"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:fdac966699707b5554b815acc272d81e619dd0999f187cd52a61aef075f870ee"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e463b4aa0a6b31cf2e57c4abc1a1b53531a18a570baeed39d8d7b65deb16b7e"}, + {file = "grpcio-1.43.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f11d05402e0ac3a284443d8a432d3dfc76a6bd3f7b5858cddd75617af2d7bd9b"}, + {file = "grpcio-1.43.0-cp310-cp310-win32.whl", hash = "sha256:c36f418c925a41fccada8f7ae9a3d3e227bfa837ddbfddd3d8b0ac252d12dda9"}, + {file = "grpcio-1.43.0-cp310-cp310-win_amd64.whl", hash = "sha256:772b943f34374744f70236bbbe0afe413ed80f9ae6303503f85e2b421d4bca92"}, + {file = "grpcio-1.43.0-cp36-cp36m-linux_armv7l.whl", hash = "sha256:cbc9b83211d905859dcf234ad39d7193ff0f05bfc3269c364fb0d114ee71de59"}, + {file = "grpcio-1.43.0-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:fb7229fa2a201a0c377ff3283174ec966da8f9fd7ffcc9a92f162d2e7fc9025b"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:17b75f220ee6923338155b4fcef4c38802b9a57bc57d112c9599a13a03e99f8d"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:6620a5b751b099b3b25553cfc03dfcd873cda06f9bb2ff7e9948ac7090e20f05"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_aarch64.whl", hash = "sha256:1898f999383baac5fcdbdef8ea5b1ef204f38dc211014eb6977ac6e55944d738"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47b6821238d8978014d23b1132713dac6c2d72cbb561cf257608b1673894f90a"}, + {file = "grpcio-1.43.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80398e9fb598060fa41050d1220f5a2440fe74ff082c36dda41ac3215ebb5ddd"}, + {file = "grpcio-1.43.0-cp36-cp36m-win32.whl", hash = "sha256:0110310eff07bb69782f53b7a947490268c4645de559034c43c0a635612e250f"}, + {file = "grpcio-1.43.0-cp36-cp36m-win_amd64.whl", hash = "sha256:45401d00f2ee46bde75618bf33e9df960daa7980e6e0e7328047191918c98504"}, + {file = "grpcio-1.43.0-cp37-cp37m-linux_armv7l.whl", hash = "sha256:af78ac55933811e6a25141336b1f2d5e0659c2f568d44d20539b273792563ca7"}, + {file = "grpcio-1.43.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:8b2b9dc4d7897566723b77422e11c009a0ebd397966b165b21b89a62891a9fdf"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:77ef653f966934b3bfdd00e4f2064b68880eb40cf09b0b99edfa5ee22a44f559"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e95b5d62ec26d0cd0b90c202d73e7cb927c369c3358e027225239a4e354967dc"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_aarch64.whl", hash = "sha256:04239e8f71db832c26bbbedb4537b37550a39d77681d748ab4678e58dd6455d6"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b4a7152187a49767a47d1413edde2304c96f41f7bc92cc512e230dfd0fba095"}, + {file = "grpcio-1.43.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8cc936a29c65ab39714e1ba67a694c41218f98b6e2a64efb83f04d9abc4386b"}, + {file = "grpcio-1.43.0-cp37-cp37m-win32.whl", hash = "sha256:577e024c8dd5f27cd98ba850bc4e890f07d4b5942e5bc059a3d88843a2f48f66"}, + {file = "grpcio-1.43.0-cp37-cp37m-win_amd64.whl", hash = "sha256:138f57e3445d4a48d9a8a5af1538fdaafaa50a0a3c243f281d8df0edf221dc02"}, + {file = "grpcio-1.43.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:08cf25f2936629db062aeddbb594bd76b3383ab0ede75ef0461a3b0bc3a2c150"}, + {file = "grpcio-1.43.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:01f4b887ed703fe82ebe613e1d2dadea517891725e17e7a6134dcd00352bd28c"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0aa8285f284338eb68962fe1a830291db06f366ea12f213399b520c062b01f65"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:0edbfeb6729aa9da33ce7e28fb7703b3754934115454ae45e8cc1db601756fd3"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:c354017819201053d65212befd1dcb65c2d91b704d8977e696bae79c47cd2f82"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50cfb7e1067ee5e00b8ab100a6b7ea322d37ec6672c0455106520b5891c4b5f5"}, + {file = "grpcio-1.43.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57f1aeb65ed17dfb2f6cd717cc109910fe395133af7257a9c729c0b9604eac10"}, + {file = "grpcio-1.43.0-cp38-cp38-win32.whl", hash = "sha256:fa26a8bbb3fe57845acb1329ff700d5c7eaf06414c3e15f4cb8923f3a466ef64"}, + {file = "grpcio-1.43.0-cp38-cp38-win_amd64.whl", hash = "sha256:ade8b79a6b6aea68adb9d4bfeba5d647667d842202c5d8f3ba37ac1dc8e5c09c"}, + {file = "grpcio-1.43.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:124e718faf96fe44c98b05f3f475076be8b5198bb4c52a13208acf88a8548ba9"}, + {file = "grpcio-1.43.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2f96142d0abc91290a63ba203f01649e498302b1b6007c67bad17f823ecde0cf"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:31e6e489ccd8f08884b9349a39610982df48535881ec34f05a11c6e6b6ebf9d0"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:0e731f660e1e68238f56f4ce11156f02fd06dc58bc7834778d42c0081d4ef5ad"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:1f16725a320460435a8a5339d8b06c4e00d307ab5ad56746af2e22b5f9c50932"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4b4543e13acb4806917d883d0f70f21ba93b29672ea81f4aaba14821aaf9bb0"}, + {file = "grpcio-1.43.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:594aaa0469f4fca7773e80d8c27bf1298e7bbce5f6da0f084b07489a708f16ab"}, + {file = "grpcio-1.43.0-cp39-cp39-win32.whl", hash = "sha256:5449ae564349e7a738b8c38583c0aad954b0d5d1dd3cea68953bfc32eaee11e3"}, + {file = "grpcio-1.43.0-cp39-cp39-win_amd64.whl", hash = "sha256:bdf41550815a831384d21a498b20597417fd31bd084deb17d31ceb39ad9acc79"}, + {file = "grpcio-1.43.0.tar.gz", hash = "sha256:735d9a437c262ab039d02defddcb9f8f545d7009ae61c0114e19dda3843febe5"}, +] h2 = [ {file = "h2-2.6.2-py2.py3-none-any.whl", hash = "sha256:93cbd1013a2218539af05cdf9fc37b786655b93bbc94f5296b7dabd1c5cadf41"}, {file = "h2-2.6.2.tar.gz", hash = "sha256:af35878673c83a44afbc12b13ac91a489da2819b5dc1e11768f3c2406f740fe9"}, @@ -1157,6 +1253,32 @@ prompt-toolkit = [ {file = "prompt_toolkit-3.0.19-py3-none-any.whl", hash = "sha256:7089d8d2938043508aa9420ec18ce0922885304cddae87fb96eebca942299f88"}, {file = "prompt_toolkit-3.0.19.tar.gz", hash = "sha256:08360ee3a3148bdb5163621709ee322ec34fc4375099afa4bbf751e9b7b7fa4f"}, ] +protobuf = [ + {file = "protobuf-3.19.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d80f80eb175bf5f1169139c2e0c5ada98b1c098e2b3c3736667f28cbbea39fc8"}, + {file = "protobuf-3.19.1-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a529e7df52204565bcd33738a7a5f288f3d2d37d86caa5d78c458fa5fabbd54d"}, + {file = "protobuf-3.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28ccea56d4dc38d35cd70c43c2da2f40ac0be0a355ef882242e8586c6d66666f"}, + {file = "protobuf-3.19.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:8b30a7de128c46b5ecb343917d9fa737612a6e8280f440874e5cc2ba0d79b8f6"}, + {file = "protobuf-3.19.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5935c8ce02e3d89c7900140a8a42b35bc037ec07a6aeb61cc108be8d3c9438a6"}, + {file = "protobuf-3.19.1-cp36-cp36m-win32.whl", hash = "sha256:74f33edeb4f3b7ed13d567881da8e5a92a72b36495d57d696c2ea1ae0cfee80c"}, + {file = "protobuf-3.19.1-cp36-cp36m-win_amd64.whl", hash = "sha256:038daf4fa38a7e818dd61f51f22588d61755160a98db087a046f80d66b855942"}, + {file = "protobuf-3.19.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e51561d72efd5bd5c91490af1f13e32bcba8dab4643761eb7de3ce18e64a853"}, + {file = "protobuf-3.19.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:6e8ea9173403219239cdfd8d946ed101f2ab6ecc025b0fda0c6c713c35c9981d"}, + {file = "protobuf-3.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db3532d9f7a6ebbe2392041350437953b6d7a792de10e629c1e4f5a6b1fe1ac6"}, + {file = "protobuf-3.19.1-cp37-cp37m-win32.whl", hash = "sha256:615b426a177780ce381ecd212edc1e0f70db8557ed72560b82096bd36b01bc04"}, + {file = "protobuf-3.19.1-cp37-cp37m-win_amd64.whl", hash = "sha256:d8919368410110633717c406ab5c97e8df5ce93020cfcf3012834f28b1fab1ea"}, + {file = "protobuf-3.19.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:71b0250b0cfb738442d60cab68abc166de43411f2a4f791d31378590bfb71bd7"}, + {file = "protobuf-3.19.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:3cd0458870ea7d1c58e948ac8078f6ba8a7ecc44a57e03032ed066c5bb318089"}, + {file = "protobuf-3.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:655264ed0d0efe47a523e2255fc1106a22f6faab7cc46cfe99b5bae085c2a13e"}, + {file = "protobuf-3.19.1-cp38-cp38-win32.whl", hash = "sha256:b691d996c6d0984947c4cf8b7ae2fe372d99b32821d0584f0b90277aa36982d3"}, + {file = "protobuf-3.19.1-cp38-cp38-win_amd64.whl", hash = "sha256:e7e8d2c20921f8da0dea277dfefc6abac05903ceac8e72839b2da519db69206b"}, + {file = "protobuf-3.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fd390367fc211cc0ffcf3a9e149dfeca78fecc62adb911371db0cec5c8b7472d"}, + {file = "protobuf-3.19.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d83e1ef8cb74009bebee3e61cc84b1c9cd04935b72bca0cbc83217d140424995"}, + {file = "protobuf-3.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36d90676d6f426718463fe382ec6274909337ca6319d375eebd2044e6c6ac560"}, + {file = "protobuf-3.19.1-cp39-cp39-win32.whl", hash = "sha256:e7b24c11df36ee8e0c085e5b0dc560289e4b58804746fb487287dda51410f1e2"}, + {file = "protobuf-3.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:77d2fadcf369b3f22859ab25bd12bb8e98fb11e05d9ff9b7cd45b711c719c002"}, + {file = "protobuf-3.19.1-py2.py3-none-any.whl", hash = "sha256:e813b1c9006b6399308e917ac5d298f345d95bb31f46f02b60cd92970a9afa17"}, + {file = "protobuf-3.19.1.tar.gz", hash = "sha256:62a8e4baa9cb9e064eb62d1002eca820857ab2138440cb4b3ea4243830f94ca7"}, +] psycopg2-binary = [ {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, {file = "psycopg2_binary-2.8.6-cp27-cp27m-macosx_10_6_intel.macosx_10_9_intel.macosx_10_9_x86_64.macosx_10_10_intel.macosx_10_10_x86_64.whl", hash = "sha256:d14b140a4439d816e3b1229a4a525df917d6ea22a0771a2a78332273fd9528a4"}, @@ -1338,6 +1460,10 @@ sqlalchemy = [ {file = "stomp.py-6.1.0-py3-none-any.whl", hash = "sha256:8a1ed68cd8b12f41ba56a8dfeff995e7866d1d47ed7f53aaa78da3bea44696b8"}, {file = "stomp.py-6.1.0.tar.gz", hash = "sha256:1f6c7e1e5089b1d8a75161e66533cabb9895de5121cc3900cb7e12c41c1bda18"}, ] +tenacity = [ + {file = "tenacity-8.0.1-py3-none-any.whl", hash = "sha256:f78f4ea81b0fabc06728c11dc2a8c01277bfc5181b321a4770471902e3eb844a"}, + {file = "tenacity-8.0.1.tar.gz", hash = "sha256:43242a20e3e73291a28bcbcacfd6e000b02d3857a9a9fff56b297a27afdc932f"}, +] toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, diff --git a/pyproject.toml b/pyproject.toml index 889db5cd2b362a0b88cd45983ec0deacfd17c55f..6fd7211c6df986e74e0326d62f0921498c231af0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ pycrypto = "^2.6.1" python-smail = "^0.9.0" mattermostdriver = "^7.3.1" cachetools = "^4.2.4" +etcd3 = "^0.12.0" [tool.poetry.dev-dependencies] pre-commit = "~2.9.2"