From d9ae21782484be608c86a36ea6c092f5476596fb Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Thu, 21 Sep 2023 07:11:51 +1000 Subject: [PATCH 1/5] Missing finalizers RBAC required for OpenShift. --- .../bundle/config/11-session-manager/01-clusterroles.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/carvel-packages/training-platform/bundle/config/11-session-manager/01-clusterroles.yaml b/carvel-packages/training-platform/bundle/config/11-session-manager/01-clusterroles.yaml index d4185749..52f66dd9 100644 --- a/carvel-packages/training-platform/bundle/config/11-session-manager/01-clusterroles.yaml +++ b/carvel-packages/training-platform/bundle/config/11-session-manager/01-clusterroles.yaml @@ -686,6 +686,7 @@ rules: resources: - trainingportals/finalizers - workshopenvironments/finalizers + - workshopsessions/finalizers verbs: - update - apiGroups: From 0e680fef70c78d7d95c1a18d925b27f0f5dd298f Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Thu, 21 Sep 2023 07:46:39 +1000 Subject: [PATCH 2/5] Change default loopback hostname so uses cluster domain variable. --- client-programs/pkg/cmd/cluster_workshop_serve_cmd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go index b0a92f02..31e7e0f7 100644 --- a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go @@ -275,7 +275,7 @@ func (p *ProjectInfo) NewClusterWorkshopServeCmd() *cobra.Command { c.Flags().StringVar( &o.ProxyHost, "proxy-host", - "loopback.default.svc.cluster.local", + "loopback.default.svc.$(cluster_domain)", "host by which any remote proxy will be accessed", ) c.Flags().IntVar( From afe34166135ee97d892cb320ddf3085692300485 Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Thu, 21 Sep 2023 09:53:23 +1000 Subject: [PATCH 3/5] Add user option for when requesting workshop using CLI to test REST API. --- .../pkg/cmd/cluster_workshop_request_cmd.go | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index d10e7bd7..639a2f7e 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -34,6 +34,7 @@ type ClusterWorkshopRequestOptions struct { ParamFiles []string ParamsFiles []string IndexUrl string + UserIdentity string WorkshopFile string WorkshopVersion string DataValuesFlags yttcmd.DataValuesFlags @@ -128,7 +129,7 @@ func (o *ClusterWorkshopRequestOptions) Run() error { // Request the workshop from the training portal. - err = requestWorkshop(dynamicClient, name, o.Portal, params, o.IndexUrl) + err = requestWorkshop(dynamicClient, name, o.Portal, params, o.IndexUrl, o.UserIdentity) if err != nil { return err @@ -152,7 +153,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { "name", "n", "", - "name to be used for the workshop definition, generated if not set", + "name of the workshop being requested, overrides derived workshop name", ) c.Flags().StringVarP( &o.Path, @@ -202,6 +203,13 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { "the URL to redirect to when workshop session is complete", ) + c.Flags().StringVar( + &o.UserIdentity, + "user", + "", + "the training portal user identifier", + ) + c.Flags().StringVar( &o.WorkshopFile, "workshop-file", @@ -257,7 +265,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { return c } -func requestWorkshop(client dynamic.Interface, name string, portal string, params map[string]string, indexUrl string) error { +func requestWorkshop(client dynamic.Interface, name string, portal string, params map[string]string, indexUrl string, user string) error { trainingPortalClient := client.Resource(trainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) @@ -481,7 +489,16 @@ func requestWorkshop(client dynamic.Interface, name string, portal string, param indexUrl = fmt.Sprintf("%s/accounts/logout/", portalUrl) } - requestURL = fmt.Sprintf("%s/workshops/environment/%s/request/?index_url=%s", portalUrl, environmentName, url.QueryEscape(indexUrl)) + queryString := url.Values{} + queryString.Add("index_url", indexUrl) + + if user != "" { + queryString.Add("user", user) + } + + fmt.Printf("Requesting workshop %q from training portal %q.\n", name, portal) + + requestURL = fmt.Sprintf("%s/workshops/environment/%s/request/?%s", portalUrl, environmentName, queryString.Encode()) req, err = http.NewRequest("POST", requestURL, bytes.NewBuffer(body)) @@ -520,9 +537,12 @@ func requestWorkshop(client dynamic.Interface, name string, portal string, param return errors.Wrap(err, "failed to decode response from training portal") } + fmt.Printf("Assigned training portal user %q.\n", requestWorkshopResult.User) + fmt.Printf("Workshop session name is %q.\n", requestWorkshopResult.Name) + workshopUrl := fmt.Sprintf("%s%s", portalUrl, requestWorkshopResult.URL) - fmt.Println(workshopUrl) + fmt.Printf("Opening workshop URL %s.\n", workshopUrl) switch runtime.GOOS { case "linux": From 7eff80139c73f670324ada9a19d197f27c6643ee Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Thu, 21 Sep 2023 17:49:02 +1000 Subject: [PATCH 4/5] Add request activation timeout and no browser option. --- .../pkg/cmd/cluster_workshop_request_cmd.go | 51 ++++++++++++++----- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index 639a2f7e..ba35dd98 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -26,18 +26,20 @@ import ( ) type ClusterWorkshopRequestOptions struct { - Name string - Path string - Kubeconfig string - Portal string - Params []string - ParamFiles []string - ParamsFiles []string - IndexUrl string - UserIdentity string - WorkshopFile string - WorkshopVersion string - DataValuesFlags yttcmd.DataValuesFlags + Name string + Path string + Kubeconfig string + Portal string + Params []string + ParamFiles []string + ParamsFiles []string + IndexUrl string + UserIdentity string + ActivationTimeout int + NoBrowser bool + WorkshopFile string + WorkshopVersion string + DataValuesFlags yttcmd.DataValuesFlags } func (o *ClusterWorkshopRequestOptions) Run() error { @@ -129,7 +131,7 @@ func (o *ClusterWorkshopRequestOptions) Run() error { // Request the workshop from the training portal. - err = requestWorkshop(dynamicClient, name, o.Portal, params, o.IndexUrl, o.UserIdentity) + err = requestWorkshop(dynamicClient, name, o.Portal, params, o.IndexUrl, o.UserIdentity, o.ActivationTimeout, o.NoBrowser) if err != nil { return err @@ -210,6 +212,20 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { "the training portal user identifier", ) + c.Flags().IntVar( + &o.ActivationTimeout, + "timeout", + 60, + "maximum time in seconds to activate the workshop", + ) + + c.Flags().BoolVar( + &o.NoBrowser, + "no-browser", + false, + "flag indicate whether to open browser automatically", + ) + c.Flags().StringVar( &o.WorkshopFile, "workshop-file", @@ -265,7 +281,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { return c } -func requestWorkshop(client dynamic.Interface, name string, portal string, params map[string]string, indexUrl string, user string) error { +func requestWorkshop(client dynamic.Interface, name string, portal string, params map[string]string, indexUrl string, user string, timeout int, noBrowser bool) error { trainingPortalClient := client.Resource(trainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) @@ -491,6 +507,7 @@ func requestWorkshop(client dynamic.Interface, name string, portal string, param queryString := url.Values{} queryString.Add("index_url", indexUrl) + queryString.Add("timeout", fmt.Sprintf("%d", timeout)) if user != "" { queryString.Add("user", user) @@ -542,6 +559,12 @@ func requestWorkshop(client dynamic.Interface, name string, portal string, param workshopUrl := fmt.Sprintf("%s%s", portalUrl, requestWorkshopResult.URL) + if noBrowser { + fmt.Printf("Workshop activation URL is %s.\n", workshopUrl) + + return nil + } + fmt.Printf("Opening workshop URL %s.\n", workshopUrl) switch runtime.GOOS { From 71b01f42395892cd030800111c313e36ca5df49f Mon Sep 17 00:00:00 2001 From: Graham Dumpleton Date: Thu, 21 Sep 2023 21:39:08 +1000 Subject: [PATCH 5/5] Add way to override environment name when requesting workshop session using CLI. --- .../pkg/cmd/cluster_workshop_request_cmd.go | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index ba35dd98..867b626b 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -35,6 +35,7 @@ type ClusterWorkshopRequestOptions struct { ParamsFiles []string IndexUrl string UserIdentity string + EnvironmentName string ActivationTimeout int NoBrowser bool WorkshopFile string @@ -131,7 +132,7 @@ func (o *ClusterWorkshopRequestOptions) Run() error { // Request the workshop from the training portal. - err = requestWorkshop(dynamicClient, name, o.Portal, params, o.IndexUrl, o.UserIdentity, o.ActivationTimeout, o.NoBrowser) + err = requestWorkshop(dynamicClient, name, o.EnvironmentName, o.Portal, params, o.IndexUrl, o.UserIdentity, o.ActivationTimeout, o.NoBrowser) if err != nil { return err @@ -219,6 +220,13 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { "maximum time in seconds to activate the workshop", ) + c.Flags().StringVar( + &o.EnvironmentName, + "environment-name", + "", + "workshop environment name, overrides derived environment name", + ) + c.Flags().BoolVar( &o.NoBrowser, "no-browser", @@ -281,7 +289,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { return c } -func requestWorkshop(client dynamic.Interface, name string, portal string, params map[string]string, indexUrl string, user string, timeout int, noBrowser bool) error { +func requestWorkshop(client dynamic.Interface, name string, environmentName string, portal string, params map[string]string, indexUrl string, user string, timeout int, noBrowser bool) error { trainingPortalClient := client.Resource(trainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) @@ -455,11 +463,11 @@ func requestWorkshop(client dynamic.Interface, name string, portal string, param // Work out the name of the workshop environment. - environmentName := "" - - for _, item := range listEnvironmentsResult.Environments { - if item.Workshop.Name == name && item.State == "RUNNING" { - environmentName = item.Name + if environmentName == "" { + for _, item := range listEnvironmentsResult.Environments { + if item.Workshop.Name == name && item.State == "RUNNING" { + environmentName = item.Name + } } }