Modernize whole stack

master
Lauri Võsandi 7 months ago committed by Lauri Võsandi
parent 1150fc2998
commit 9e7c49884a
  1. 3
      .flake8
  2. 6
      .gitlint
  3. 11
      .pre-commit-config.yaml
  4. 2
      Dockerfile
  5. 20
      Jenkinsfile
  6. 19
      README.md
  7. 2
      const.py
  8. 23
      docker-compose.dev.yml
  9. 40
      docker-compose.yml
  10. 77
      doorboy.py
  11. 9
      mongo-init.sh
  12. 6
      overnode.yml

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

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

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

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

20
Jenkinsfile vendored

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

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

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

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

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

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

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

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