Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions workspaces/backend/api/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,12 +245,19 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
VolumeMounts: kubefloworgv1beta1.WorkspaceKindVolumeMounts{
Home: "/home/jovyan",
},
HTTPProxy: &kubefloworgv1beta1.HTTPProxy{
RemovePathPrefix: ptr.To(false),
RequestHeaders: &kubefloworgv1beta1.IstioHeaderOperations{
Set: map[string]string{"X-RStudio-Root-Path": "{{ .PathPrefix }}"},
Add: map[string]string{},
Remove: []string{},
Ports: []kubefloworgv1beta1.WorkspaceKindPort{
{
Id: "jupyterlab",
DefaultDisplayName: "JupyterLab",
Protocol: "HTTP",
HTTPProxy: &kubefloworgv1beta1.HTTPProxy{
RemovePathPrefix: ptr.To(false),
RequestHeaders: &kubefloworgv1beta1.IstioHeaderOperations{
Set: map[string]string{},
Add: map[string]string{},
Remove: []string{},
},
},
},
},
ExtraEnv: []v1.EnvVar{
Expand Down Expand Up @@ -317,9 +324,8 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
Ports: []kubefloworgv1beta1.ImagePort{
{
Id: "jupyterlab",
DisplayName: "JupyterLab",
DisplayName: ptr.To("JupyterLab"),
Port: 8888,
Protocol: "HTTP",
},
},
},
Expand All @@ -342,9 +348,8 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
Ports: []kubefloworgv1beta1.ImagePort{
{
Id: "jupyterlab",
DisplayName: "JupyterLab",
DisplayName: ptr.To("JupyterLab"),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be testing with at least one of these being nil, so that we use the default.

Port: 8888,
Protocol: "HTTP",
},
},
},
Expand Down
1 change: 0 additions & 1 deletion workspaces/backend/api/workspacekinds_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,6 @@ spec:
- id: "jupyterlab"
displayName: "JupyterLab"
port: 8888
protocol: "HTTP"
podConfig:
spawner:
default: "tiny_cpu"
Expand Down
9 changes: 3 additions & 6 deletions workspaces/backend/internal/models/workspaces/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -340,12 +340,9 @@ func buildServices(ws *kubefloworgv1beta1.Workspace, imageConfigValue *kubeflowo
services := make([]Service, len(imageConfigValue.Spec.Ports))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to first build a map from portId -> port config (from the outer workspace kind ports), so that we can look it up when processing each of the ports.

for i := range imageConfigValue.Spec.Ports {
port := imageConfigValue.Spec.Ports[i]
switch port.Protocol { //nolint:gocritic
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should keep this switch statement, but instead switch on the protocol from the outer workspace kind ports list.

case kubefloworgv1beta1.ImagePortProtocolHTTP:
services[i].HttpService = &HttpService{
DisplayName: port.DisplayName,
HttpPath: fmt.Sprintf("/workspace/%s/%s/%s/", ws.Namespace, ws.Name, port.Id),
}
services[i].HttpService = &HttpService{
DisplayName: *port.DisplayName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will null pointer deref error, you should use something like ptr.Deref(port.DisplayName, default DisplayNameForThisPort)

HttpPath: fmt.Sprintf("/workspace/%s/%s/%s/", ws.Namespace, ws.Name, port.Id),
}
}

Expand Down
47 changes: 35 additions & 12 deletions workspaces/controller/api/v1beta1/workspacekind_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ import (
===============================================================================
*/

// PortId the id of the port
//
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=32
// +kubebuilder:validation:Pattern:=^[a-z0-9][a-z0-9_-]*[a-z0-9]$
type PortId string

// WorkspaceKindSpec defines the desired state of WorkspaceKind
type WorkspaceKindSpec struct {

Expand Down Expand Up @@ -115,9 +122,11 @@ type WorkspaceKindPodTemplate struct {
// volume mount paths
VolumeMounts WorkspaceKindVolumeMounts `json:"volumeMounts"`

// http proxy configs (MUTABLE)
// ports that the container listens on
// +kubebuilder:validation:Optional
HTTPProxy *HTTPProxy `json:"httpProxy,omitempty"`
// +listType:="map"
// +listMapKey:="id"
Ports []WorkspaceKindPort `json:"ports,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity - what is the practical purpose of defining a WorkspaceKind with no Ports ? Why would someone want to do that ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great question , we should rethink this 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should require at least one port, just so that the frontend does not have to deal with the possibility of a workspace with no ports.

+kubebuilder:validation:MinItems:=1


// environment variables for Workspace Pods (MUTABLE)
// - the following go template functions are available:
Expand Down Expand Up @@ -151,6 +160,27 @@ type WorkspaceKindPodTemplate struct {
Options WorkspaceKindPodOptions `json:"options"`
}

type WorkspaceKindPort struct {
// the id of the port
// - identifier for the port in `imageconfig` ports.[].id
// +kubebuilder:example="jupyterlab"
Id PortId `json:"id"`

// the protocol of the port
// +kubebuilder:example:="HTTP"
Protocol ImagePortProtocol `json:"protocol"`

// the display name of the port
// +kubebuilder:validation:MinLength:=2
// +kubebuilder:validation:MaxLength:=64
// +kubebuilder:example:="JupyterLab"
DefaultDisplayName string `json:"displayName"`

// the http proxy config for the port (MUTABLE)
// +kubebuilder:validation:Optional
HTTPProxy *HTTPProxy `json:"httpProxy,omitempty"`
}

type WorkspaceKindPodMetadata struct {
// labels to be applied to the Pod resource
// +kubebuilder:validation:Optional
Expand Down Expand Up @@ -339,11 +369,8 @@ type ImageConfigSpec struct {
type ImagePort struct {
// the id of the port
// - this is NOT used as the Container or Service port name, but as part of the HTTP path
// +kubebuilder:validation:MinLength:=1
// +kubebuilder:validation:MaxLength:=32
// +kubebuilder:validation:Pattern:=^[a-z0-9][a-z0-9_-]*[a-z0-9]$
// +kubebuilder:example="jupyterlab"
Id string `json:"id"`
Id PortId `json:"id"`

// the port number
// +kubebuilder:validation:Minimum:=1
Expand All @@ -354,12 +381,8 @@ type ImagePort struct {
// the display name of the port
// +kubebuilder:validation:MinLength:=2
// +kubebuilder:validation:MaxLength:=64
// +kubebuilder:example:="JupyterLab"
DisplayName string `json:"displayName"`

// the protocol of the port
// +kubebuilder:example:="HTTP"
Protocol ImagePortProtocol `json:"protocol"`
// +kubebuilder:validation:Optional
DisplayName *string `json:"displayName,omitempty"`
}

// +kubebuilder:validation:Enum:={"HTTP"}
Expand Down
39 changes: 34 additions & 5 deletions workspaces/controller/api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -2275,51 +2275,6 @@ spec:
x-kubernetes-list-map-keys:
- name
x-kubernetes-list-type: map
httpProxy:
description: http proxy configs (MUTABLE)
properties:
removePathPrefix:
default: false
description: |-
if the path prefix is stripped from incoming HTTP requests
- if true, the '/workspace/{profile_name}/{workspace_name}/' path prefix
is stripped from incoming requests, the application sees the request
as if it was made to '/...'
- this only works if the application serves RELATIVE URLs for its assets
type: boolean
requestHeaders:
description: |-
header manipulation rules for incoming HTTP requests
- sets the `spec.http[].headers.request` of the Istio VirtualService
https://istio.io/latest/docs/reference/config/networking/virtual-service/#Headers-HeaderOperations
- the following string templates are available:
- `.PathPrefix`: the path prefix of the Workspace (e.g. '/workspace/{profile_name}/{workspace_name}/')
properties:
add:
additionalProperties:
type: string
description: append the given values to the headers specified
by keys (will create a comma-separated list of values)
example:
My-Header: value-to-append
type: object
remove:
description: remove the specified headers
example:
- Header-To-Remove
items:
type: string
type: array
set:
additionalProperties:
type: string
description: overwrite the headers specified by key with
the given values
example:
X-RStudio-Root-Path: '{{ .PathPrefix }}'
type: object
type: object
type: object
options:
description: options are the user-selectable fields, they determine
the PodSpec of the Workspace
Expand Down Expand Up @@ -2457,7 +2412,6 @@ spec:
properties:
displayName:
description: the display name of the port
example: JupyterLab
maxLength: 64
minLength: 2
type: string
Expand All @@ -2477,17 +2431,9 @@ spec:
maximum: 65535
minimum: 1
type: integer
protocol:
description: the protocol of the port
enum:
- HTTP
example: HTTP
type: string
required:
- displayName
- id
- port
- protocol
type: object
minItems: 1
type: array
Expand Down Expand Up @@ -3729,6 +3675,86 @@ spec:
description: labels to be applied to the Pod resource
type: object
type: object
ports:
description: ports that the container listens on
items:
properties:
displayName:
description: the display name of the port
example: JupyterLab
maxLength: 64
minLength: 2
type: string
httpProxy:
description: the http proxy config for the port (MUTABLE)
properties:
removePathPrefix:
default: false
description: |-
if the path prefix is stripped from incoming HTTP requests
- if true, the '/workspace/{profile_name}/{workspace_name}/' path prefix
is stripped from incoming requests, the application sees the request
as if it was made to '/...'
- this only works if the application serves RELATIVE URLs for its assets
type: boolean
requestHeaders:
description: |-
header manipulation rules for incoming HTTP requests
- sets the `spec.http[].headers.request` of the Istio VirtualService
https://istio.io/latest/docs/reference/config/networking/virtual-service/#Headers-HeaderOperations
- the following string templates are available:
- `.PathPrefix`: the path prefix of the Workspace (e.g. '/workspace/{profile_name}/{workspace_name}/')
properties:
add:
additionalProperties:
type: string
description: append the given values to the headers
specified by keys (will create a comma-separated
list of values)
example:
My-Header: value-to-append
type: object
remove:
description: remove the specified headers
example:
- Header-To-Remove
items:
type: string
type: array
set:
additionalProperties:
type: string
description: overwrite the headers specified by
key with the given values
example:
X-RStudio-Root-Path: '{{ .PathPrefix }}'
type: object
type: object
type: object
id:
description: |-
the id of the port
- identifier for the port in `imageconfig` ports.[].id
example: jupyterlab
maxLength: 32
minLength: 1
pattern: ^[a-z0-9][a-z0-9_-]*[a-z0-9]$
type: string
protocol:
description: the protocol of the port
enum:
- HTTP
example: HTTP
type: string
required:
- displayName
- id
- protocol
type: object
type: array
x-kubernetes-list-map-keys:
- id
x-kubernetes-list-type: map
probes:
description: standard probes to determine Container health (MUTABLE)
properties:
Expand Down
Loading