From b7f5ac997955ef9269f4fa1ade3fb25076924ffd Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Fri, 14 Apr 2023 17:17:14 +1000 Subject: [PATCH] Rename request inputs to session parameters. --- .../11-session-manager/01-crds-workshop.yaml | 28 +++++----- training-portal/requirements.in | 1 + training-portal/requirements.txt | 10 ++-- .../src/project/apps/workshops/admin.py | 2 +- .../apps/workshops/manager/environments.py | 3 +- .../apps/workshops/manager/sessions.py | 52 +++++++++++-------- .../migrations/0002_auto_20230414_0138.py | 34 ++++++++++++ .../migrations/0002_workshop_inputs.py | 19 ------- .../migrations/0003_auto_20230412_0332.py | 23 -------- .../migrations/0004_session_inputs.py | 19 ------- .../src/project/apps/workshops/models.py | 4 +- .../apps/workshops/views/environment.py | 11 ++-- .../project/apps/workshops/views/session.py | 4 +- training-portal/testing/test-rest-api.http | 4 +- 14 files changed, 97 insertions(+), 117 deletions(-) create mode 100644 training-portal/src/project/apps/workshops/migrations/0002_auto_20230414_0138.py delete mode 100644 training-portal/src/project/apps/workshops/migrations/0002_workshop_inputs.py delete mode 100644 training-portal/src/project/apps/workshops/migrations/0003_auto_20230412_0332.py delete mode 100644 training-portal/src/project/apps/workshops/migrations/0004_session_inputs.py diff --git a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshop.yaml b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshop.yaml index 527e69e6..0c65791c 100644 --- a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshop.yaml +++ b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshop.yaml @@ -816,18 +816,7 @@ spec: type: string value: type: string - patches: - type: object - x-kubernetes-preserve-unknown-fields: true - objects: - type: array - items: - type: object - x-kubernetes-preserve-unknown-fields: true - request: - type: object - properties: - inputs: + parameters: type: array items: type: object @@ -836,9 +825,20 @@ spec: properties: name: type: string - default: + value: type: string - default: "" + generate: + type: string + from: + type: string + patches: + type: object + x-kubernetes-preserve-unknown-fields: true + objects: + type: array + items: + type: object + x-kubernetes-preserve-unknown-fields: true status: type: object x-kubernetes-preserve-unknown-fields: true diff --git a/training-portal/requirements.in b/training-portal/requirements.in index 414d8618..0abeb955 100644 --- a/training-portal/requirements.in +++ b/training-portal/requirements.in @@ -10,6 +10,7 @@ requests==2.28.1 django-csp==3.7 kopf[full-auth]==1.35.6 pykube-ng==22.9.0 +rstr==3.2.0 black==22.3.0 pip-tools==6.6.2 diff --git a/training-portal/requirements.txt b/training-portal/requirements.txt index ea105e42..7a978b04 100644 --- a/training-portal/requirements.txt +++ b/training-portal/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: # # pip-compile requirements.in # @@ -133,6 +133,8 @@ requests-oauthlib==1.3.1 # via kubernetes rsa==4.8 # via google-auth +rstr==3.2.0 + # via -r requirements.in six==1.16.0 # via # google-auth @@ -141,9 +143,7 @@ six==1.16.0 sqlparse==0.4.2 # via django tomli==2.0.1 - # via - # black - # pep517 + # via pep517 typing-extensions==4.2.0 # via kopf urllib3==1.26.9 diff --git a/training-portal/src/project/apps/workshops/admin.py b/training-portal/src/project/apps/workshops/admin.py index cba721a3..ec9c0efd 100644 --- a/training-portal/src/project/apps/workshops/admin.py +++ b/training-portal/src/project/apps/workshops/admin.py @@ -156,7 +156,7 @@ class SessionAdmin(admin.ModelAdmin): "started", "expires", "url_link", - "inputs", + "params", ] def has_add_permission(self, request): diff --git a/training-portal/src/project/apps/workshops/manager/environments.py b/training-portal/src/project/apps/workshops/manager/environments.py index 058546cb..7a50977b 100644 --- a/training-portal/src/project/apps/workshops/manager/environments.py +++ b/training-portal/src/project/apps/workshops/manager/environments.py @@ -166,14 +166,13 @@ def activate_workshop_environment(resource): workshop.tags = details.get("spec.tags", []).obj() workshop.logo = details.get("spec.logo", "") workshop.url = details.get("spec.url", "") - workshop.inputs = details.get("spec.request.inputs", []).obj() + workshop.params = details.get("spec.session.parameters", []).obj() content = dict(details.get("spec.content", {}).obj()) image = content.get("image", "") files = content.get("files", "") url = details.get("spec.session.applications.workshop.url", "") - inputs = details.get("spec.request.inputs", []) if url: content["url"] = url diff --git a/training-portal/src/project/apps/workshops/manager/sessions.py b/training-portal/src/project/apps/workshops/manager/sessions.py index 6f3e48b4..d8ff4b1d 100644 --- a/training-portal/src/project/apps/workshops/manager/sessions.py +++ b/training-portal/src/project/apps/workshops/manager/sessions.py @@ -11,6 +11,7 @@ from itertools import islice import pykube +import rstr from django.conf import settings from django.contrib.auth import get_user_model @@ -28,34 +29,41 @@ api = pykube.HTTPClient(pykube.KubeConfig.from_env()) -def resolve_request_inputs(workshop, params): - default_inputs = {} +def resolve_request_params(workshop, params): + default_params = {} - for item in workshop.inputs: - key = item.get("name", "") - value = item.get("default", "") + def default_value(param): + if "value" in param: + return param["value"] + elif "generate" in param and param["generate"] == "expression": + return rstr.xeger(param.get("from", "")) + else: + return "" + + for item in workshop.params: + name = item.get("name", "") - if key: - default_inputs[key] = value + if name: + default_params[name] = default_value(item) - final_inputs = dict(default_inputs) + final_params = dict(default_params) - for item in params.get("inputs", []): - key = item.get("name", "") + for item in params: + name = item.get("name", "") value = item.get("value", "") - if key and key in final_inputs: - final_inputs[key] = value + if name and name in final_params: + final_params[name] = value - return final_inputs + return final_params -def create_inputs_resource(session): +def create_request_resource(session): secret_body = { "apiVersion": "v1", "kind": "Secret", "metadata": { - "name": f"{session.name}-inputs", + "name": f"{session.name}-params", "namespace": session.environment.name, "labels": { f"training.{settings.OPERATOR_API_GROUP}/component": "environment", @@ -77,7 +85,7 @@ def create_inputs_resource(session): "data": {}, } - for key, value in session.inputs.items(): + for key, value in session.params.items(): secret_body["data"][key] = base64.b64encode(value.encode("UTF-8")).decode("UTF-8") pykube.Secret(api, secret_body).create() @@ -230,7 +238,7 @@ def create_workshop_session(name): if session.token: session.mark_as_waiting() else: - create_inputs_resource(session) + create_request_resource(session) session.mark_as_running() else: session.mark_as_waiting() @@ -575,7 +583,7 @@ def allocate_session_for_user(environment, user, token, timeout=None, params={}) # confirmation is needed so can reclaim a session which was abandoned # immediately due to not being accessed. - session.inputs = resolve_request_inputs(session.environment.workshop, params) + session.params = resolve_request_params(session.environment.workshop, params) if token: update_session_status(session.name, "Allocating") @@ -584,7 +592,7 @@ def allocate_session_for_user(environment, user, token, timeout=None, params={}) else: update_session_status(session.name, "Allocated") report_analytics_event(session, "Session/Started") - create_inputs_resource(session) + create_request_resource(session) session.mark_as_running(user) # See if we need to create a new reserved session to replace the one which @@ -618,7 +626,7 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): if portal.sessions_maximum == 0: session = create_new_session(environment) - session.inputs = resolve_request_inputs(session.environment.workshop, params) + session.params = resolve_request_params(session.environment.workshop, params) if token: update_session_status(session.name, "Allocating") @@ -647,7 +655,7 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): if portal.active_sessions_count() < portal.sessions_maximum: session = create_new_session(environment) - session.inputs = resolve_request_inputs(session.environment.workshop, params) + session.params = resolve_request_params(session.environment.workshop, params) if token: update_session_status(session.name, "Allocating") @@ -683,7 +691,7 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): session = create_new_session(environment) - session.inputs = resolve_request_inputs(session.environment.workshop, params) + session.params = resolve_request_params(session.environment.workshop, params) if token: update_session_status(session.name, "Allocating") diff --git a/training-portal/src/project/apps/workshops/migrations/0002_auto_20230414_0138.py b/training-portal/src/project/apps/workshops/migrations/0002_auto_20230414_0138.py new file mode 100644 index 00000000..1b3f27c0 --- /dev/null +++ b/training-portal/src/project/apps/workshops/migrations/0002_auto_20230414_0138.py @@ -0,0 +1,34 @@ +# Generated by Django 3.2.18 on 2023-04-14 01:38 + +from django.db import migrations, models +import project.apps.workshops.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('workshops', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='session', + name='params', + field=project.apps.workshops.models.JSONField(default={}, verbose_name='session params'), + ), + migrations.AddField( + model_name='session', + name='uid', + field=models.CharField(default='', max_length=255, verbose_name='resource uid'), + ), + migrations.AddField( + model_name='workshop', + name='params', + field=project.apps.workshops.models.JSONField(default=[], verbose_name='session parameters'), + ), + migrations.AlterField( + model_name='session', + name='id', + field=models.CharField(max_length=64, verbose_name='session id'), + ), + ] diff --git a/training-portal/src/project/apps/workshops/migrations/0002_workshop_inputs.py b/training-portal/src/project/apps/workshops/migrations/0002_workshop_inputs.py deleted file mode 100644 index 60d85e69..00000000 --- a/training-portal/src/project/apps/workshops/migrations/0002_workshop_inputs.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 01:09 - -from django.db import migrations -import project.apps.workshops.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workshops', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='workshop', - name='inputs', - field=project.apps.workshops.models.JSONField(default=[], verbose_name='request inputs'), - ), - ] diff --git a/training-portal/src/project/apps/workshops/migrations/0003_auto_20230412_0332.py b/training-portal/src/project/apps/workshops/migrations/0003_auto_20230412_0332.py deleted file mode 100644 index c48fca9a..00000000 --- a/training-portal/src/project/apps/workshops/migrations/0003_auto_20230412_0332.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 03:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workshops', '0002_workshop_inputs'), - ] - - operations = [ - migrations.AddField( - model_name='session', - name='uid', - field=models.CharField(default='', max_length=255, verbose_name='resource uid'), - ), - migrations.AlterField( - model_name='session', - name='id', - field=models.CharField(max_length=64, verbose_name='session id'), - ), - ] diff --git a/training-portal/src/project/apps/workshops/migrations/0004_session_inputs.py b/training-portal/src/project/apps/workshops/migrations/0004_session_inputs.py deleted file mode 100644 index e90a0d9f..00000000 --- a/training-portal/src/project/apps/workshops/migrations/0004_session_inputs.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.2.18 on 2023-04-12 03:39 - -from django.db import migrations -import project.apps.workshops.models - - -class Migration(migrations.Migration): - - dependencies = [ - ('workshops', '0003_auto_20230412_0332'), - ] - - operations = [ - migrations.AddField( - model_name='session', - name='inputs', - field=project.apps.workshops.models.JSONField(default={}, verbose_name='session inputs'), - ), - ] diff --git a/training-portal/src/project/apps/workshops/models.py b/training-portal/src/project/apps/workshops/models.py index 03b08435..a0ebc7df 100644 --- a/training-portal/src/project/apps/workshops/models.py +++ b/training-portal/src/project/apps/workshops/models.py @@ -387,7 +387,7 @@ class Workshop(models.Model): url = models.CharField(max_length=255) content = JSONField(default={}) ingresses = JSONField(verbose_name="session ingresses", default=[]) - inputs = JSONField(verbose_name="request inputs", default=[]) + params = JSONField(verbose_name="session parameters", default=[]) class EnvironmentState(enum.IntEnum): @@ -608,7 +608,7 @@ class Session(models.Model): expires = models.DateTimeField(null=True, blank=True) token = models.CharField(max_length=256, null=True, blank=True) url = models.URLField(verbose_name="session url", null=True) - inputs = JSONField(verbose_name="session inputs", default={}) + params = JSONField(verbose_name="session params", default={}) def environment_name(self): return self.environment.name diff --git a/training-portal/src/project/apps/workshops/views/environment.py b/training-portal/src/project/apps/workshops/views/environment.py index 9574239e..7c90e371 100644 --- a/training-portal/src/project/apps/workshops/views/environment.py +++ b/training-portal/src/project/apps/workshops/views/environment.py @@ -199,7 +199,7 @@ def environment_request(request, name): # Extract any request parameters from the request body for using in late # binding of workshop session configuration. - params = {} + params = [] if request.body: try: @@ -217,12 +217,12 @@ def environment_request(request, name): if not isinstance(request_data, dict): return HttpResponseBadRequest("Malformed JSON request payload") - request_inputs = request_data.get("inputs", []) + request_params = request_data.get("parameters", []) - if not isinstance(request_inputs, list): + if not isinstance(request_params, list): return HttpResponseBadRequest("Malformed JSON request payload") - for value in request_inputs: + for value in request_params: key = value.get("name", "") item = value.get("item", "") @@ -233,8 +233,7 @@ def environment_request(request, name): else: return HttpResponseBadRequest("Malformed JSON request payload") - if request_inputs: - params["inputs"] = request_inputs + params = request_params # Check whether a user already has an existing session allocated # to them, in which case return that rather than create a new one. diff --git a/training-portal/src/project/apps/workshops/views/session.py b/training-portal/src/project/apps/workshops/views/session.py index c78ca133..773680b8 100644 --- a/training-portal/src/project/apps/workshops/views/session.py +++ b/training-portal/src/project/apps/workshops/views/session.py @@ -37,7 +37,7 @@ from ..manager.locking import resources_lock from ..manager.cleanup import delete_workshop_session -from ..manager.sessions import update_session_status, create_inputs_resource +from ..manager.sessions import update_session_status, create_request_resource from ..manager.analytics import report_analytics_event from ..models import TrainingPortal, SessionState @@ -125,7 +125,7 @@ def session_activate(request, name): if not instance.is_running(): update_session_status(instance.name, "Allocated") report_analytics_event(instance, "Session/Started") - create_inputs_resource(instance) + create_request_resource(instance) instance.mark_as_running() login(request, instance.owner, backend=settings.AUTHENTICATION_BACKENDS[0]) diff --git a/training-portal/testing/test-rest-api.http b/training-portal/testing/test-rest-api.http index 2683ba43..70553996 100644 --- a/training-portal/testing/test-rest-api.http +++ b/training-portal/testing/test-rest-api.http @@ -6,7 +6,7 @@ @client_id=my-client-id @client_secret=my-client-secret @environment_name=educates-cli-w01 -@index_url= +@index_url=https://www.example.com/ ### @@ -48,7 +48,7 @@ Authorization: Bearer {{login.response.body.access_token}} Content-Type: application/json { - "inputs": [ + "parameters": [ { "name": "PARAM1", "value": "VALUE1"