Skip to content
Merged
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
28 changes: 16 additions & 12 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 @@ -341,10 +347,8 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
Image: "ghcr.io/kubeflow/kubeflow/notebook-servers/jupyter-scipy:v1.9.0",
Ports: []kubefloworgv1beta1.ImagePort{
{
Id: "jupyterlab",
DisplayName: "JupyterLab",
Port: 8888,
Protocol: "HTTP",
Id: "jupyterlab",
Port: 8888,
},
},
},
Expand Down
5 changes: 4 additions & 1 deletion workspaces/backend/api/workspacekinds_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,10 @@ spec:
name: "default-editor"
volumeMounts:
home: "/home/jovyan"
ports:
- id: "jupyterlab"
defaultDisplayName: "JupyterLab"
protocol: "HTTP"
options:
imageConfig:
spawner:
Expand All @@ -299,7 +303,6 @@ spec:
- id: "jupyterlab"
displayName: "JupyterLab"
port: 8888
protocol: "HTTP"
podConfig:
spawner:
default: "tiny_cpu"
Expand Down
23 changes: 19 additions & 4 deletions workspaces/backend/internal/models/workspaces/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef

imageConfigModel, imageConfigValue := buildImageConfig(ws, wsk)
podConfigModel, _ := buildPodConfig(ws, wsk)
wskPodTemplatePorts := make(map[kubefloworgv1beta1.PortId]kubefloworgv1beta1.WorkspaceKindPort)
if wskExists(wsk) {
for _, port := range wsk.Spec.PodTemplate.Ports {
wskPodTemplatePorts[port.Id] = port
}
}

workspaceModel := Workspace{
Name: ws.Name,
Expand Down Expand Up @@ -128,7 +134,7 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef
// https://github.com/kubeflow/notebooks/issues/38
LastProbe: nil,
},
Services: buildServices(ws, imageConfigValue),
Services: buildServices(ws, wskPodTemplatePorts, imageConfigValue),
}
return workspaceModel
}
Expand Down Expand Up @@ -332,18 +338,27 @@ func buildRedirectMessage(msg *kubefloworgv1beta1.RedirectMessage) *RedirectMess
}
}

func buildServices(ws *kubefloworgv1beta1.Workspace, imageConfigValue *kubefloworgv1beta1.ImageConfigValue) []Service {
func buildServices(ws *kubefloworgv1beta1.Workspace, wskPodTemplatePorts map[kubefloworgv1beta1.PortId]kubefloworgv1beta1.WorkspaceKindPort, imageConfigValue *kubefloworgv1beta1.ImageConfigValue) []Service {
if imageConfigValue == nil {
return nil
}

services := make([]Service, len(imageConfigValue.Spec.Ports))
for i := range imageConfigValue.Spec.Ports {
port := imageConfigValue.Spec.Ports[i]
switch port.Protocol { //nolint:gocritic

// Check if the port ID exists in the workspace kind
wskPort, exists := wskPodTemplatePorts[port.Id]
if !exists {
panic(fmt.Sprintf("workspace portID \"%q\" does not exist in the workspace kind", port.Id))
}

protocol := wskPort.Protocol
// golint complains about the single case in switch statement
switch protocol { //nolint:gocritic
case kubefloworgv1beta1.ImagePortProtocolHTTP:
services[i].HttpService = &HttpService{
DisplayName: port.DisplayName,
DisplayName: ptr.Deref(port.DisplayName, wskPort.DefaultDisplayName),
HttpPath: fmt.Sprintf("/workspace/%s/%s/%s/", ws.Namespace, ws.Name, port.Id),
}
}
Expand Down
54 changes: 41 additions & 13 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 (
===============================================================================
*/

// the id of a 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,15 @@ type WorkspaceKindPodTemplate struct {
// volume mount paths
VolumeMounts WorkspaceKindVolumeMounts `json:"volumeMounts"`

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

// environment variables for Workspace Pods (MUTABLE)
// - the following go template functions are available:
Expand Down Expand Up @@ -151,6 +164,28 @@ 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 default display name of the port
// - note, this can be overridden on a per image config value basis
// +kubebuilder:validation:MinLength:=2
// +kubebuilder:validation:MaxLength:=64
// +kubebuilder:example:="JupyterLab"
DefaultDisplayName string `json:"defaultDisplayName"`

// 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 +374,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 +386,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.

Loading