From b2ece55232d9c904f65bf496089afdd09e23f2d8 Mon Sep 17 00:00:00 2001
From: Arie Peterson <arie@greenhost.nl>
Date: Wed, 18 Jan 2023 15:34:42 +0100
Subject: [PATCH] Add missing apps from configmap specifications

---
 backend/app.py               | 11 ++++++---
 backend/areas/apps/models.py |  6 +++++
 backend/cluster_config.py    | 45 ++++++++++++++++++++++++++++++++++++
 backend/requirements.txt     |  1 +
 4 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 backend/cluster_config.py

diff --git a/backend/app.py b/backend/app.py
index 9d5aec62..8394a1ff 100644
--- a/backend/app.py
+++ b/backend/app.py
@@ -32,6 +32,7 @@ from helpers import (
     unauthorized_error,
 )
 
+import cluster_config
 from config import *
 import logging
 
@@ -59,13 +60,17 @@ app.config["SECRET_KEY"] = SECRET_KEY
 app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI
 app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = SQLALCHEMY_TRACK_MODIFICATIONS
 
+app.logger.setLevel(logging.INFO)
+app.logger.info("Starting dashboard backend.")
+
 cors = CORS(app)
 Migrate(app, db)
 db.init_app(app)
 
-
-app.logger.setLevel(logging.INFO)
-app.logger.info("Starting dashboard backend.")
+# We need this app context in order to talk the database, which is managed by
+# flask-sqlalchemy, which assumes a flask app context.
+with app.app_context():
+    cluster_config.populate_apps()
 
 app.register_blueprint(api_v1)
 app.register_blueprint(web)
diff --git a/backend/areas/apps/models.py b/backend/areas/apps/models.py
index ef930501..0f2b00ec 100644
--- a/backend/areas/apps/models.py
+++ b/backend/areas/apps/models.py
@@ -30,6 +30,12 @@ class App(db.Model):
     # URL is stored in a configmap (see get_url)
     url = db.Column(String(length=128), unique=False)
 
+    def __init__(self, slug, name, external=False, url=None):
+        self.slug = slug
+        self.name = name
+        self.external = external
+        self.url = url
+
     def __repr__(self):
         return f"{self.id} <{self.name}>"
 
diff --git a/backend/cluster_config.py b/backend/cluster_config.py
new file mode 100644
index 00000000..1ea44e1b
--- /dev/null
+++ b/backend/cluster_config.py
@@ -0,0 +1,45 @@
+from database import db
+from areas.apps.models import App
+import helpers.kubernetes as k8s
+
+import logging
+import yaml
+
+# Read in two configmaps from the cluster, which specify which apps should be
+# present in the database.
+def populate_apps():
+    logging.info("cluster_config: populating apps")
+    database_apps = {}
+    for app in App.query.all():
+        slug = app.slug
+        database_apps[slug] = app
+        logging.info(f"database app: {slug}")
+    _populate_apps_from(database_apps, "stackspin-apps")
+    _populate_apps_from(database_apps, "stackspin-apps-custom")
+
+# Read a list of apps from a configmap. Check if they are already present in
+# the database, and if not, add missing ones there. Properties `name`,
+# `external` and `url` can be specified in yaml format in the configmap value
+# contents.
+def _populate_apps_from(database_apps, configmap_name):
+    cm_apps = k8s.get_kubernetes_config_map_data(configmap_name, "flux-system")
+    if cm_apps is None:
+        logging.info(f"Could not find configmap '{configmap_name}' in namespace 'flux-system'; ignoring.")
+    else:
+        for app_slug, app_data in cm_apps.items():
+            logging.info(f"configmap app: {app_slug}")
+            if app_slug in database_apps:
+                logging.info(f"  already present in database")
+            else:
+                logging.info(f"  not present in database, adding!")
+                data = yaml.safe_load(app_data)
+                name = data["name"]
+                logging.info(f"  name: {name}")
+                external = data.get("external", False)
+                logging.info(f"  type external: {type(external)}")
+                logging.info(f"  external: {external}")
+                url = data.get("url", None)
+                logging.info(f"  url: {url}")
+                new_app = App(slug=app_slug, name=name, external=external, url=url)
+                db.session.add(new_app)
+        db.session.commit()
diff --git a/backend/requirements.txt b/backend/requirements.txt
index eae5bd29..9a625388 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -25,6 +25,7 @@ platformdirs==2.5.1
 pycparser==2.21
 PyJWT==2.3.0
 pyrsistent==0.18.1
+PyYAML==6.0
 regex==2022.3.15
 requests==2.27.1
 requests-oauthlib==1.3.1
-- 
GitLab