Hacking towards the second.py functionality

This commit is contained in:
Luke Murphy 2020-07-05 02:41:06 +02:00
parent b914d1ae73
commit 4adac0ec20
No known key found for this signature in database
GPG Key ID: 5E2EF5A63E3718CC
14 changed files with 217 additions and 23 deletions

View File

@ -1,8 +1,9 @@
export CELERY_BROKER_URL=redis://localhost:6379
export CELERY_RESULT_BACKEND=redis://localhost:6379
export FLASK_ENV=development
export FLASK_APP=wsgi:app
export FLASK_ENV=development
export REDIS_HOST=localhost
export REDIS_PORT=6379
export REDIS_SESSION_DB=0
export SECRET_KEY="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
export SERVER_PORT=0.0.0.0:8000

View File

@ -57,9 +57,9 @@ def configure_celery(app):
def configure_views(app):
"""Configure API resource views."""
from magic_app.views import home
from magic_app.views import apps
app.register_blueprint(home)
app.register_blueprint(apps)
def configure_logging(app):

View File

@ -1,6 +1,7 @@
"""The Application settings."""
from os import environ, pardir
from os.path import abspath, dirname, join
from pathlib import Path
class Base:
@ -8,10 +9,10 @@ class Base:
DEBUG = False
JSON_AS_ASCII = False
SECRET_KEY = environ["SECRET_KEY"]
APP_DIR = abspath(dirname(__file__))
PROJECT_ROOT = abspath(join(APP_DIR, pardir))
SWAGGER_DIR = abspath(join(PROJECT_ROOT, "swagger_docs"))
REDIS_HOST = environ["REDIS_HOST"]
REDIS_PORT = environ["REDIS_PORT"]
@ -25,9 +26,9 @@ class Development(Base):
"""The Development configuration."""
ENV = "development"
CELERY_ALWAYS_EAGER = True
DEBUG = True
DATA_DIR = Path(Base.PROJECT_ROOT) / "data"
class Testing(Base):
@ -41,6 +42,7 @@ class Production(Base):
"""The production configuration."""
ENV = "production"
DATA_DIR = "/data"
CONFIG = {

1
magic_app/docker.py Normal file
View File

@ -0,0 +1 @@
"""Docker interaction module"""

50
magic_app/forms.py Normal file
View File

@ -0,0 +1,50 @@
"""Forms for app installation."""
from os import environ
from flask import request
from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField
from wtforms.validators import DataRequired, Length
class GiteaInstallForm(FlaskForm):
"""Gitea installation form."""
# "simple"
app_name = StringField("Application name", default="Git with a cup of tea")
domain = StringField("Domain name", validators=[DataRequired()],)
stack_name = StringField("Stack name", default="magic-app-gitea")
# "advanced"
db_host = StringField("Database host", default="mariadb:3306")
db_name = StringField("Database name", default="gitea")
db_type = StringField("Database type", default="mysql")
db_user = StringField("Database user", default="mysql")
ssh_host_port = StringField("SSH host port", default="2225")
# secrets
db_passwd = PasswordField(
"Database password", validators=[DataRequired(), Length(min=32)],
)
db_root_passwd = PasswordField(
"Database root password", validators=[DataRequired(), Length(min=32)],
)
internal_token = PasswordField(
"Internal secret token", validators=[DataRequired(), Length(min=105)],
)
jwt_secret = PasswordField(
"JWT secret", validators=[DataRequired(), Length(min=43)],
)
secret_key = PasswordField(
"Secret key", validators=[DataRequired(), Length(min=64)],
)
def form_to_env(app_name, request_form):
"""Load form data into a environment."""
env = environ.copy()
for key in request_form.keys():
env[key.upper()] = request.form[key]
return env

View File

@ -1,7 +1,14 @@
"""Celery tasks module."""
from magic_app.app import celery
from magic_app.forms import form_to_env
from magic_app.templates import clone_app_template, create_data_dir
@celery.task
def hello_world():
print("Hello, World")
def install_app(app_name: str, form_data) -> None:
"""Install an application."""
create_data_dir()
clone_app_template(app_name)
# Note(decentral1se): this is where I left off...
env = form_to_env() # noqa

29
magic_app/templates.py Normal file
View File

@ -0,0 +1,29 @@
"""Compose template handling."""
from os import mkdir
from os.path import exists
from shlex import split
from shutil import rmtree
from subprocess import run
from flask import current_app
APP_TEMPLATES = {
"gitea": "https://git.autonomic.zone/compose-stacks/gitea",
}
def create_data_dir() -> None:
"""Create data directory for compose templates."""
try:
mkdir(current_app.config["DATA_DIR"])
except FileExistsError:
pass
def clone_app_template(app_name: str) -> None:
"""Clone an application template repository."""
clone_path = current_app.config["DATA_DIR"] / app_name
clone_url = APP_TEMPLATES[app_name]
if exists(clone_path):
rmtree(clone_path)
run(split(f"git clone {clone_url} {clone_path}"))

View File

@ -0,0 +1,19 @@
{% from "macros.html" import with_errors %}
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<title>Install {{ app_name | capitalize }}</title>
</head>
<body>
<p>Install {{ app_name | capitalize }}</p>
<form method="POST" action="{{ url_for("apps.install", app_name=app_name) }}">
{% for field in form %}
{{ field.label() }} {{ with_errors(field, style='font-weight: bold') }}
{% endfor %}
<input type="submit" value="Install" />
</form>
</body>
</html>
</html>

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<title>Application Listing</title>
</head>
<body>
<ul>
{% for app in apps %}
<li>
<a href="{{ url_for('apps.install', app_name=app) }}">{{ app }}</a>
</li>
{% endfor %}
</ul>
</body>
</html>
</html>

View File

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head>
<title>{{ app_name | capitalize }} Status</title>
<style>
table,
th,
td {
border: 1px solid black;
}
</style>
</head>
<body>
<table>
<tr>
<th>App</th>
<th>Status</th>
</tr>
<tr>
<th>{{ app_name }}</th>
<th>{{ status }}</th>
</tr>
</table>
</body>
</html>
</html>

View File

@ -1,7 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<html>
<head></head>
<body></body>
</html>
</html>

View File

@ -0,0 +1,16 @@
{#
From: https://wtforms.readthedocs.io/en/2.3.x/specific_problems/#rendering-errors
Usage: with_errors(form.field, style='font-weight: bold')
#}
{% macro with_errors(field) %}
<div class="form_field">
{% if field.errors %}
{% set css_class = 'has_error ' + kwargs.pop('class', '') %}
{{ field(class=css_class, **kwargs) }}
<ul class="errors">{% for error in field.errors %}<li>{{ error|e }}</li>{% endfor %}</ul>
{% else %}
{{ field(**kwargs) }}
{% endif %}
</div>
{% endmacro %}

View File

@ -1,10 +1,44 @@
"""View routing."""
from flask import Blueprint
from flask import Blueprint, redirect, render_template, url_for
home = Blueprint("home", __name__)
from magic_app.forms import GiteaInstallForm
apps = Blueprint("apps", __name__)
@home.route("/")
def hello_world():
@apps.route("/")
def listing():
return render_template("app_list.html", apps=["gitea"])
return "Hello, World"
@apps.route("/install/<app_name>", methods=("GET", "POST"))
def install(app_name):
"""Install an application."""
from magic_app.tasks import install_app
if app_name == "gitea":
form = GiteaInstallForm()
if form.validate_on_submit():
install_app.apply_async(args=[app_name])
return redirect(url_for("apps.status", app_name=app_name))
return render_template("app_install.html", app_name=app_name, form=form)
@apps.route("/install-test/<app_name>")
def install_test(app_name):
"""Development aid to quickly test installation logic."""
from magic_app.tasks import install_app
install_app.apply_async(args=[app_name])
return f"<a href='{app_name}'>Try again?</a>"
@apps.route("/status/<app_name>")
def status(app_name):
"""Show status of applications."""
return render_template(
"app_status.html", status="UNKNOWN", app_name=app_name
)

View File

@ -10,10 +10,8 @@
<body>
<p>Install {{ app_name }}</p>
<form method="POST" action="/deploy/{{ app_name }}">
{% for field in form %}
{{ field.label() }}
{{ with_errors(field, style='font-weight: bold') }}
{% endfor %}
{% for field in form %} {{ field.label() }} {{ with_errors(field,
style='font-weight: bold') }} {% endfor %}
<input type="submit" value="Deploy" />
</form>
</body>