misc/pod-as-a-service#3: Define the interface for app modules



Issue Information

Issue Type: issue
Status: closed
Reported By: btasker
Assigned To: btasker

Milestone: PoC
Created: 10-Nov-24 13:49



Description

At the moment, we have a single app module - influxdb1x - and its spec has been (somewhat) hacked together as part of building.

Before making that too concrete, should probably think about whether it meets the needs of other apps so that we can adjust.

Then, as a test of the new spec, I'd like to chuck together a package for something different (Grafana maybe?)



Toggle State Changes

Activity


assigned to @btasker

Lets start with the current signature then:

create(k8s_client, apps_api, name, namespace, customer_id) -> dict
create_service(k8s_client, name, namespace, customer_id) -> dict
delete(k8s_client, apps_api, name, namespace) -> void

At some point we'll also want a describe() that returns information about the package.

I guess the answer, then, is to start writing a package for grafana and see what headaches we run into

verified

mentioned in commit 2d37e51baea5dcbac7b87132b89cb5928fff6d24

Commit: 2d37e51baea5dcbac7b87132b89cb5928fff6d24 
Author: B Tasker                            
                            
Date: 2024-11-10T14:14:42.000+00:00 

Message

feat: introduce grafana app (#3)

+214 -1 (215 lines changed)

That wasn't too bad at all.

There's relatively little difference between the two modules:

--- app/apps/influxdbv1.py  2024-11-10 13:34:52.070336719 +0000
+++ app/apps/grafana.py 2024-11-10 14:01:34.709599411 +0000
@@ -8,7 +8,7 @@
 from kubernetes import client

 def getAppName(name):
-    return f"influxdb1x-{name}"
+    return f"grafana-{name}"

 def getDefaultCreds():
     ''' Generate a random password to use for the default user
@@ -27,16 +27,16 @@
     return {"user": "admin", "password": ''.join(p)}

 def create(k8s_client, apps_api, name, namespace, customer_id):
-    ''' Create a new InfluxDB OSS 1.x app in the namespace
-    
+    ''' Create a new Grafana app in the namespace
         args:

-            @k8s_client: The kubernetes client object
+            k8s_client: The kubernetes client object
+            apps_api: the k8s apps api handle
             name: the user provided name for this deployment
             namespace: the namespace to create in,
             customer_id: the customer's internal ID

-        # TODO: persistent volume claim
+            # TODO: do we want to access SMTP credentials?
     '''

     app_name = getAppName(name)
@@ -45,20 +45,20 @@

     # Define the container
     container = client.V1Container(
-        name="influxdb",
-        image="influxdb:1.11.7",
+        name="grafana",
+        image="grafana/grafana:latest",
         image_pull_policy="IfNotPresent",
-        ports=[client.V1ContainerPort(container_port=8086)],
+        ports=[client.V1ContainerPort(container_port=3000)],
         volume_mounts = [
             {
-                "mountPath" : "/var/lib/influxdb",
+                "mountPath" : "/var/lib/grafana",
                 "name" :  "storage",
                 "subPath" : f"paas/{app_name}/data"
             },
             {
-                "mountPath" : "/etc/influxdb",
-                "name" :  "storage",
-                "subPath" : f"paas/{app_name}/config"
+                "mountPath" : "/var/run/grafana_secret",
+                "name" : "grafana-secrets",
+                "readOnly" : True
             }
             ],
             env = [
@@ -67,25 +67,9 @@
                 "value" : "true"
                 },
                 {
-                "name" : "INFLUXDB_ADMIN_PASSWORD",
-                "valueFrom" : {
-                        "secretKeyRef" : {
-                                "name" : app_name,
-                                "key" : "admin_pass",
-                                "optional" : False
-                            }
-                    }
-                },
-                {
-                "name" : "INFLUXDB_ADMIN_USER",
-                "valueFrom" : {
-                        "secretKeyRef" : {
-                                "name" : app_name,
-                                "key" : "admin_user",
-                                "optional" : False
-                            }
-                    }
-                }                
+                 "name" : "GF_SECURITY_ADMIN_PASSWORD__FILE",
+                 "value" : "/var/run/grafana_secret/admin_pass"
+                }
             ]
     )

@@ -93,7 +77,7 @@
     template = client.V1PodTemplateSpec(
         metadata=client.V1ObjectMeta(
             labels={
-                "app": "influxdb1x",
+                "app": "grafana",
                 "name": app_name
             },
             annotations={
@@ -110,6 +94,13 @@
                             "path" : os.getenv("NFS_PATH", "/volume1/kubernetes_misc_mounts"),
                             "readOnly": False
                         }
+                },
+                {
+                    "name" : "grafana-secrets",
+                    "secret" : {
+                            "secretName" : app_name
+                        }
+                    
                 }
                 ]

@@ -123,7 +114,7 @@
         service_name=f"influx1x-{name}",
         selector=client.V1LabelSelector(
             match_labels={
-                "app": "influxdb1x",
+                "app": "grafana",
                 "name" : app_name
             }
         ),
@@ -180,9 +171,9 @@
                 },
             "spec" : {
                 "ports" : [{
-                    "port": 8086,
+                    "port": 3000,
                     "protocol": "TCP",
-                    "targetPort": 8086
+                    "targetPort": 3000
                 }],
                 "selector": {
                     "name" : app_name
@@ -191,7 +182,7 @@
             }
         }
         k8s_client.create_namespaced_service(namespace, service)
-        return {"name" : app_name, "port": 8086}, ""
+        return {"name" : app_name, "port": 3000}, ""
     except Exception as e:
         return False, e


But that's because they're also both quite simple in terms of demands.

One thing I think we need to do, though, is have create include an attribute that can be used to passed a dict of generic config in (for example, if we wanted to add support for SMTP config to grafana).

We should also make sure the signature doesn't need updating to pass some other k8s api handle in

verified

mentioned in commit f2b235a84368f4bc1c71064ab2a0a394a5710a2f

Commit: f2b235a84368f4bc1c71064ab2a0a394a5710a2f 
Author: B Tasker                            
                            
Date: 2024-11-10T14:24:38.000+00:00 

Message

feat: add ability to pass an arbitrary dict of config into modules (#3)

Nothing currently sets or consumes these, but it'll allow us to pass app specific config through in future

+14 -10 (24 lines changed)
verified

mentioned in commit de66f0f43df8191a2b0a94aea1cfb046f75f29a1

Commit: de66f0f43df8191a2b0a94aea1cfb046f75f29a1 
Author: B Tasker                            
                            
Date: 2024-11-10T14:22:47.000+00:00 

Message

fix: pass API handles through as a dict (#3)

This makes it easy for other modules to use more complex modules if needed

+21 -16 (37 lines changed)
verified

mentioned in commit 8583afd593879e857e00a478f47fb099f2a5bdc6

Commit: 8583afd593879e857e00a478f47fb099f2a5bdc6 
Author: B Tasker                            
                            
Date: 2024-11-10T14:26:57.000+00:00 

Message

chore: make it clear these utility functions aren't part of the ABI (#3)

+10 -10 (20 lines changed)

Although I'm not planning on doing anything with it at the moment, I'd like to add a resize_disk() function too.

Most app instances are actually going to be a StateFulSet and you can't simply resize a PV included in the claim template - you have to go through a bit of mischief.

Each module is the authority on how its volumes are defined, so it'd probably be best to give them the (optional) ability to resize.

verified

mentioned in commit 59f80954a543305cd4f6fd899e4179b2359d0718

Commit: 59f80954a543305cd4f6fd899e4179b2359d0718 
Author: B Tasker                            
                            
Date: 2024-11-10T14:34:21.000+00:00 

Message

feat: add describe() to module API (#3)

+20 -0 (20 lines changed)
verified

mentioned in commit 72fd3f1a2705ac060faaee704b8c45e440c52fd7

Commit: 72fd3f1a2705ac060faaee704b8c45e440c52fd7 
Author: B Tasker                            
                            
Date: 2024-11-10T14:39:47.000+00:00 

Message

feat: introduce resize_disk() (#3)

+14 -0 (14 lines changed)

OK, so that gives us the following

describe() -> dict
resize_disk(clients, name, namespace, customer_id, new_size, config) -> dict, error (but not currently implemented)
create(clients, name, namespace, customer_id, config) -> dict, error
create_service(clients, name, namespace, customer_id, config) -> dict, error
delete(k8s_client, apps_api, name, namespace, config) -> bool, error
verified

mentioned in commit 8052b79a8c4b8abe2e5d89d1a28d43745b711ef4

Commit: 8052b79a8c4b8abe2e5d89d1a28d43745b711ef4 
Author: B Tasker                            
                            
Date: 2024-11-10T14:43:58.000+00:00 

Message

fix: make return type consistent (#3)

+2 -2 (4 lines changed)