From 9e7c49884ae9d3aa6ffbf0e4ef5fe2d2edeb22c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauri=20V=C3=B5sandi?= Date: Mon, 21 Feb 2022 22:10:57 +0200 Subject: [PATCH] Modernize whole stack --- .flake8 | 3 ++ .gitlint | 6 ++++ .pre-commit-config.yaml | 11 ++++++ Dockerfile | 2 -- Jenkinsfile | 20 ----------- README.md | 19 ++-------- const.py | 2 -- docker-compose.dev.yml | 23 ------------ docker-compose.yml | 40 +++++++++++++-------- doorboy.py | 77 ++++++++++++++++++++++------------------- mongo-init.sh | 9 +++++ overnode.yml | 6 ---- 12 files changed, 99 insertions(+), 119 deletions(-) create mode 100644 .flake8 create mode 100644 .gitlint create mode 100644 .pre-commit-config.yaml delete mode 100644 Jenkinsfile delete mode 100644 const.py delete mode 100644 docker-compose.dev.yml create mode 100644 mongo-init.sh delete mode 100644 overnode.yml diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..8bd7d2b --- /dev/null +++ b/.flake8 @@ -0,0 +1,3 @@ +[flake8] +inline-quotes = " +indent-size = 4 diff --git a/.gitlint b/.gitlint new file mode 100644 index 0000000..9b7930c --- /dev/null +++ b/.gitlint @@ -0,0 +1,6 @@ +[general] +ignore=body-is-missing,T3 +ignore-stdin=true + +[title-match-regex] +regex=[A-Z] diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..a60b2ae --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,11 @@ +repos: +- repo: https://github.com/PyCQA/flake8 + rev: 3.9.2 + hooks: + - id: flake8 + additional_dependencies: [flake8-typing-imports==1.10.0,flake8-quotes==3.2.0] + +- repo: https://github.com/jorisroovers/gitlint + rev: v0.15.1 + hooks: + - id: gitlint diff --git a/Dockerfile b/Dockerfile index f28d41a..40d63c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,6 @@ FROM python:3 WORKDIR /app - COPY requirements.txt ./ RUN pip install -r requirements.txt - COPY *.py ./ CMD python doorboy.py diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 67dd363..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/groovy -image = "doorboy-proxy" -label = "latest" -build = null - -node { - stage("Checkout") { - checkout scm - } - - stage("Build") { - build = docker.build("kspaceee/${image}") - } - - stage("Push") { - docker.withRegistry("https://registry.hub.docker.com", "dockerhub-kspace") { - build.push(label) - } - } -} diff --git a/README.md b/README.md index afa5f6b..ce7316c 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,13 @@ This component serves allowed list of keyfob UID hashes from MongoDB and pushes open door commands to door controllers. -We run three instances `replica{1..3}.doorboy.infra.k-space.ee` for high availability. -To connect from door controllers use round robin record `doorboy.infra.k-space.ee` - # Testing endpoints For manually testing something like this might help: ``` -source .env -curl -f http://doorboy.infra.k-space.ee:5000/allowed -H "KEY: $DOORBOY_SECRET" | md5sum +curl -f http://127.0.0.1:5000/allowed -H "KEY: 0123456789" ``` # Test scenarios @@ -35,19 +31,8 @@ The easiest is to obtain VM from Proxmox cluster with public IP address. To run development instance: ``` -docker-compose -f docker-compose.dev.yml up --build +docker-compose -f docker-compose.yml up --build ``` On kdoorpi override `KDOORPI_API_ALLOWED`, `KDOORPI_API_LONGPOLL` environment variables to redirect requests to your dev instance. - -# Deploying - -Images are built and pushed to Docker Hub by Jenkins. -To deploy in prod adjust `DOORBOY_SECRET`, `MONGO_URI` in `.env` and proceed to launch: - -``` -overnode pull -overnode up -``` - diff --git a/const.py b/const.py deleted file mode 100644 index 24b2f0e..0000000 --- a/const.py +++ /dev/null @@ -1,2 +0,0 @@ -import os -MONGO_URI = os.getenv("MONGO_URI", "mongodb://127.0.0.1:27017/kspace_accounting") diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index 07702be..0000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,23 +0,0 @@ -version: '3.7' - -services: - mongoexpress: - image: mongo-express - network_mode: host - environment: - - ME_CONFIG_MONGODB_ENABLE_ADMIN=true - - ME_CONFIG_MONGODB_SERVER=127.0.0.1 - - ME_CONFIG_MONGODB_PORT=27017 - - ME_CONFIG_MONGODB_AUTH_DATABASE=admin - - mongo: - network_mode: host - image: mongo:latest - - doorboy_proxy: - network_mode: host - environment: - MONGO_URI=mongodb://127.0.0.1:27017/kspace_accounting?replicaSet=kspace-mongo-set - DOORBOY_SECRET=keykeykey - build: - context: . diff --git a/docker-compose.yml b/docker-compose.yml index 2b35d48..bd933a4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,29 @@ version: '3.7' -networks: - infra: - external: true - services: - app: - image: kspaceee/doorboy-proxy:latest - hostname: replica${OVERNODE_ID:-1}.doorboy.infra.k-space.ee - restart: unless-stopped - env_file: .env - user: "65534:65534" - networks: - infra: - ipv4_address: 172.21.57.${OVERNODE_ID:-1} - ipv6_address: 2001:bb8:4008:21:57::${OVERNODE_ID:-1} + mongoexpress: + image: mongo-express + network_mode: host + environment: + - ME_CONFIG_MONGODB_ENABLE_ADMIN=true + - ME_CONFIG_MONGODB_SERVER=127.0.0.1 + - ME_CONFIG_MONGODB_PORT=27017 + - ME_CONFIG_MONGODB_AUTH_DATABASE=admin + logging: + driver: none + + mongo: + network_mode: host + image: mongo:latest + volumes: + - ./mongo-init.sh:/docker-entrypoint-initdb.d/mongo-init.sh:ro + command: mongod --replSet rs0 --bind_ip 127.0.0.1 + logging: + driver: none + + doorboy_proxy: + network_mode: host + environment: + DOORBOY_SECRET: 0123456789 + build: + context: . diff --git a/doorboy.py b/doorboy.py index e1db8be..1be672d 100644 --- a/doorboy.py +++ b/doorboy.py @@ -1,35 +1,43 @@ -from datetime import datetime -from sanic import Sanic, response -from sanic.response import text, json, stream +from sanic import Sanic +from sanic.response import text, json from motor.motor_asyncio import AsyncIOMotorClient -import asyncio import pymongo import os -import const app = Sanic(__name__) DOORBOY_SECRET = os.environ["DOORBOY_SECRET"] +MONGO_URI = os.getenv("MONGO_URI", + "mongodb://127.0.0.1:27017/default?replicaSet=rs0") -assert len(DOORBOY_SECRET) > 10 +assert len(DOORBOY_SECRET) >= 10 @app.listener("before_server_start") async def setup_db(app, loop): # TODO: find cleaner way to do this, for more see # https://github.com/sanic-org/sanic/issues/919 - app.db = AsyncIOMotorClient(const.MONGO_URI).get_default_database() + app.ctx.db = AsyncIOMotorClient(MONGO_URI).get_default_database() @app.route("/allowed") async def view_doorboy_uids(request): - if request.headers.get('KEY') != DOORBOY_SECRET: + if request.headers.get("KEY") != DOORBOY_SECRET: return text("how about no") allowed_names = [] - async for obj in app.db.member.find({"enabled": True}): + async for obj in app.ctx.db.member.find({"enabled": True}): allowed_names.append(obj["_id"]) allowed_uids = [] - async for obj in app.db.inventory.find({"token.uid_hash": {"$exists":True}, "inventory.owner": {"$exists":True}, "token.enabled": {"$exists":True}}, {"inventory.owner": True, "token.uid_hash": True }): + flt = { + "token.uid_hash": {"$exists": True}, + "inventory.owner": {"$exists": True}, + "token.enabled": {"$exists": True} + } + prj = { + "inventory.owner": True, + "token.uid_hash": True + } + async for obj in app.ctx.db.inventory.find(flt, prj): if obj["inventory"].pop("owner").get("foreign_id") in allowed_names: del obj["_id"] del obj["inventory"] @@ -37,31 +45,30 @@ async def view_doorboy_uids(request): return json({"allowed_uids": allowed_uids}) -@app.route("/longpoll") +@app.route("/longpoll", stream=True) async def view_longpoll(request): - if request.headers.get('KEY') != DOORBOY_SECRET: - return text("how about no") + response = await request.respond(content_type="text/event-stream") + if request.headers.get("KEY") != DOORBOY_SECRET: + return text("Invalid token") - async def g(response): - await response.write("data: response-generator-started\n\n") - pipeline = [ - { - '$match': { - 'operationType': "insert", -# 'component': 'doorboy', -# 'type': 'open-door' - } - } - ] - try: - async with app.db.eventlog.watch(pipeline) as stream: - await response.write("data: watch-stream-opened\n\n") - async for event in stream: - if event["fullDocument"].get("type") == "open-door": - await response.write("data: %s\n\n" % event["fullDocument"]["door"]) - except pymongo.errors.PyMongoError: - return - return stream(g, content_type="text/event-stream") + await response.send("data: response-generator-started\n\n") + pipeline = [ + { + "$match": { + "operationType": "insert", + } + } + ] + try: + async with app.ctx.db.eventlog.watch(pipeline) as stream: + await response.send("data: watch-stream-opened\n\n") + async for event in stream: + if event["fullDocument"].get("type") == "open-door": + await response.send("data: %s\n\n" % + event["fullDocument"]["door"]) + except pymongo.errors.PyMongoError: + return -if __name__ == '__main__': - app.run(debug=False, host='0.0.0.0', port=5000) + +if __name__ == "__main__": + app.run(debug=False, host="0.0.0.0", port=5000) diff --git a/mongo-init.sh b/mongo-init.sh new file mode 100644 index 0000000..2643b27 --- /dev/null +++ b/mongo-init.sh @@ -0,0 +1,9 @@ +#!/bin/bash +mongo <