diff --git a/Makefile b/Makefile index 94c1eee1..35da26a7 100644 --- a/Makefile +++ b/Makefile @@ -261,8 +261,8 @@ clean-project-docs: rm -rf project-docs/_build deploy-workshop: - kubectl apply -f https://github.com/vmware-tanzu-labs/lab-k8s-fundamentals/releases/download/5.0/workshop.yaml - kubectl apply -f https://github.com/vmware-tanzu-labs/lab-k8s-fundamentals/releases/download/5.0/trainingportal.yaml + kubectl apply -f https://github.com/educates/lab-k8s-fundamentals/releases/download/7.4/workshop.yaml + kubectl apply -f https://github.com/educates/lab-k8s-fundamentals/releases/download/7.4/trainingportal.yaml STATUS=1; ATTEMPTS=0; ROLLOUT_STATUS_CMD="kubectl rollout status deployment/training-portal -n lab-k8s-fundamentals-ui"; until [ $$STATUS -eq 0 ] || $$ROLLOUT_STATUS_CMD || [ $$ATTEMPTS -eq 5 ]; do sleep 5; $$ROLLOUT_STATUS_CMD; STATUS=$$?; ATTEMPTS=$$((ATTEMPTS + 1)); done delete-workshop: diff --git a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopallocation.yaml b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopallocation.yaml index 87a70c84..2ca1fecb 100644 --- a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopallocation.yaml +++ b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopallocation.yaml @@ -40,9 +40,12 @@ spec: type: object required: - name + - user properties: name: type: string + user: + type: string status: type: object x-kubernetes-preserve-unknown-fields: true diff --git a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopenvironment.yaml b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopenvironment.yaml index 50ccb26b..b733c1f7 100644 --- a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopenvironment.yaml +++ b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopenvironment.yaml @@ -154,6 +154,12 @@ spec: type: string namespace: type: string + capacity: + type: integer + initial: + type: integer + reserved: + type: integer secrets: type: object properties: diff --git a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopsession.yaml b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopsession.yaml index b55fc2d2..2f644898 100644 --- a/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopsession.yaml +++ b/carvel-packages/training-platform/bundle/config/11-session-manager/01-crds-workshopsession.yaml @@ -151,6 +151,8 @@ spec: properties: enabled: type: boolean + user: + type: string additionalPrinterColumns: - name: URL type: string diff --git a/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterrolebindings.yaml b/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterrolebindings.yaml new file mode 100644 index 00000000..05f2f3f1 --- /dev/null +++ b/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterrolebindings.yaml @@ -0,0 +1,13 @@ +#! Cluster role bindings for the remote access. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: educates-remote-access +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: educates-remote-access +subjects: +- kind: ServiceAccount + name: remote-access + namespace: educates diff --git a/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterroles.yaml b/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterroles.yaml new file mode 100644 index 00000000..a809844b --- /dev/null +++ b/carvel-packages/training-platform/bundle/config/12-lookup-service/clusterroles.yaml @@ -0,0 +1,18 @@ +#! Cluster role for the remote access clients. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: educates-remote-access +rules: + - apiGroups: + - training.educates.dev + resources: + - trainingportals + - workshopenvironments + - workshopsessions + - workshopallocations + - workshops + verbs: + - get + - list + - watch diff --git a/carvel-packages/training-platform/bundle/config/12-lookup-service/secrets.yaml b/carvel-packages/training-platform/bundle/config/12-lookup-service/secrets.yaml new file mode 100644 index 00000000..7ed512e0 --- /dev/null +++ b/carvel-packages/training-platform/bundle/config/12-lookup-service/secrets.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + name: remote-access-token + namespace: educates + annotations: + kubernetes.io/service-account.name: remote-access + kapp.k14s.io/change-rule: "upsert after upserting educates/sa-with-separate-token-secret" +type: kubernetes.io/service-account-token diff --git a/carvel-packages/training-platform/bundle/config/12-lookup-service/serviceaccounts.yaml b/carvel-packages/training-platform/bundle/config/12-lookup-service/serviceaccounts.yaml new file mode 100644 index 00000000..b31894cb --- /dev/null +++ b/carvel-packages/training-platform/bundle/config/12-lookup-service/serviceaccounts.yaml @@ -0,0 +1,8 @@ +#! ServiceAccount for remote access clients. +apiVersion: v1 +kind: ServiceAccount +metadata: + name: remote-access + namespace: educates + annotations: + kapp.k14s.io/change-group: "educates/sa-with-separate-token-secret" diff --git a/project-docs/index.rst b/project-docs/index.rst index c98857d0..56e24810 100644 --- a/project-docs/index.rst +++ b/project-docs/index.rst @@ -85,6 +85,7 @@ Educates :maxdepth: 2 :caption: Release Notes: + release-notes/version-2.7.3 release-notes/version-2.7.2 release-notes/version-2.7.1 release-notes/version-2.7.0 diff --git a/project-docs/release-notes/version-2.7.3.md b/project-docs/release-notes/version-2.7.3.md new file mode 100644 index 00000000..d3551486 --- /dev/null +++ b/project-docs/release-notes/version-2.7.3.md @@ -0,0 +1,18 @@ +Version 2.7.3 +============= + +Upcoming Changes +---------------- + +For details on significant changes in future versions, including feature +deprecations and removals which may necessitate updates to existing workshops, +see [Upcoming changes](upcoming-changes). + +Backported Changes +------------------ + +* Range of changes backported from 3.0.0 in order to support monitoring of + Educates 2.7.3 clusters using the new lookup service included with 3.0.0. It + is believed this should cause no noticeable changes or incompatabilities. If + you have no need for the lookup service, there is no need to upgrade to + Educates 2.7.3 from 2.7.2. diff --git a/session-manager/handlers/trainingportal.py b/session-manager/handlers/trainingportal.py index bbd2d6be..3186ec2b 100644 --- a/session-manager/handlers/trainingportal.py +++ b/session-manager/handlers/trainingportal.py @@ -86,6 +86,8 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, # Calculate name for the portal namespace. portal_name = name + portal_uid = uid + portal_namespace = f"{portal_name}-ui" # Calculate access details for the portal. The hostname used to access the @@ -388,6 +390,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/policy.engine": CLUSTER_SECURITY_POLICY_ENGINE, f"training.{OPERATOR_API_GROUP}/policy.name": "baseline", }, @@ -444,6 +447,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "roleRef": { @@ -472,6 +476,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "roleRef": { @@ -530,6 +535,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, } @@ -544,6 +550,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "type": "kubernetes.io/service-account-token", @@ -557,6 +564,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "roleRef": { @@ -584,6 +592,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "spec": { @@ -604,6 +613,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "data": { @@ -620,6 +630,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/portal.services.dashboard": "true", }, }, @@ -633,6 +644,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "deployment": "training-portal", f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/portal.services.dashboard": "true", }, }, @@ -850,6 +862,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "spec": { @@ -868,6 +881,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, "annotations": {}, }, @@ -936,6 +950,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "spec": { @@ -969,6 +984,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "spec": { @@ -997,6 +1013,7 @@ def training_portal_create(name, uid, body, spec, status, patch, runtime, retry, "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "spec": { diff --git a/session-manager/handlers/workshopallocation.py b/session-manager/handlers/workshopallocation.py index dd06b4d6..0535e804 100644 --- a/session-manager/handlers/workshopallocation.py +++ b/session-manager/handlers/workshopallocation.py @@ -146,8 +146,16 @@ def workshop_allocation_create( portal_name = meta.get("labels", {}).get( f"training.{OPERATOR_API_GROUP}/portal.name", "" ) + portal_uid = meta.get("labels", {}).get( + f"training.{OPERATOR_API_GROUP}/portal.uid", "" + ) environment_name = spec["environment"]["name"] + + environment_uid = meta.get("labels", {}).get( + f"training.{OPERATOR_API_GROUP}/environment.uid", "" + ) + workshop_namespace = environment_name session_name = spec["session"]["name"] @@ -447,7 +455,9 @@ def workshop_allocation_create( f"training.{OPERATOR_API_GROUP}/component.group": "objects", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.objects": "true", } diff --git a/session-manager/handlers/workshopenvironment.py b/session-manager/handlers/workshopenvironment.py index 1903e2b0..cd1fc8d1 100644 --- a/session-manager/handlers/workshopenvironment.py +++ b/session-manager/handlers/workshopenvironment.py @@ -123,6 +123,8 @@ def workshop_environment_create( # created. environment_name = name + environment_uid = uid + workshop_namespace = environment_name # Can optionally be passed name of the training portal via a label when the @@ -131,6 +133,9 @@ def workshop_environment_create( portal_name = meta.get("labels", {}).get( f"training.{OPERATOR_API_GROUP}/portal.name", "" ) + portal_uid = meta.get("labels", {}).get( + f"training.{OPERATOR_API_GROUP}/portal.uid", "" + ) # The name of the workshop to be deployed can differ and is taken from the # specification of the workshop environment. Lookup the workshop resource @@ -464,7 +469,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/policy.engine": CLUSTER_SECURITY_POLICY_ENGINE, f"training.{OPERATOR_API_GROUP}/policy.name": "privileged", }, @@ -526,7 +533,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "roleRef": { @@ -558,7 +567,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "roleRef": { @@ -622,7 +633,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -696,7 +709,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "data": { @@ -849,7 +864,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "rules": [ @@ -877,7 +894,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -928,7 +947,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -973,7 +994,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1020,7 +1043,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1096,7 +1121,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, } @@ -1131,7 +1158,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1158,7 +1187,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.mirror": "true", }, }, @@ -1175,7 +1206,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.mirror": "true", }, }, @@ -1285,7 +1318,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1339,7 +1374,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.images": "true", }, }, @@ -1354,7 +1391,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.images": "true", }, }, @@ -1416,7 +1455,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1488,7 +1529,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1531,7 +1574,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "data": {"config.yaml": yaml.dump(artifacts_config, Dumper=yaml.Dumper)}, @@ -1559,7 +1604,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1639,7 +1686,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.assets": "true", }, }, @@ -1654,7 +1703,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.services.assets": "true", }, }, @@ -1737,7 +1788,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1809,7 +1862,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1829,7 +1884,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "data": {}, @@ -1888,7 +1945,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -1965,7 +2024,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, } @@ -1979,7 +2040,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "roleRef": { @@ -2006,7 +2069,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -2020,7 +2085,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -2070,7 +2137,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, }, }, "spec": { @@ -2151,7 +2220,9 @@ def workshop_environment_create( f"training.{OPERATOR_API_GROUP}/component": "environment", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/environment.objects": "true", } ) diff --git a/session-manager/handlers/workshoprequest.py b/session-manager/handlers/workshoprequest.py index 2e1b562e..58ad58b0 100644 --- a/session-manager/handlers/workshoprequest.py +++ b/session-manager/handlers/workshoprequest.py @@ -32,7 +32,12 @@ def workshop_request_create(name, uid, namespace, spec, patch, logger, **_): # resource anyway. First lookup up the desired workshop environment # and determine if it exists and is valid. - portal_name = spec.get("portal", {}).get("name", "") + # NOTE: Details of the portal are not actually available so this will + # result in empty strings. As WorkshopRequest isn't being used anymore + # no big deal. + + # portal_name = spec.get("portal", {}).get("name", "") + # portal_uid = spec.get("portal", {}).get("uid", "") environment_name = spec["environment"]["name"] @@ -120,8 +125,9 @@ def _generate_random_session_id(n=5): "metadata": { "name": session_name, "labels": { - f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, - f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + # f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + # f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, + # f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, }, }, "spec": { diff --git a/session-manager/handlers/workshopsession.py b/session-manager/handlers/workshopsession.py index 8435a3f8..5118187c 100644 --- a/session-manager/handlers/workshopsession.py +++ b/session-manager/handlers/workshopsession.py @@ -98,7 +98,9 @@ def _setup_session_namespace( primary_namespace_body, workshop_name, portal_name, + portal_uid, environment_name, + environment_uid, session_name, workshop_namespace, session_namespace, @@ -202,7 +204,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -277,7 +281,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -311,7 +317,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -342,7 +350,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -391,7 +401,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -412,7 +424,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, } ) @@ -432,7 +446,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, } ) @@ -448,7 +464,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, } ) @@ -464,7 +482,9 @@ def _setup_session_namespace( f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, } ) @@ -553,6 +573,8 @@ def workshop_session_create(name, meta, uid, spec, status, patch, retry, **_): session_id = spec["session"]["id"] session_namespace = f"{workshop_namespace}-{session_id}" + environment_uid = environment_instance.obj["metadata"]["uid"] + # Can optionally be passed name of the training portal via a label # when the workshop environment is created as a child to a training # portal. @@ -560,6 +582,9 @@ def workshop_session_create(name, meta, uid, spec, status, patch, retry, **_): portal_name = meta.get("labels", {}).get( f"training.{OPERATOR_API_GROUP}/portal.name", "" ) + portal_uid = meta.get("labels", {}).get( + f"training.{OPERATOR_API_GROUP}/portal.uid", "" + ) # We pull details of the workshop to be deployed from the status of the # environment custom resource. This is a copy of the specification from the @@ -717,7 +742,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/policy.engine": CLUSTER_SECURITY_POLICY_ENGINE, f"training.{OPERATOR_API_GROUP}/policy.name": namespace_security_policy, @@ -809,7 +836,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -844,6 +873,7 @@ def resolve_security_policy(name): "labels": { f"training.{OPERATOR_API_GROUP}/component": "portal", f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, }, }, "type": "kubernetes.io/service-account-token", @@ -880,7 +910,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -925,7 +957,9 @@ def resolve_security_policy(name): namespace_instance.obj, workshop_name, portal_name, + portal_uid, environment_name, + environment_uid, session_name, workshop_namespace, session_namespace, @@ -1070,7 +1104,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -1109,7 +1145,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component.group": "variables", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -1166,7 +1204,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/policy.engine": CLUSTER_SECURITY_POLICY_ENGINE, f"training.{OPERATOR_API_GROUP}/policy.name": target_security_policy, @@ -1199,7 +1239,9 @@ def resolve_security_policy(name): namespace_instance.obj, workshop_name, portal_name, + portal_uid, environment_name, + environment_uid, session_name, workshop_namespace, session_namespace, @@ -1249,7 +1291,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.objects": "true", } @@ -1374,7 +1418,9 @@ def resolve_security_policy(name): namespace_instance.obj, workshop_name, portal_name, + portal_uid, environment_name, + environment_uid, session_name, workshop_namespace, session_namespace, @@ -1508,7 +1554,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/application": "workshop", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.services.workshop": "true", }, @@ -1525,7 +1573,9 @@ def resolve_security_policy(name): f"training.{OPERATOR_API_GROUP}/application": "workshop", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.services.workshop": "true", }, @@ -1986,7 +2036,9 @@ def vendir_secrets_required(contents): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2124,7 +2176,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2272,7 +2326,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2333,7 +2389,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2509,7 +2567,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2536,7 +2596,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2560,7 +2622,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "registry", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.services.registry": "true", }, @@ -2579,7 +2643,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "registry", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, f"training.{OPERATOR_API_GROUP}/session.services.registry": "true", }, @@ -2700,7 +2766,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "registry", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2723,7 +2791,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "registry", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2804,7 +2874,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/component": "session", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -2867,7 +2939,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "workshop", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -3009,7 +3083,9 @@ def _apply_environment_patch(patch): f"training.{OPERATOR_API_GROUP}/application": "workshop", f"training.{OPERATOR_API_GROUP}/workshop.name": workshop_name, f"training.{OPERATOR_API_GROUP}/portal.name": portal_name, + f"training.{OPERATOR_API_GROUP}/portal.uid": portal_uid, f"training.{OPERATOR_API_GROUP}/environment.name": environment_name, + f"training.{OPERATOR_API_GROUP}/environment.uid": environment_uid, f"training.{OPERATOR_API_GROUP}/session.name": session_name, }, }, @@ -3128,22 +3204,16 @@ def _apply_environment_patch(patch): # Set the URL for accessing the workshop session directly in the # status. This would only be used if directly creating workshop # session and not when using training portal. Set phase to Running - # if standalone workshop environment or Available if associated - # with a training portal. The latter can be overridden though if - # the training portal had already set the phase before the operator - # had managed to process the resource. + # if standalone workshop environment. Where created by a training + # portal it will set the status itself appropriately. url = f"{INGRESS_PROTOCOL}://{session_hostname}" phase = "Running" - if portal_name: - phase = status.get(OPERATOR_STATUS_KEY, {}).get("phase", "Available") - - patch["status"] = {} + logger.info("STATUS %s", status) - patch["status"][OPERATOR_STATUS_KEY] = { - "phase": phase, + changes = { "message": None, "url": url, "sshd": { @@ -3154,6 +3224,13 @@ def _apply_environment_patch(patch): }, } + if not portal_name: + changes["phase"] = phase + + patch["status"] = { + OPERATOR_STATUS_KEY: changes, + } + @kopf.on.delete( f"training.{OPERATOR_API_GROUP}", diff --git a/training-portal/src/project/apps/workshops/manager/environments.py b/training-portal/src/project/apps/workshops/manager/environments.py index aecea2e3..787238c2 100644 --- a/training-portal/src/project/apps/workshops/manager/environments.py +++ b/training-portal/src/project/apps/workshops/manager/environments.py @@ -421,6 +421,10 @@ def update_workshop_environments(training_portal, workshops): environment.save() + update_environment_status_details( + environment.name, environment.capacity, environment.reserved + ) + @background_task @resources_lock @@ -496,6 +500,7 @@ def process_workshop_environment(portal, workshop, position): "name": environment.name, "labels": { f"training.{settings.OPERATOR_API_GROUP}/portal.name": portal.name, + f"training.{settings.OPERATOR_API_GROUP}/portal.uid": portal.uid, }, "ownerReferences": [ { @@ -523,6 +528,13 @@ def process_workshop_environment(portal, workshop, position): "theme": {"name": settings.THEME_NAME}, "cookies": {"domain": settings.SESSION_COOKIE_DOMAIN}, }, + "status": { + settings.OPERATOR_STATUS_KEY: { + "capacity": environment.capacity, + "initial": environment.initial, + "reserved": environment.reserved, + }, + }, } if settings.GOOGLE_TRACKING_ID is not None: @@ -644,3 +656,42 @@ def replace_workshop_environment(environment): # Now schedule creation of the replacement workshop session. process_workshop_environment(environment.portal, workshop, position).schedule() + + +def update_environment_status_details(name, capacity, reserved): + """Update the capacity for the workshop environment recorded in the status.""" + + try: + K8SWorkshopEnvironment = pykube.object_factory( + api, + f"training.{settings.OPERATOR_API_GROUP}/v1beta1", + "WorkshopEnvironment", + ) + + resource = K8SWorkshopEnvironment.objects(api).get(name=name) + + # The status may not exist as yet if not processed by the operator. + + status = resource.obj.setdefault("status", {}).setdefault( + settings.OPERATOR_STATUS_KEY, {} + ) + + status["capacity"] = capacity + status["reserved"] = reserved + + resource.update() + + logger.info( + "Updated status of workshop environment %s with capacity=%s and reserved=%s.", + name, + capacity, + reserved, + ) + + except pykube.exceptions.ObjectDoesNotExist: + pass + + except pykube.exceptions.PyKubeError: + logger.exception( + "Failed to update status details of workshop environment %s.", name + ) diff --git a/training-portal/src/project/apps/workshops/manager/sessions.py b/training-portal/src/project/apps/workshops/manager/sessions.py index 49018c9e..a1864c65 100644 --- a/training-portal/src/project/apps/workshops/manager/sessions.py +++ b/training-portal/src/project/apps/workshops/manager/sessions.py @@ -72,6 +72,7 @@ def create_request_resources(session): f"training.{settings.OPERATOR_API_GROUP}/component.group": "variables", f"training.{settings.OPERATOR_API_GROUP}/workshop.name": session.environment.workshop.name, f"training.{settings.OPERATOR_API_GROUP}/portal.name": settings.PORTAL_NAME, + f"training.{settings.OPERATOR_API_GROUP}/portal.uid": settings.PORTAL_UID, f"training.{settings.OPERATOR_API_GROUP}/environment.name": session.environment.name, f"training.{settings.OPERATOR_API_GROUP}/session.name": session.name, }, @@ -109,7 +110,9 @@ def create_request_resources(session): f"training.{settings.OPERATOR_API_GROUP}/component": "request", f"training.{settings.OPERATOR_API_GROUP}/workshop.name": session.environment.workshop.name, f"training.{settings.OPERATOR_API_GROUP}/portal.name": settings.PORTAL_NAME, + f"training.{settings.OPERATOR_API_GROUP}/portal.uid": settings.PORTAL_UID, f"training.{settings.OPERATOR_API_GROUP}/environment.name": session.environment.name, + f"training.{settings.OPERATOR_API_GROUP}/environment.uid": session.environment.uid, f"training.{settings.OPERATOR_API_GROUP}/session.name": session.name, }, "ownerReferences": [ @@ -125,7 +128,7 @@ def create_request_resources(session): }, "spec": { "environment": {"name": session.environment.name}, - "session": {"name": session.name}, + "session": {"name": session.name, "user": session.owner.username}, }, } @@ -137,7 +140,7 @@ def create_request_resources(session): ) -def update_session_status(name, phase): +def update_session_status(name, phase, user=None): """Update the status of the Kubernetes resource object for the workshop session. @@ -154,12 +157,26 @@ def update_session_status(name, phase): # In this case fill it in and operator will preserve the value when # sees associated with a training portal. - resource.obj.setdefault("status", {}).setdefault( + status = resource.obj.setdefault("status", {}).setdefault( settings.OPERATOR_STATUS_KEY, {} - )["phase"] = phase + ) + + status["phase"] = phase + + if user: + status["user"] = str(user.username) + resource.update() - logger.info("Updated status of workshop session %s to %s.", name, phase) + if user: + logger.info( + "Updated status of workshop session %s to %s for user %s.", + name, + phase, + user.username, + ) + else: + logger.info("Updated status of workshop session %s to %s.", name, phase) except pykube.exceptions.ObjectDoesNotExist: pass @@ -219,7 +236,9 @@ def create_workshop_session(session, secret): "name": session.name, "labels": { f"training.{settings.OPERATOR_API_GROUP}/portal.name": settings.PORTAL_NAME, + f"training.{settings.OPERATOR_API_GROUP}/portal.uid": settings.PORTAL_UID, f"training.{settings.OPERATOR_API_GROUP}/environment.name": session.environment.name, + f"training.{settings.OPERATOR_API_GROUP}/environment.uid": session.environment.uid, }, "ownerReferences": [ { @@ -300,7 +319,7 @@ def create_workshop_session(session, secret): report_analytics_event(session, "Session/Created") if session.owner: - update_session_status(session.name, "Allocated") + update_session_status(session.name, "Allocated", session.owner) report_analytics_event(session, "Session/Started") if session.token: session.mark_as_waiting() @@ -312,6 +331,7 @@ def _schedule_resource_creation(): transaction.on_commit(_schedule_resource_creation) else: + update_session_status(session.name, "Available") session.mark_as_waiting() @@ -676,11 +696,11 @@ def allocate_session_for_user(environment, user, token, timeout=None, params={}) session.params = resolve_request_params(session.environment.workshop, params) if token: - update_session_status(session.name, "Allocating") + update_session_status(session.name, "Allocating", user) report_analytics_event(session, "Session/Pending") session.mark_as_pending(user, token, timeout) else: - update_session_status(session.name, "Allocated") + update_session_status(session.name, "Allocated", user) report_analytics_event(session, "Session/Started") session.mark_as_running(user) @@ -723,9 +743,9 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): session.params = resolve_request_params(session.environment.workshop, params) if token: - update_session_status(session.name, "Allocating") + update_session_status(session.name, "Allocating", user) else: - update_session_status(session.name, "Allocated") + update_session_status(session.name, "Allocated", user) session.mark_as_pending(user, token, timeout) @@ -752,9 +772,9 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): session.params = resolve_request_params(session.environment.workshop, params) if token: - update_session_status(session.name, "Allocating") + update_session_status(session.name, "Allocating", user) else: - update_session_status(session.name, "Allocated") + update_session_status(session.name, "Allocated", user) session.mark_as_pending(user, token, timeout) @@ -788,9 +808,9 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): session.params = resolve_request_params(session.environment.workshop, params) if token: - update_session_status(session.name, "Allocating") + update_session_status(session.name, "Allocating", user) else: - update_session_status(session.name, "Allocated") + update_session_status(session.name, "Allocated", user) session.mark_as_pending(user, token, timeout) @@ -802,12 +822,16 @@ def create_session_for_user(environment, user, token, timeout=None, params={}): return session -def retrieve_session_for_user(environment, user, token=None, timeout=None, params={}): +def retrieve_session_for_user( + environment, user, session_name=None, token=None, timeout=None, params={} +): """Determine if there is already an allocated session for this workshop environment which the user is an owner of. If there is return it. Note that if we have a token because this is being requested via the REST API, it will not overwrite any existing token as we want to reuse the existing - one and not generate a new one. + one and not generate a new one. if we can't find an existing session, we + will create a new one if there is available capacity. If there is no + available capacity, no session will be returned. """ @@ -821,8 +845,23 @@ def retrieve_session_for_user(environment, user, token=None, timeout=None, param if session and not session.is_stopping(): if token and session.is_pending(): session.mark_as_pending(user, token, timeout) + + # If a session name was provided then any existing session found for the + # user must have that name. This is so that it is possible to reacquire + # a session that was previously created via the REST API and not create + # a new one if it couldn't be found. + + if session_name and session.name != session_name: + return + return session + # A session name was provided but we didn't find an existing session so + # we do not create a new one. + + if session_name: + return + # Determine if the user is permitted to create a workshop session. portal = environment.portal diff --git a/training-portal/src/project/apps/workshops/views/environment.py b/training-portal/src/project/apps/workshops/views/environment.py index 5ccefded..547c7187 100644 --- a/training-portal/src/project/apps/workshops/views/environment.py +++ b/training-portal/src/project/apps/workshops/views/environment.py @@ -2,7 +2,12 @@ """ -__all__ = ["environment", "environment_create", "environment_status", "environment_request"] +__all__ = [ + "environment", + "environment_create", + "environment_status", + "environment_request", +] import copy import uuid @@ -32,6 +37,7 @@ from ..manager.locking import resources_lock from ..models import TrainingPortal, Environment, EnvironmentState, SessionState + @login_required @require_http_methods(["GET"]) @resources_lock @@ -298,6 +304,8 @@ def environment_request(request, name): if last_name: user_details["last_name"] = last_name + session_name = request.GET.get("session") + # The timeout here in seconds is how long the workshop session will be # retained while waiting for it to be activated as a result of the URL # returned by the REST API call being visited by a user. This technically @@ -348,7 +356,7 @@ def environment_request(request, name): else: return HttpResponseBadRequest("Malformed JSON request payload") - + params = request_params # Check whether a user already has an existing session allocated @@ -373,18 +381,27 @@ def environment_request(request, name): characters = string.ascii_letters + string.digits token = "".join(random.sample(characters, 32)) - session = retrieve_session_for_user(instance, user, token, timeout, params) + session = retrieve_session_for_user( + instance, user, session_name, token, timeout, params + ) if not session: return JsonResponse({"error": "No session available"}, status=503) - details = {} + # If there is a session but it doesn't have a token associated with it then + # it wasn't created via the REST API and so cannot be reacquired using the + # REST API. - details["name"] = session.name + if session and not session.token: + return JsonResponse({"error": "Cannot be reacquired"}, status=503) # The "session" property was replaced by "name" and "session" deprecated. # Include "session" for now, but it will be removed in future update. + details = {} + + details["name"] = session.name + details["session"] = session.name details["user"] = user.get_username() diff --git a/training-portal/src/project/apps/workshops/views/session.py b/training-portal/src/project/apps/workshops/views/session.py index 922955bc..aa615c4b 100644 --- a/training-portal/src/project/apps/workshops/views/session.py +++ b/training-portal/src/project/apps/workshops/views/session.py @@ -140,7 +140,7 @@ def session_activate(request, name): return HttpResponseServerError("Owner for session is not active") if not instance.is_running(): - update_session_status(instance.name, "Allocated") + update_session_status(instance.name, "Allocated", instance.owner) report_analytics_event(instance, "Session/Started") instance.mark_as_running()