Skip to content

Commit 12c994f

Browse files
authored
feat(ws): add spec.podTemplate.ports[] to WorkspaceKind (#507)
* include ports array in podtemplate for httpProxy setting - moved protocal from imageconfig.spec.ports to podtemplates.ports - included the podtemplates.ports with defaultdisplayname - add validation webhook for podtemplate.ports - update the sample workspacekind with ports reference - referencing same id for portid in imageconfig and podtemplate.ports Signed-off-by: Harshad Reddy Nalla <hnalla@redhat.com> * Update the field description in workspacekind CRD - adjusted method buildService with details from ports Signed-off-by: Harshad Reddy Nalla <hnalla@redhat.com> * panic if port.id not present in buildService Signed-off-by: Harshad Reddy Nalla <hnalla@redhat.com> --------- Signed-off-by: Harshad Reddy Nalla <hnalla@redhat.com>
1 parent 1e5c049 commit 12c994f

File tree

13 files changed

+400
-162
lines changed

13 files changed

+400
-162
lines changed

workspaces/backend/api/suite_test.go

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,19 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
245245
VolumeMounts: kubefloworgv1beta1.WorkspaceKindVolumeMounts{
246246
Home: "/home/jovyan",
247247
},
248-
HTTPProxy: &kubefloworgv1beta1.HTTPProxy{
249-
RemovePathPrefix: ptr.To(false),
250-
RequestHeaders: &kubefloworgv1beta1.IstioHeaderOperations{
251-
Set: map[string]string{"X-RStudio-Root-Path": "{{ .PathPrefix }}"},
252-
Add: map[string]string{},
253-
Remove: []string{},
248+
Ports: []kubefloworgv1beta1.WorkspaceKindPort{
249+
{
250+
Id: "jupyterlab",
251+
DefaultDisplayName: "JupyterLab",
252+
Protocol: "HTTP",
253+
HTTPProxy: &kubefloworgv1beta1.HTTPProxy{
254+
RemovePathPrefix: ptr.To(false),
255+
RequestHeaders: &kubefloworgv1beta1.IstioHeaderOperations{
256+
Set: map[string]string{},
257+
Add: map[string]string{},
258+
Remove: []string{},
259+
},
260+
},
254261
},
255262
},
256263
ExtraEnv: []v1.EnvVar{
@@ -317,9 +324,8 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
317324
Ports: []kubefloworgv1beta1.ImagePort{
318325
{
319326
Id: "jupyterlab",
320-
DisplayName: "JupyterLab",
327+
DisplayName: ptr.To("JupyterLab"),
321328
Port: 8888,
322-
Protocol: "HTTP",
323329
},
324330
},
325331
},
@@ -341,10 +347,8 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
341347
Image: "ghcr.io/kubeflow/kubeflow/notebook-servers/jupyter-scipy:v1.9.0",
342348
Ports: []kubefloworgv1beta1.ImagePort{
343349
{
344-
Id: "jupyterlab",
345-
DisplayName: "JupyterLab",
346-
Port: 8888,
347-
Protocol: "HTTP",
350+
Id: "jupyterlab",
351+
Port: 8888,
348352
},
349353
},
350354
},

workspaces/backend/api/workspacekinds_handler_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,10 @@ spec:
283283
name: "default-editor"
284284
volumeMounts:
285285
home: "/home/jovyan"
286+
ports:
287+
- id: "jupyterlab"
288+
defaultDisplayName: "JupyterLab"
289+
protocol: "HTTP"
286290
options:
287291
imageConfig:
288292
spawner:
@@ -299,7 +303,6 @@ spec:
299303
- id: "jupyterlab"
300304
displayName: "JupyterLab"
301305
port: 8888
302-
protocol: "HTTP"
303306
podConfig:
304307
spawner:
305308
default: "tiny_cpu"

workspaces/backend/internal/models/workspaces/funcs.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,12 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef
9191

9292
imageConfigModel, imageConfigValue := buildImageConfig(ws, wsk)
9393
podConfigModel, _ := buildPodConfig(ws, wsk)
94+
wskPodTemplatePorts := make(map[kubefloworgv1beta1.PortId]kubefloworgv1beta1.WorkspaceKindPort)
95+
if wskExists(wsk) {
96+
for _, port := range wsk.Spec.PodTemplate.Ports {
97+
wskPodTemplatePorts[port.Id] = port
98+
}
99+
}
94100

95101
workspaceModel := Workspace{
96102
Name: ws.Name,
@@ -128,7 +134,7 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef
128134
// https://github.com/kubeflow/notebooks/issues/38
129135
LastProbe: nil,
130136
},
131-
Services: buildServices(ws, imageConfigValue),
137+
Services: buildServices(ws, wskPodTemplatePorts, imageConfigValue),
132138
}
133139
return workspaceModel
134140
}
@@ -332,18 +338,27 @@ func buildRedirectMessage(msg *kubefloworgv1beta1.RedirectMessage) *RedirectMess
332338
}
333339
}
334340

335-
func buildServices(ws *kubefloworgv1beta1.Workspace, imageConfigValue *kubefloworgv1beta1.ImageConfigValue) []Service {
341+
func buildServices(ws *kubefloworgv1beta1.Workspace, wskPodTemplatePorts map[kubefloworgv1beta1.PortId]kubefloworgv1beta1.WorkspaceKindPort, imageConfigValue *kubefloworgv1beta1.ImageConfigValue) []Service {
336342
if imageConfigValue == nil {
337343
return nil
338344
}
339345

340346
services := make([]Service, len(imageConfigValue.Spec.Ports))
341347
for i := range imageConfigValue.Spec.Ports {
342348
port := imageConfigValue.Spec.Ports[i]
343-
switch port.Protocol { //nolint:gocritic
349+
350+
// Check if the port ID exists in the workspace kind
351+
wskPort, exists := wskPodTemplatePorts[port.Id]
352+
if !exists {
353+
panic(fmt.Sprintf("workspace portID \"%q\" does not exist in the workspace kind", port.Id))
354+
}
355+
356+
protocol := wskPort.Protocol
357+
// golint complains about the single case in switch statement
358+
switch protocol { //nolint:gocritic
344359
case kubefloworgv1beta1.ImagePortProtocolHTTP:
345360
services[i].HttpService = &HttpService{
346-
DisplayName: port.DisplayName,
361+
DisplayName: ptr.Deref(port.DisplayName, wskPort.DefaultDisplayName),
347362
HttpPath: fmt.Sprintf("/workspace/%s/%s/%s/", ws.Namespace, ws.Name, port.Id),
348363
}
349364
}

workspaces/controller/api/v1beta1/workspacekind_types.go

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ import (
2929
===============================================================================
3030
*/
3131

32+
// the id of a port
33+
//
34+
// +kubebuilder:validation:MinLength:=1
35+
// +kubebuilder:validation:MaxLength:=32
36+
// +kubebuilder:validation:Pattern:=^[a-z0-9][a-z0-9_-]*[a-z0-9]$
37+
type PortId string
38+
3239
// WorkspaceKindSpec defines the desired state of WorkspaceKind
3340
type WorkspaceKindSpec struct {
3441

@@ -115,9 +122,15 @@ type WorkspaceKindPodTemplate struct {
115122
// volume mount paths
116123
VolumeMounts WorkspaceKindVolumeMounts `json:"volumeMounts"`
117124

118-
// http proxy configs (MUTABLE)
119-
// +kubebuilder:validation:Optional
120-
HTTPProxy *HTTPProxy `json:"httpProxy,omitempty"`
125+
// port definitions which can be referenced in image config values
126+
// - think of port definitions as the "types" of services which could be provided by a specific image
127+
// - a port definition has a common id (URL path) for consistency if the listening TCP port changes
128+
// - ports are referenced in image config values by their `id` and their definition here establishes
129+
// their protocol type, and default display name in the UI
130+
// +kubebuilder:validation:MinItems:=1
131+
// +listType:="map"
132+
// +listMapKey:="id"
133+
Ports []WorkspaceKindPort `json:"ports"`
121134

122135
// environment variables for Workspace Pods (MUTABLE)
123136
// - the following go template functions are available:
@@ -151,6 +164,28 @@ type WorkspaceKindPodTemplate struct {
151164
Options WorkspaceKindPodOptions `json:"options"`
152165
}
153166

167+
type WorkspaceKindPort struct {
168+
// the id of the port
169+
// - identifier for the port in `imageconfig` ports.[].id
170+
// +kubebuilder:example="jupyterlab"
171+
Id PortId `json:"id"`
172+
173+
// the protocol of the port
174+
// +kubebuilder:example:="HTTP"
175+
Protocol ImagePortProtocol `json:"protocol"`
176+
177+
// the default display name of the port
178+
// - note, this can be overridden on a per image config value basis
179+
// +kubebuilder:validation:MinLength:=2
180+
// +kubebuilder:validation:MaxLength:=64
181+
// +kubebuilder:example:="JupyterLab"
182+
DefaultDisplayName string `json:"defaultDisplayName"`
183+
184+
// the http proxy config for the port (MUTABLE)
185+
// +kubebuilder:validation:Optional
186+
HTTPProxy *HTTPProxy `json:"httpProxy,omitempty"`
187+
}
188+
154189
type WorkspaceKindPodMetadata struct {
155190
// labels to be applied to the Pod resource
156191
// +kubebuilder:validation:Optional
@@ -339,11 +374,8 @@ type ImageConfigSpec struct {
339374
type ImagePort struct {
340375
// the id of the port
341376
// - this is NOT used as the Container or Service port name, but as part of the HTTP path
342-
// +kubebuilder:validation:MinLength:=1
343-
// +kubebuilder:validation:MaxLength:=32
344-
// +kubebuilder:validation:Pattern:=^[a-z0-9][a-z0-9_-]*[a-z0-9]$
345377
// +kubebuilder:example="jupyterlab"
346-
Id string `json:"id"`
378+
Id PortId `json:"id"`
347379

348380
// the port number
349381
// +kubebuilder:validation:Minimum:=1
@@ -354,12 +386,8 @@ type ImagePort struct {
354386
// the display name of the port
355387
// +kubebuilder:validation:MinLength:=2
356388
// +kubebuilder:validation:MaxLength:=64
357-
// +kubebuilder:example:="JupyterLab"
358-
DisplayName string `json:"displayName"`
359-
360-
// the protocol of the port
361-
// +kubebuilder:example:="HTTP"
362-
Protocol ImagePortProtocol `json:"protocol"`
389+
// +kubebuilder:validation:Optional
390+
DisplayName *string `json:"displayName,omitempty"`
363391
}
364392

365393
// +kubebuilder:validation:Enum:={"HTTP"}

workspaces/controller/api/v1beta1/zz_generated.deepcopy.go

Lines changed: 34 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)