Modernize whole stack

This commit is contained in:
Lauri Võsandi 2022-02-21 22:10:57 +02:00 committed by Lauri Võsandi
parent 1150fc2998
commit 9e7c49884a
12 changed files with 99 additions and 119 deletions

3
.flake8 Normal file
View File

@ -0,0 +1,3 @@
[flake8]
inline-quotes = "
indent-size = 4

6
.gitlint Normal file
View File

@ -0,0 +1,6 @@
[general]
ignore=body-is-missing,T3
ignore-stdin=true
[title-match-regex]
regex=[A-Z]

11
.pre-commit-config.yaml Normal file
View File

@ -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

View File

@ -1,8 +1,6 @@
FROM python:3 FROM python:3
WORKDIR /app WORKDIR /app
COPY requirements.txt ./ COPY requirements.txt ./
RUN pip install -r requirements.txt RUN pip install -r requirements.txt
COPY *.py ./ COPY *.py ./
CMD python doorboy.py CMD python doorboy.py

20
Jenkinsfile vendored
View File

@ -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)
}
}
}

View File

@ -2,17 +2,13 @@
This component serves allowed list of keyfob UID hashes from MongoDB and This component serves allowed list of keyfob UID hashes from MongoDB and
pushes open door commands to door controllers. 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 # Testing endpoints
For manually testing something like this might help: For manually testing something like this might help:
``` ```
source .env curl -f http://127.0.0.1:5000/allowed -H "KEY: 0123456789"
curl -f http://doorboy.infra.k-space.ee:5000/allowed -H "KEY: $DOORBOY_SECRET" | md5sum
``` ```
# Test scenarios # Test scenarios
@ -35,19 +31,8 @@ The easiest is to obtain VM from Proxmox cluster with public IP address.
To run development instance: 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 On kdoorpi override `KDOORPI_API_ALLOWED`, `KDOORPI_API_LONGPOLL` environment variables
to redirect requests to your dev instance. 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
```

View File

@ -1,2 +0,0 @@
import os
MONGO_URI = os.getenv("MONGO_URI", "mongodb://127.0.0.1:27017/kspace_accounting")

View File

@ -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: .

View File

@ -1,17 +1,29 @@
version: '3.7' version: '3.7'
networks:
infra:
external: true
services: services:
app: mongoexpress:
image: kspaceee/doorboy-proxy:latest image: mongo-express
hostname: replica${OVERNODE_ID:-1}.doorboy.infra.k-space.ee network_mode: host
restart: unless-stopped environment:
env_file: .env - ME_CONFIG_MONGODB_ENABLE_ADMIN=true
user: "65534:65534" - ME_CONFIG_MONGODB_SERVER=127.0.0.1
networks: - ME_CONFIG_MONGODB_PORT=27017
infra: - ME_CONFIG_MONGODB_AUTH_DATABASE=admin
ipv4_address: 172.21.57.${OVERNODE_ID:-1} logging:
ipv6_address: 2001:bb8:4008:21:57::${OVERNODE_ID:-1} 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: .

View File

@ -1,35 +1,43 @@
from datetime import datetime from sanic import Sanic
from sanic import Sanic, response from sanic.response import text, json
from sanic.response import text, json, stream
from motor.motor_asyncio import AsyncIOMotorClient from motor.motor_asyncio import AsyncIOMotorClient
import asyncio
import pymongo import pymongo
import os import os
import const
app = Sanic(__name__) app = Sanic(__name__)
DOORBOY_SECRET = os.environ["DOORBOY_SECRET"] 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") @app.listener("before_server_start")
async def setup_db(app, loop): async def setup_db(app, loop):
# TODO: find cleaner way to do this, for more see # TODO: find cleaner way to do this, for more see
# https://github.com/sanic-org/sanic/issues/919 # 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") @app.route("/allowed")
async def view_doorboy_uids(request): 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") return text("how about no")
allowed_names = [] 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_names.append(obj["_id"])
allowed_uids = [] 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: if obj["inventory"].pop("owner").get("foreign_id") in allowed_names:
del obj["_id"] del obj["_id"]
del obj["inventory"] del obj["inventory"]
@ -37,31 +45,30 @@ async def view_doorboy_uids(request):
return json({"allowed_uids": allowed_uids}) return json({"allowed_uids": allowed_uids})
@app.route("/longpoll") @app.route("/longpoll", stream=True)
async def view_longpoll(request): async def view_longpoll(request):
if request.headers.get('KEY') != DOORBOY_SECRET: response = await request.respond(content_type="text/event-stream")
return text("how about no") if request.headers.get("KEY") != DOORBOY_SECRET:
return text("Invalid token")
async def g(response): await response.send("data: response-generator-started\n\n")
await response.write("data: response-generator-started\n\n")
pipeline = [ pipeline = [
{ {
'$match': { "$match": {
'operationType': "insert", "operationType": "insert",
# 'component': 'doorboy',
# 'type': 'open-door'
} }
} }
] ]
try: try:
async with app.db.eventlog.watch(pipeline) as stream: async with app.ctx.db.eventlog.watch(pipeline) as stream:
await response.write("data: watch-stream-opened\n\n") await response.send("data: watch-stream-opened\n\n")
async for event in stream: async for event in stream:
if event["fullDocument"].get("type") == "open-door": if event["fullDocument"].get("type") == "open-door":
await response.write("data: %s\n\n" % event["fullDocument"]["door"]) await response.send("data: %s\n\n" %
event["fullDocument"]["door"])
except pymongo.errors.PyMongoError: except pymongo.errors.PyMongoError:
return return
return stream(g, content_type="text/event-stream")
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)

9
mongo-init.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
mongo <<EOF
rs.initiate({
_id: 'rs0',
members: [
{_id: 0, host: '127.0.0.1:27017'}
]
})
EOF

View File

@ -1,6 +0,0 @@
id: doorboy
version: 3.7
app:
docker-compose.yml: 1, 2, 3