diff --git a/workspaces/backend/api/workspaces_handler_test.go b/workspaces/backend/api/workspaces_handler_test.go index b224904fc..84e6919b9 100644 --- a/workspaces/backend/api/workspaces_handler_test.go +++ b/workspaces/backend/api/workspaces_handler_test.go @@ -479,7 +479,6 @@ var _ = Describe("Workspaces Handler", func() { By("ensuring the model for Workspace with missing WorkspaceKind is as expected") workspaceMissingWskModel := models.NewWorkspaceListItemFromWorkspace(a.Config, workspaceMissingWsk, nil) Expect(workspaceMissingWskModel.WorkspaceKind.Missing).To(BeTrue()) - Expect(workspaceMissingWskModel.PodTemplate.Volumes.Home.MountPath).To(Equal(models.UnknownHomeMountPath)) Expect(workspaceMissingWskModel.PodTemplate.Options.PodConfig.Current.DisplayName).To(Equal(models.UnknownPodConfig)) Expect(workspaceMissingWskModel.PodTemplate.Options.PodConfig.Current.Description).To(Equal(models.UnknownPodConfig)) Expect(workspaceMissingWskModel.PodTemplate.Options.ImageConfig.Current.DisplayName).To(Equal(models.UnknownImageConfig)) diff --git a/workspaces/backend/internal/models/workspaces/common/funcs.go b/workspaces/backend/internal/models/workspaces/common/funcs.go index c8fb18461..8aaa49d56 100644 --- a/workspaces/backend/internal/models/workspaces/common/funcs.go +++ b/workspaces/backend/internal/models/workspaces/common/funcs.go @@ -20,7 +20,6 @@ import ( "maps" kubefloworgv1beta1 "github.com/kubeflow/notebooks/workspaces/controller/api/v1beta1" - "k8s.io/utils/ptr" ) // WskExists checks if a WorkspaceKind is non-nil and has a valid UID. @@ -42,59 +41,9 @@ func ExtractPodMetadata(ws *kubefloworgv1beta1.Workspace) PodMetadata { } } -// BuildDataVolumes creates a PodVolumeInfo slice from a workspace's data volumes. -func BuildDataVolumes(ws *kubefloworgv1beta1.Workspace) []PodVolumeInfo { - var dataVolumes []PodVolumeInfo - if len(ws.Spec.PodTemplate.Volumes.Data) > 0 { - dataVolumes = make([]PodVolumeInfo, 0, len(ws.Spec.PodTemplate.Volumes.Data)) - for _, v := range ws.Spec.PodTemplate.Volumes.Data { - dataVolumes = append(dataVolumes, PodVolumeInfo{ - PVCName: v.PVCName, - MountPath: v.MountPath, - ReadOnly: ptr.Deref(v.ReadOnly, false), - }) - } - } - return dataVolumes -} - -// BuildSecretVolumes creates a PodSecretInfo slice from a workspace's secret volumes. -func BuildSecretVolumes(ws *kubefloworgv1beta1.Workspace) []PodSecretInfo { - var secretVolumes []PodSecretInfo - if len(ws.Spec.PodTemplate.Volumes.Secrets) > 0 { - secretVolumes = make([]PodSecretInfo, len(ws.Spec.PodTemplate.Volumes.Secrets)) - for i, s := range ws.Spec.PodTemplate.Volumes.Secrets { - secretVolumes[i] = PodSecretInfo{ - SecretName: s.SecretName, - MountPath: s.MountPath, - DefaultMode: s.DefaultMode, - } - } - } - return secretVolumes -} - // EnsureWskMatchesWorkspace panics if the provided WorkspaceKind exists but does not match the Workspace. func EnsureWskMatchesWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubefloworgv1beta1.WorkspaceKind) { if WskExists(wsk) && ws.Spec.Kind != wsk.Name { panic("provided WorkspaceKind does not match the Workspace") } } - -// BuildHomeVolume creates a PodVolumeInfo for the workspace's home volume. -func BuildHomeVolume(ws *kubefloworgv1beta1.Workspace, wsk *kubefloworgv1beta1.WorkspaceKind) *PodVolumeInfo { - if ws.Spec.PodTemplate.Volumes.Home == nil { - return nil - } - - mountPath := UnknownHomeMountPath - if WskExists(wsk) { - mountPath = wsk.Spec.PodTemplate.VolumeMounts.Home - } - - return &PodVolumeInfo{ - PVCName: *ws.Spec.PodTemplate.Volumes.Home, - MountPath: mountPath, - ReadOnly: false, - } -} diff --git a/workspaces/backend/internal/models/workspaces/common/types.go b/workspaces/backend/internal/models/workspaces/common/types.go index cef5ee835..b760a828c 100644 --- a/workspaces/backend/internal/models/workspaces/common/types.go +++ b/workspaces/backend/internal/models/workspaces/common/types.go @@ -16,21 +16,7 @@ limitations under the License. package common -const UnknownHomeMountPath = "__UNKNOWN_HOME_MOUNT_PATH__" - type PodMetadata struct { Labels map[string]string `json:"labels"` Annotations map[string]string `json:"annotations"` } - -type PodVolumeInfo struct { - PVCName string `json:"pvcName"` - MountPath string `json:"mountPath"` - ReadOnly bool `json:"readOnly"` -} - -type PodSecretInfo struct { - SecretName string `json:"secretName"` - MountPath string `json:"mountPath"` - DefaultMode int32 `json:"defaultMode,omitempty"` -} diff --git a/workspaces/backend/internal/models/workspaces/funcs.go b/workspaces/backend/internal/models/workspaces/funcs.go index 1d608a096..65761227a 100644 --- a/workspaces/backend/internal/models/workspaces/funcs.go +++ b/workspaces/backend/internal/models/workspaces/funcs.go @@ -29,11 +29,10 @@ import ( ) const ( - UnknownHomeMountPath = commonWorkspaces.UnknownHomeMountPath - UnknownImageConfig = "__UNKNOWN_IMAGE_CONFIG__" - UnknownPodConfig = "__UNKNOWN_POD_CONFIG__" - UnknownIconURL = "__UNKNOWN_ICON_URL__" - UnknownLogoURL = "__UNKNOWN_LOGO_URL__" + UnknownImageConfig = "__UNKNOWN_IMAGE_CONFIG__" + UnknownPodConfig = "__UNKNOWN_POD_CONFIG__" + UnknownIconURL = "__UNKNOWN_ICON_URL__" + UnknownLogoURL = "__UNKNOWN_LOGO_URL__" ) // NewWorkspaceListItemFromWorkspace creates a WorkspaceListItem model from a Workspace and WorkspaceKind object. @@ -41,9 +40,6 @@ const ( func NewWorkspaceListItemFromWorkspace(cfg *config.EnvConfig, ws *kubefloworgv1beta1.Workspace, wsk *kubefloworgv1beta1.WorkspaceKind) WorkspaceListItem { commonWorkspaces.EnsureWskMatchesWorkspace(ws, wsk) - podMetadata := commonWorkspaces.ExtractPodMetadata(ws) - dataVolumes := commonWorkspaces.BuildDataVolumes(ws) - imageConfigModel, imageConfigValue := buildImageConfig(ws, wsk) podConfigModel, _ := buildPodConfig(ws, wsk) wskPodTemplatePorts := make(map[kubefloworgv1beta1.PortId]kubefloworgv1beta1.WorkspaceKindPort) @@ -62,17 +58,11 @@ func NewWorkspaceListItemFromWorkspace(cfg *config.EnvConfig, ws *kubefloworgv1b Icon: buildIconImageRef(cfg, ws, wsk), Logo: buildLogoImageRef(cfg, ws, wsk), }, - Paused: ptr.Deref(ws.Spec.Paused, false), - PausedTime: ws.Status.PauseTime, - PendingRestart: ws.Status.PendingRestart, - State: ws.Status.State, - StateMessage: ws.Status.StateMessage, + Paused: ptr.Deref(ws.Spec.Paused, false), + PausedTime: ws.Status.PauseTime, + State: ws.Status.State, + StateMessage: ws.Status.StateMessage, PodTemplate: PodTemplate{ - PodMetadata: podMetadata, - Volumes: PodVolumes{ - Home: commonWorkspaces.BuildHomeVolume(ws, wsk), - Data: dataVolumes, - }, Options: PodTemplateOptions{ ImageConfig: imageConfigModel, PodConfig: podConfigModel, diff --git a/workspaces/backend/internal/models/workspaces/podtemplate/details/funcs.go b/workspaces/backend/internal/models/workspaces/podtemplate/details/funcs.go index 89a56e86b..ffbc21bae 100644 --- a/workspaces/backend/internal/models/workspaces/podtemplate/details/funcs.go +++ b/workspaces/backend/internal/models/workspaces/podtemplate/details/funcs.go @@ -17,6 +17,8 @@ limitations under the License. package details import ( + "k8s.io/utils/ptr" + kubefloworgv1beta1 "github.com/kubeflow/notebooks/workspaces/controller/api/v1beta1" commonWorkspaces "github.com/kubeflow/notebooks/workspaces/backend/internal/models/workspaces/common" @@ -55,10 +57,60 @@ func NewWorkspaceDetailsFromWorkspace( return WorkspaceDetails{ PodMetadata: commonWorkspaces.ExtractPodMetadata(ws), Volumes: WorkspaceDetailVolumes{ - Home: commonWorkspaces.BuildHomeVolume(ws, wsk), - Data: commonWorkspaces.BuildDataVolumes(ws), - Secrets: commonWorkspaces.BuildSecretVolumes(ws), + Home: BuildHomeVolume(ws, wsk), + Data: BuildDataVolumes(ws), + Secrets: BuildSecretVolumes(ws), }, Pod: pod, } } + +// BuildHomeVolume creates a PodVolumeInfo for the workspace's home volume. +func BuildHomeVolume(ws *kubefloworgv1beta1.Workspace, wsk *kubefloworgv1beta1.WorkspaceKind) *PodVolumeInfo { + if ws.Spec.PodTemplate.Volumes.Home == nil { + return nil + } + + mountPath := UnknownHomeMountPath + if commonWorkspaces.WskExists(wsk) { + mountPath = wsk.Spec.PodTemplate.VolumeMounts.Home + } + + return &PodVolumeInfo{ + PVCName: *ws.Spec.PodTemplate.Volumes.Home, + MountPath: mountPath, + ReadOnly: false, + } +} + +// BuildDataVolumes creates a PodVolumeInfo slice from a workspace's data volumes. +func BuildDataVolumes(ws *kubefloworgv1beta1.Workspace) []PodVolumeInfo { + var dataVolumes []PodVolumeInfo + if len(ws.Spec.PodTemplate.Volumes.Data) > 0 { + dataVolumes = make([]PodVolumeInfo, 0, len(ws.Spec.PodTemplate.Volumes.Data)) + for _, v := range ws.Spec.PodTemplate.Volumes.Data { + dataVolumes = append(dataVolumes, PodVolumeInfo{ + PVCName: v.PVCName, + MountPath: v.MountPath, + ReadOnly: ptr.Deref(v.ReadOnly, false), + }) + } + } + return dataVolumes +} + +// BuildSecretVolumes creates a PodSecretInfo slice from a workspace's secret volumes. +func BuildSecretVolumes(ws *kubefloworgv1beta1.Workspace) []PodSecretInfo { + var secretVolumes []PodSecretInfo + if len(ws.Spec.PodTemplate.Volumes.Secrets) > 0 { + secretVolumes = make([]PodSecretInfo, len(ws.Spec.PodTemplate.Volumes.Secrets)) + for i, s := range ws.Spec.PodTemplate.Volumes.Secrets { + secretVolumes[i] = PodSecretInfo{ + SecretName: s.SecretName, + MountPath: s.MountPath, + DefaultMode: s.DefaultMode, + } + } + } + return secretVolumes +} diff --git a/workspaces/backend/internal/models/workspaces/podtemplate/details/types.go b/workspaces/backend/internal/models/workspaces/podtemplate/details/types.go index ed04a9ed1..093056d68 100644 --- a/workspaces/backend/internal/models/workspaces/podtemplate/details/types.go +++ b/workspaces/backend/internal/models/workspaces/podtemplate/details/types.go @@ -20,6 +20,8 @@ import ( commonWorkspaces "github.com/kubeflow/notebooks/workspaces/backend/internal/models/workspaces/common" ) +const UnknownHomeMountPath = "__UNKNOWN_HOME_MOUNT_PATH__" + type WorkspaceDetails struct { PodMetadata commonWorkspaces.PodMetadata `json:"podMetadata"` Volumes WorkspaceDetailVolumes `json:"volumes"` @@ -27,9 +29,9 @@ type WorkspaceDetails struct { } type WorkspaceDetailVolumes struct { - Home *commonWorkspaces.PodVolumeInfo `json:"home"` - Data []commonWorkspaces.PodVolumeInfo `json:"data,omitempty"` - Secrets []commonWorkspaces.PodSecretInfo `json:"secrets,omitempty"` + Home *PodVolumeInfo `json:"home"` + Data []PodVolumeInfo `json:"data,omitempty"` + Secrets []PodSecretInfo `json:"secrets,omitempty"` } type WorkspaceDetailPod struct { @@ -42,3 +44,15 @@ type WorkspaceDetailPod struct { type WorkspaceDetailContainer struct { Name string `json:"name"` } + +type PodVolumeInfo struct { + PVCName string `json:"pvcName"` + MountPath string `json:"mountPath"` + ReadOnly bool `json:"readOnly"` +} + +type PodSecretInfo struct { + SecretName string `json:"secretName"` + MountPath string `json:"mountPath"` + DefaultMode int32 `json:"defaultMode,omitempty"` +} diff --git a/workspaces/backend/internal/models/workspaces/types.go b/workspaces/backend/internal/models/workspaces/types.go index d08d72072..191b7d473 100644 --- a/workspaces/backend/internal/models/workspaces/types.go +++ b/workspaces/backend/internal/models/workspaces/types.go @@ -21,7 +21,6 @@ import ( commonCore "github.com/kubeflow/notebooks/workspaces/backend/internal/models/common" commonAssets "github.com/kubeflow/notebooks/workspaces/backend/internal/models/common/assets" - commonWorkspaces "github.com/kubeflow/notebooks/workspaces/backend/internal/models/workspaces/common" ) // WorkspaceListItem represents a workspace in the system, and is returned by LIST operations. @@ -29,18 +28,17 @@ import ( // TODO: we need to validate which fields should actually be returned in the response // - should only be returning fields relevant to the list view in the UI type WorkspaceListItem struct { - Name string `json:"name"` - Namespace string `json:"namespace"` - WorkspaceKind WorkspaceKindInfo `json:"workspaceKind"` - Paused bool `json:"paused"` - PausedTime int64 `json:"pausedTime"` - PendingRestart bool `json:"pendingRestart"` - State kubefloworgv1beta1.WorkspaceState `json:"state"` - StateMessage string `json:"stateMessage"` - PodTemplate PodTemplate `json:"podTemplate"` - Activity Activity `json:"activity"` - Services []Service `json:"services"` - Audit commonCore.Audit `json:"audit"` + Name string `json:"name"` + Namespace string `json:"namespace"` + WorkspaceKind WorkspaceKindInfo `json:"workspaceKind"` + Paused bool `json:"paused"` + PausedTime int64 `json:"pausedTime"` + State kubefloworgv1beta1.WorkspaceState `json:"state"` + StateMessage string `json:"stateMessage"` + PodTemplate PodTemplate `json:"podTemplate"` + Activity Activity `json:"activity"` + Services []Service `json:"services"` + Audit commonCore.Audit `json:"audit"` } type WorkspaceKindInfo struct { @@ -51,15 +49,7 @@ type WorkspaceKindInfo struct { } type PodTemplate struct { - PodMetadata commonWorkspaces.PodMetadata `json:"podMetadata"` - Volumes PodVolumes `json:"volumes"` - Options PodTemplateOptions `json:"options"` -} - -type PodVolumes struct { - Home *commonWorkspaces.PodVolumeInfo `json:"home,omitempty"` - Data []commonWorkspaces.PodVolumeInfo `json:"data,omitempty"` - Secrets []commonWorkspaces.PodSecretInfo `json:"secrets,omitempty"` + Options PodTemplateOptions `json:"options"` } type PodTemplateOptions struct { diff --git a/workspaces/backend/openapi/docs.go b/workspaces/backend/openapi/docs.go index ff25551eb..3328fb165 100644 --- a/workspaces/backend/openapi/docs.go +++ b/workspaces/backend/openapi/docs.go @@ -2363,7 +2363,21 @@ const docTemplate = `{ } } }, - "common.PodSecretInfo": { + "common.Restrictions": { + "type": "object", + "required": [ + "deny" + ], + "properties": { + "deny": { + "type": "boolean" + }, + "denyMessage": { + "$ref": "#/definitions/common.DenyMessage" + } + } + }, + "details.PodSecretInfo": { "type": "object", "required": [ "mountPath", @@ -2381,7 +2395,7 @@ const docTemplate = `{ } } }, - "common.PodVolumeInfo": { + "details.PodVolumeInfo": { "type": "object", "required": [ "mountPath", @@ -2400,20 +2414,6 @@ const docTemplate = `{ } } }, - "common.Restrictions": { - "type": "object", - "required": [ - "deny" - ], - "properties": { - "deny": { - "type": "boolean" - }, - "denyMessage": { - "$ref": "#/definitions/common.DenyMessage" - } - } - }, "details.WorkspaceDetailContainer": { "type": "object", "required": [ @@ -2461,16 +2461,16 @@ const docTemplate = `{ "data": { "type": "array", "items": { - "$ref": "#/definitions/common.PodVolumeInfo" + "$ref": "#/definitions/details.PodVolumeInfo" } }, "home": { - "$ref": "#/definitions/common.PodVolumeInfo" + "$ref": "#/definitions/details.PodVolumeInfo" }, "secrets": { "type": "array", "items": { - "$ref": "#/definitions/common.PodSecretInfo" + "$ref": "#/definitions/details.PodSecretInfo" } } } @@ -7306,19 +7306,11 @@ const docTemplate = `{ "workspaces.PodTemplate": { "type": "object", "required": [ - "options", - "podMetadata", - "volumes" + "options" ], "properties": { "options": { "$ref": "#/definitions/workspaces.PodTemplateOptions" - }, - "podMetadata": { - "$ref": "#/definitions/common.PodMetadata" - }, - "volumes": { - "$ref": "#/definitions/workspaces.PodVolumes" } } }, @@ -7389,26 +7381,6 @@ const docTemplate = `{ } } }, - "workspaces.PodVolumes": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/common.PodVolumeInfo" - } - }, - "home": { - "$ref": "#/definitions/common.PodVolumeInfo" - }, - "secrets": { - "type": "array", - "items": { - "$ref": "#/definitions/common.PodSecretInfo" - } - } - } - }, "workspaces.PodVolumesMutate": { "type": "object", "required": [ @@ -7554,7 +7526,6 @@ const docTemplate = `{ "namespace", "paused", "pausedTime", - "pendingRestart", "podTemplate", "services", "state", @@ -7580,9 +7551,6 @@ const docTemplate = `{ "pausedTime": { "type": "integer" }, - "pendingRestart": { - "type": "boolean" - }, "podTemplate": { "$ref": "#/definitions/workspaces.PodTemplate" }, diff --git a/workspaces/backend/openapi/swagger.json b/workspaces/backend/openapi/swagger.json index e4a7340e9..7c27cf757 100644 --- a/workspaces/backend/openapi/swagger.json +++ b/workspaces/backend/openapi/swagger.json @@ -2361,7 +2361,21 @@ } } }, - "common.PodSecretInfo": { + "common.Restrictions": { + "type": "object", + "required": [ + "deny" + ], + "properties": { + "deny": { + "type": "boolean" + }, + "denyMessage": { + "$ref": "#/definitions/common.DenyMessage" + } + } + }, + "details.PodSecretInfo": { "type": "object", "required": [ "mountPath", @@ -2379,7 +2393,7 @@ } } }, - "common.PodVolumeInfo": { + "details.PodVolumeInfo": { "type": "object", "required": [ "mountPath", @@ -2398,20 +2412,6 @@ } } }, - "common.Restrictions": { - "type": "object", - "required": [ - "deny" - ], - "properties": { - "deny": { - "type": "boolean" - }, - "denyMessage": { - "$ref": "#/definitions/common.DenyMessage" - } - } - }, "details.WorkspaceDetailContainer": { "type": "object", "required": [ @@ -2459,16 +2459,16 @@ "data": { "type": "array", "items": { - "$ref": "#/definitions/common.PodVolumeInfo" + "$ref": "#/definitions/details.PodVolumeInfo" } }, "home": { - "$ref": "#/definitions/common.PodVolumeInfo" + "$ref": "#/definitions/details.PodVolumeInfo" }, "secrets": { "type": "array", "items": { - "$ref": "#/definitions/common.PodSecretInfo" + "$ref": "#/definitions/details.PodSecretInfo" } } } @@ -7304,19 +7304,11 @@ "workspaces.PodTemplate": { "type": "object", "required": [ - "options", - "podMetadata", - "volumes" + "options" ], "properties": { "options": { "$ref": "#/definitions/workspaces.PodTemplateOptions" - }, - "podMetadata": { - "$ref": "#/definitions/common.PodMetadata" - }, - "volumes": { - "$ref": "#/definitions/workspaces.PodVolumes" } } }, @@ -7387,26 +7379,6 @@ } } }, - "workspaces.PodVolumes": { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/common.PodVolumeInfo" - } - }, - "home": { - "$ref": "#/definitions/common.PodVolumeInfo" - }, - "secrets": { - "type": "array", - "items": { - "$ref": "#/definitions/common.PodSecretInfo" - } - } - } - }, "workspaces.PodVolumesMutate": { "type": "object", "required": [ @@ -7552,7 +7524,6 @@ "namespace", "paused", "pausedTime", - "pendingRestart", "podTemplate", "services", "state", @@ -7578,9 +7549,6 @@ "pausedTime": { "type": "integer" }, - "pendingRestart": { - "type": "boolean" - }, "podTemplate": { "$ref": "#/definitions/workspaces.PodTemplate" },