CLI interface for external apps

This commit is contained in:
Mart van Santen 2022-09-24 01:04:29 +08:00
parent 6e427f275f
commit a2019a32d0
5 changed files with 113 additions and 8 deletions

View file

@ -1,7 +1,12 @@
from flask import jsonify from flask import jsonify, current_app
from flask_jwt_extended import jwt_required from flask_jwt_extended import jwt_required
from flask_cors import cross_origin from flask_cors import cross_origin
from sqlalchemy import func
from config import *
from .apps_service import AppsService
from database import db
from areas import api_v1 from areas import api_v1
CONFIG_DATA = [ CONFIG_DATA = [
@ -26,17 +31,24 @@ APP_DATA = {"id": 1, "name": "Nextcloud", "selected": True, "status": "ON for ev
APP_NOT_INSTALLED_STATUS = "Not installed" APP_NOT_INSTALLED_STATUS = "Not installed"
@api_v1.route('/apps', methods=['GET']) @api_v1.route('/apps', methods=['GET'])
@jwt_required() @jwt_required()
@cross_origin() @cross_origin()
def get_apps(): def get_apps():
return jsonify(APPS_DATA) apps = AppsService.get_all_apps()
for obj in apps:
current_app.logger.info(obj['slug'])
current_app.logger.info(str(obj))
return jsonify(apps)
@api_v1.route('/apps/<string:slug>', methods=['GET']) @api_v1.route('/apps/<string:slug>', methods=['GET'])
@jwt_required() #@jwt_required()
def get_app(slug): def get_app(slug):
return jsonify(APPS_DATA[0])
app = AppsService.get_app(slug)
return jsonify(app)
@api_v1.route('/apps', methods=['POST']) @api_v1.route('/apps', methods=['POST'])

View file

@ -4,7 +4,13 @@ class AppsService:
@staticmethod @staticmethod
def get_all_apps(): def get_all_apps():
apps = App.query.all() apps = App.query.all()
return [{"id": app.id, "name": app.name, "slug": app.slug} for app in apps] return [{"id": app.id, "name": app.name, "slug": app.slug, "external": app.external, "url": app.get_url(), "status": app.get_status()} for app in apps]
@staticmethod
def get_app(slug):
app = App.query.filter_by(slug=slug).first()
return {"id": app.id, "name": app.name, "slug": app.slug, "external": app.external, "url": app.get_url(), "status": app.get_status()}
@staticmethod @staticmethod
def get_app_roles(): def get_app_roles():

View file

@ -2,12 +2,15 @@
import os import os
from sqlalchemy import ForeignKey, Integer, String from sqlalchemy import ForeignKey, Integer, String, Boolean
from sqlalchemy.orm import relationship from sqlalchemy.orm import relationship
from database import db from database import db
import helpers.kubernetes as k8s import helpers.kubernetes as k8s
from .apps import APP_NOT_INSTALLED_STATUS
# Circular import, need fixing
#from .apps import APP_NOT_INSTALLED_STATUS
APP_NOT_INSTALLED_STATUS = "Not installed"
class App(db.Model): class App(db.Model):
""" """
@ -18,12 +21,31 @@ class App(db.Model):
id = db.Column(Integer, primary_key=True) id = db.Column(Integer, primary_key=True)
name = db.Column(String(length=64)) name = db.Column(String(length=64))
slug = db.Column(String(length=64), unique=True) slug = db.Column(String(length=64), unique=True)
external = db.Column(Boolean, unique=False, nullable=False, default=True, server_default='0')
url = db.Column(String(length=128), unique=False)
def __repr__(self): def __repr__(self):
return f"{self.id} <{self.name}>" return f"{self.id} <{self.name}>"
def get_url(self):
"""Returns the URL where this application is running"""
if self.external:
return self.url
# TODO: Get URL from Kubernetes
return "unknown"
def get_status(self): def get_status(self):
"""Returns a string that describes the app state in the cluster""" """Returns a string that describes the app state in the cluster"""
if self.external:
return("External app")
# TODO: Get some kind of caching for those values, as this is called
# on every app list, causing significant delays in the interface
kustomization = self.kustomization kustomization = self.kustomization
if kustomization is not None and "status" in kustomization: if kustomization is not None and "status" in kustomization:
ks_ready, ks_message = App.check_condition(kustomization['status']) ks_ready, ks_message = App.check_condition(kustomization['status'])
@ -78,7 +100,9 @@ class App(db.Model):
""" """
# Delete all roles first # Delete all roles first
for role in self.roles: for role in self.roles:
role.delete() db.session.delete(role)
#role.delete()
db.session.commit()
db.session.delete(self) db.session.delete(self)
return db.session.commit() return db.session.commit()

View file

@ -71,6 +71,34 @@ def create_app(slug, name):
db.session.commit() db.session.commit()
current_app.logger.info(f"App definition: {name} ({slug}) created") current_app.logger.info(f"App definition: {name} ({slug}) created")
@app_cli.command("create-external")
@click.argument("slug")
@click.argument("name")
@click.argument("url")
def create_external_app(slug, name, url):
"""Create an app for external access
:param slug: str short name of the app
:param name: str name of the application
:param url: str URL of application
"""
obj = App()
obj.name = name
obj.slug = slug
obj.external = True
obj.url = url
app_obj = App.query.filter_by(slug=slug).first()
if app_obj:
current_app.logger.info(f"App definition: {name} ({slug}) already exists in database")
return
db.session.add(obj)
db.session.commit()
current_app.logger.info(f"App definition: {name} ({slug}) created")
@app_cli.command("list") @app_cli.command("list")
@ -151,6 +179,11 @@ def install_app(slug):
current_app.logger.error(f"App {slug} does not exist") current_app.logger.error(f"App {slug} does not exist")
return return
if app.external:
current_app.logger.info(
f"App {slug} is an external app and can not be provisioned automatically")
return
current_status = app.get_status() current_status = app.get_status()
if current_status == APP_NOT_INSTALLED_STATUS: if current_status == APP_NOT_INSTALLED_STATUS:
app.install() app.install()

View file

@ -0,0 +1,30 @@
"""Add fields for external apps
Revision ID: e08df0bef76f
Revises: b514cca2d47b
Create Date: 2022-09-23 16:38:06.557307
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'e08df0bef76f'
down_revision = 'b514cca2d47b'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('app', sa.Column('external', sa.Boolean(), server_default='0', nullable=False))
op.add_column('app', sa.Column('url', sa.String(length=128), nullable=True))
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('app', 'url')
op.drop_column('app', 'external')
# ### end Alembic commands ###