This repository has been archived on 2020-09-13. You can view files and clone it, but cannot push or open issues or pull requests.
magic-app/second.py

123 lines
3.8 KiB
Python

"""Less flashy version. More hard-coding."""
from json import dumps, loads
from os import environ
from os.path import exists
from pathlib import Path
from random import choice
from shlex import split
from string import ascii_lowercase
from subprocess import run
from typing import Dict
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from ruamel.yaml import YAML
from wtforms import PasswordField, StringField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.secret_key = b'_5#y2L"F4Q8z\n\xec]/'
yaml = YAML()
APPS = {"gitea": "https://git.autonomic.zone/compose-stacks/gitea"}
DATA_DIR = Path("./data")
def get_secret(n: int) -> str:
"""Generate a ASCII secret that is n chars long."""
return "".join(choice(ascii_lowercase) for _ in range(n))
def clone_app_template(app_name: str) -> None:
"""Git clone an app template repository."""
clone_path = DATA_DIR / app_name
clone_url = APPS[app_name]
run(split(f"git clone {clone_url} {clone_path}"))
def dump_db(db: Dict) -> None:
"""Dump the database."""
with open(DATA_DIR / "db.json", "w") as handle:
handle.write(dumps(db))
def load_db() -> Dict:
"""Load the database."""
db_path = DATA_DIR / "db.json"
if exists(db_path):
with open(db_path, "r") as handle:
return loads(handle.read())
return {}
def stack_deploy(app_name, env):
"""Depoy an application to the swarm."""
compose_yml = DATA_DIR / app_name / "compose.yml"
command = f"docker stack deploy -c {compose_yml} {app_name}"
run(split(command), env=env)
def get_loaded_env(app_name, request_form):
"""Load an environment with install form values for compose.yml injection."""
environment = environ.copy()
for key in request_form.keys():
environment[key.upper()] = request.form[key]
return environment
class GiteaInstallForm(FlaskForm):
"""Gitea installation form."""
app_name = StringField("Application name", default="Git with a cup of tea")
domain = StringField("Domain name", validators=[DataRequired()])
db_host = StringField("Database host", default="mariadb:3306")
db_name = StringField("Database name", default="gitea")
db_passwd = PasswordField("DB password", default=get_secret(n=32))
db_root_passwd = PasswordField("Root DB password", default=get_secret(n=32))
db_type = StringField("Database type", default="mysql")
db_user = StringField("Database user", default="mysql")
internal_token = PasswordField("Internal token", default=get_secret(n=105))
jwt_secret = PasswordField("JWT secret", default=get_secret(n=43))
secret_key = PasswordField("Secret key", default=get_secret(n=64))
ssh_port = StringField("SSH port", default="2222")
@app.route("/")
def index():
"""Home page for app installation possibilities."""
return render_template("second/index.html", apps=[app for app in APPS])
@app.route("/install/<app_name>")
def install(app_name):
"""Installation page for an app."""
if app_name == "gitea":
form = GiteaInstallForm()
return render_template("second/install.html", app_name=app_name, form=form)
@app.route("/deploy/<app_name>", methods=["POST"])
def deploy(app_name):
"""Deployment end-point for an app."""
if app_name == "gitea":
form = GiteaInstallForm(request.form)
if not form.validate():
return render_template("install.html", app_name=app_name, form=form)
environment = get_loaded_env(app_name, request.form)
# Note(decentral1se): how to handle the following?
# configs -> ${STACK_NAME}_app_ini_${APP_INI_VERSION}
# secrets -> ${STACK_NAME}_db_passwd_${DB_PASSWD_VERSION}
# and dump it all to the db.json when we're done here too
stack_deploy(app_name, environment)
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")