From 79fda2a3232b28a55e02884b8fc66380d8bb5fbb Mon Sep 17 00:00:00 2001 From: Vlad Wing Date: Wed, 31 Jul 2019 20:44:29 +0800 Subject: [PATCH 1/2] [ESTABLISH]: Garmin microservice Features: - Docker stack, docker build - Registration - Receiving pings from service (does nothing with them) --- projects/establish/source.docker | 3 + projects/establish/source.stack | 3 + projects/establish/src/.dockerignore | 1 + projects/establish/src/Dockerfile | 10 +++ projects/establish/src/entry.sh | 4 ++ projects/establish/src/garmin/__init__.py | 0 projects/establish/src/garmin/__main__.py | 4 ++ projects/establish/src/garmin/config.py | 13 ++++ projects/establish/src/garmin/db.py | 36 +++++++++++ projects/establish/src/garmin/helpers.py | 11 ++++ projects/establish/src/garmin/tokens.py | 1 + projects/establish/src/garmin/web.py | 74 +++++++++++++++++++++++ projects/establish/src/requirements.txt | 22 +++++++ projects/establish/stack.yml | 34 +++++++++++ 14 files changed, 216 insertions(+) create mode 100644 projects/establish/source.docker create mode 100644 projects/establish/source.stack create mode 100644 projects/establish/src/.dockerignore create mode 100644 projects/establish/src/Dockerfile create mode 100755 projects/establish/src/entry.sh create mode 100644 projects/establish/src/garmin/__init__.py create mode 100644 projects/establish/src/garmin/__main__.py create mode 100644 projects/establish/src/garmin/config.py create mode 100644 projects/establish/src/garmin/db.py create mode 100644 projects/establish/src/garmin/helpers.py create mode 100644 projects/establish/src/garmin/tokens.py create mode 100644 projects/establish/src/garmin/web.py create mode 100644 projects/establish/src/requirements.txt create mode 100644 projects/establish/stack.yml diff --git a/projects/establish/source.docker b/projects/establish/source.docker new file mode 100644 index 00000000..51f09438 --- /dev/null +++ b/projects/establish/source.docker @@ -0,0 +1,3 @@ +DOCKER_FOLDER=src +IMAGE_NAME=beia/establish_garmin +IMAGE_VERSION_FIXED=latest diff --git a/projects/establish/source.stack b/projects/establish/source.stack new file mode 100644 index 00000000..9531ab00 --- /dev/null +++ b/projects/establish/source.stack @@ -0,0 +1,3 @@ +STACK_FILE=stack.yml +STACK_NAME=establish +DEPLOY_ENVS=production diff --git a/projects/establish/src/.dockerignore b/projects/establish/src/.dockerignore new file mode 100644 index 00000000..eeb8a6ec --- /dev/null +++ b/projects/establish/src/.dockerignore @@ -0,0 +1 @@ +**/__pycache__ diff --git a/projects/establish/src/Dockerfile b/projects/establish/src/Dockerfile new file mode 100644 index 00000000..4c075320 --- /dev/null +++ b/projects/establish/src/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.7.2 +WORKDIR /srv/establish +RUN apt-get update && apt-get install libuv1 +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY garmin garmin +COPY entry.sh . +EXPOSE 8080 +CMD [ "./entry.sh" ] diff --git a/projects/establish/src/entry.sh b/projects/establish/src/entry.sh new file mode 100755 index 00000000..3288c059 --- /dev/null +++ b/projects/establish/src/entry.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +cd /srv/establish && + python -m garmin diff --git a/projects/establish/src/garmin/__init__.py b/projects/establish/src/garmin/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/projects/establish/src/garmin/__main__.py b/projects/establish/src/garmin/__main__.py new file mode 100644 index 00000000..7e916861 --- /dev/null +++ b/projects/establish/src/garmin/__main__.py @@ -0,0 +1,4 @@ +from garmin.web import app +from garmin.config import PORT + +app.run(host='0.0.0.0', port=PORT) diff --git a/projects/establish/src/garmin/config.py b/projects/establish/src/garmin/config.py new file mode 100644 index 00000000..9d2ba2cb --- /dev/null +++ b/projects/establish/src/garmin/config.py @@ -0,0 +1,13 @@ +import os.path +from garmin.helpers import env_load + +ACCESS_TOKEN_URL="https://connectapi.garmin.com/oauth-service/oauth/access_token" +BASE_AUTH_URL="https://connect.garmin.com/oauthConfirm" +CALLBACK_URL="https://establish.beia-consult.ro/oauth/callback" +REQUEST_TOKEN_URL="https://connectapi.garmin.com/oauth-service/oauth/request_token" +STATIC_FOLDER = os.path.join(os.path.dirname(__file__), "../static") +TEMPLATES_FOLDER = "../templates" +DB_LOCATION = env_load("DB_LOCATION") +CLIENT_KEY = env_load("CLIENT_KEY") +CLIENT_SECRET = env_load("CLIENT_SECRET") +PORT = env_load('VIRTUAL_PORT', default=8080) diff --git a/projects/establish/src/garmin/db.py b/projects/establish/src/garmin/db.py new file mode 100644 index 00000000..aefc134b --- /dev/null +++ b/projects/establish/src/garmin/db.py @@ -0,0 +1,36 @@ +import ujson as json +from garmin.config import DB_LOCATION + +class Store: + users = [] + + def save(self): + with open(DB_LOCATION, "w") as f: + json.dump(self.users, f) + + def load(self): + try: + with open(DB_LOCATION, "r") as f: + json.load(users, f) + except: + pass + + def get_users(self): + return list(self.users) + + def get_tokens(self): + return [u.token for u in self.users] + + def add_user(self, token, secret): + self.users.append( + { + "token": token, + "secret": secret + } + ) + self.save() + + def find(self, token): + return [u for u in self.users if u["token"] == token][0] + +store = Store() diff --git a/projects/establish/src/garmin/helpers.py b/projects/establish/src/garmin/helpers.py new file mode 100644 index 00000000..27706a3c --- /dev/null +++ b/projects/establish/src/garmin/helpers.py @@ -0,0 +1,11 @@ +import os + +def env_load(name, default=None, strip=True): + value = os.environ.get(name) + if value is not None: + return value.strip() + filename = os.environ.get(name + "__FILE") + if filename is not None: + with open(filename) as f: + return f.read().strip() + return default diff --git a/projects/establish/src/garmin/tokens.py b/projects/establish/src/garmin/tokens.py new file mode 100644 index 00000000..fab892f9 --- /dev/null +++ b/projects/establish/src/garmin/tokens.py @@ -0,0 +1 @@ +tokens = {} diff --git a/projects/establish/src/garmin/web.py b/projects/establish/src/garmin/web.py new file mode 100644 index 00000000..92ff02e8 --- /dev/null +++ b/projects/establish/src/garmin/web.py @@ -0,0 +1,74 @@ +import os +import requests +from requests_oauthlib import OAuth1Session, OAuth1 +from sanic import Sanic +from sanic import response +from sanic.log import logger +from sanic.response import json +from garmin.config import ( + CLIENT_KEY, + CLIENT_SECRET, + STATIC_FOLDER, + CALLBACK_URL, + REQUEST_TOKEN_URL, + BASE_AUTH_URL, + ACCESS_TOKEN_URL + ) +from garmin.db import store +from garmin.tokens import tokens + + +app = Sanic() +store.load() + +app.static('/favicon.ico', os.path.join(STATIC_FOLDER, 'img', 'favicon.ico')) +logger.info("CLIENT_KEY=%s", CLIENT_KEY) +logger.info("CLIENT_SECRET=%s", CLIENT_SECRET) +logger.info("STATIC_FOLDER=%s", STATIC_FOLDER) +logger.info("CALLBACK_URL=%s", CALLBACK_URL) +logger.info("REQUEST_TOKEN_URL=%s", REQUEST_TOKEN_URL) +logger.info("BASE_AUTH_UrL=%s", BASE_AUTH_URL) +logger.info("ACCESS_TOKEN_URL=%s", ACCESS_TOKEN_URL) + +@app.exception(FileNotFoundError) +def ignore_file_not_found(request, exception: FileNotFoundError): + return response.text("File not found: {}".format(exception.path), 404) + + +@app.route('/register') +async def register(request): + oauth = OAuth1Session( + CLIENT_KEY, + client_secret=CLIENT_SECRET, + callback_uri=CALLBACK_URL) + req_token = oauth.fetch_request_token(REQUEST_TOKEN_URL) + logger.info("request token", req_token) + token = req_token["oauth_token"] + secret = req_token["oauth_token_secret"] + tokens[token] = secret + url = oauth.authorization_url(BASE_AUTH_URL) + return response.redirect(url) + + +@app.route('/callback') +@app.route('/oauth/callback') +async def oauth_callback(request): + token = request.args.get('oauth_token') + verifier = request.args.get('oauth_verifier') + if token not in tokens: + return response.text("haha, nice try!") + oauth = OAuth1Session( + CLIENT_KEY, + client_secret=CLIENT_SECRET, + resource_owner_key=token, + resource_owner_secret=tokens[token], + verifier=verifier) + auth_token = oauth.fetch_access_token(ACCESS_TOKEN_URL) + store.add_user(auth_token["oauth_token"], auth_token["oauth_token_secret"]) + return response.text("Cool, registered!") + + +@app.route('/ping/', methods=["GET", "POST"]) +async def ping(request, request_type): + logger.info("Received a request %s %s", request_type, request.body) + return response.text("") diff --git a/projects/establish/src/requirements.txt b/projects/establish/src/requirements.txt new file mode 100644 index 00000000..8e1818bd --- /dev/null +++ b/projects/establish/src/requirements.txt @@ -0,0 +1,22 @@ +aiofiles==0.4.0 +asyncpg==0.18.3 +certifi==2019.6.16 +chardet==3.0.4 +h11==0.8.1 +h2==3.1.0 +hpack==3.0.0 +httpcore==0.3.0 +httptools==0.0.13 +hyperframe==5.2.0 +idna==2.8 +multidict==4.5.2 +oauthlib==3.0.2 +requests==2.22.0 +requests-async==0.5.0 +requests-oauthlib==1.2.0 +rfc3986==1.3.2 +sanic==19.6.2 +ujson==1.35 +urllib3==1.25.3 +uvloop==0.12.2 +websockets==6.0 diff --git a/projects/establish/stack.yml b/projects/establish/stack.yml new file mode 100644 index 00000000..0ab9747c --- /dev/null +++ b/projects/establish/stack.yml @@ -0,0 +1,34 @@ +version: '3.6' + +services: + garmin: + image: beia/establish_garmin + environment: + VIRTUAL_HOST: 'establish.beia.cloud,establish.beia-consult.ro' + VIRTUAL_PORT: 8080 + LETSENCRYPT_HOST: 'establish.beia-consult.ro' + LETSENCRYPT_EMAIL: 'ssl-contact@beia.ro' + CLIENT_KEY__FILE: '/run/secrets/establish_client_key' + CLIENT_SECRET__FILE: '/run/secrets/establish_client_secret' + DB_LOCATION: '/srv/establish/garmin/db/user.json' + networks: + - proxy_net + volumes: + - establish_db:/srv/establish/garmin/db + secrets: + - establish_client_key + - establish_client_secret + +networks: + proxy_net: + external: true + +secrets: + establish_client_key: + external: true + establish_client_secret: + external: true + +volumes: + establish_db: + external: true From 36140e9a31bc2e8ee5f5ee740149af9b88379a2e Mon Sep 17 00:00:00 2001 From: Vlad Wing Date: Wed, 31 Jul 2019 20:46:54 +0800 Subject: [PATCH 2/2] [PLATFORM]: codeowners & update to docker build makefile --- docs/CODEOWNERS | 3 +++ platform/makefiles/Makefile.docker | 5 ++++- projects/establish/.gitignore | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 projects/establish/.gitignore diff --git a/docs/CODEOWNERS b/docs/CODEOWNERS index eea53e31..b15134b0 100644 --- a/docs/CODEOWNERS +++ b/docs/CODEOWNERS @@ -6,4 +6,7 @@ **/dirs @beia/Platform **/source.stack @beia/Platform +/projects/establish @vladwing +/projects/somedi @vladwing + /practice @beia/Junior-reviewers diff --git a/platform/makefiles/Makefile.docker b/platform/makefiles/Makefile.docker index fe7848dd..295c51a6 100644 --- a/platform/makefiles/Makefile.docker +++ b/platform/makefiles/Makefile.docker @@ -5,11 +5,14 @@ include $(THIS_DIR)/common.mak .EXPORT_ALL_VARIABLES: build: prebuild - @$(DOCKER) build -f $(DOCKER_FILE) -t $(IMAGE_NAME):$(IMAGE_VERSION_FIXED) $(DOCKER_FOLDER) + @$(DOCKER) build -f $(DOCKER_FOLDER)/$(DOCKER_FILE) -t $(IMAGE_NAME):$(IMAGE_VERSION_FIXED) $(DOCKER_FOLDER) push: @$(DOCKER) push $(IMAGE_NAME):$(IMAGE_VERSION_FIXED) + +all: build push + include $(DOCKER_SOURCE) .PHONY: $(DOCKER_PREBUILD) prebuild: $(DOCKER_PREBUILD) diff --git a/projects/establish/.gitignore b/projects/establish/.gitignore new file mode 100644 index 00000000..4c49bd78 --- /dev/null +++ b/projects/establish/.gitignore @@ -0,0 +1 @@ +.env