From a38eda0fb89d1270b1dfcb2d9a72fb5c9baf91d1 Mon Sep 17 00:00:00 2001 From: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:00:07 -0700 Subject: [PATCH] feat(ws): update `Workspace` and `WorkspaceKind` definitions (#16) Signed-off-by: Mathew Wicks <5735406+thesuperzapper@users.noreply.github.com> --- .../controller/api/v1beta1/workspace_types.go | 176 +++--- .../api/v1beta1/workspacekind_types.go | 325 ++++++---- .../api/v1beta1/zz_generated.deepcopy.go | 560 ++++++++++-------- .../bases/kubeflow.org_workspacekinds.yaml | 218 ++++++- .../crd/bases/kubeflow.org_workspaces.yaml | 2 +- workspaces/controller/go.mod | 2 +- .../controller/workspace_controller_test.go | 8 +- .../workspacekind_controller_test.go | 28 +- 8 files changed, 861 insertions(+), 458 deletions(-) diff --git a/workspaces/controller/api/v1beta1/workspace_types.go b/workspaces/controller/api/v1beta1/workspace_types.go index 9570ad1f..0c760e81 100644 --- a/workspaces/controller/api/v1beta1/workspace_types.go +++ b/workspaces/controller/api/v1beta1/workspace_types.go @@ -21,7 +21,12 @@ import ( ) // Important: Run "make" to regenerate code after modifying this file -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +/* +=============================================================================== + Workspace - Spec +=============================================================================== +*/ // WorkspaceSpec defines the desired state of Workspace type WorkspaceSpec struct { @@ -41,76 +46,22 @@ type WorkspaceSpec struct { Kind string `json:"kind"` // options for "podTemplate"-type WorkspaceKinds - PodTemplate PodTemplate `json:"podTemplate"` -} - -// WorkspaceStatus defines the observed state of Workspace -type WorkspaceStatus struct { - - // information populated by activity probes, used to determine when to cull - Activity Activity `json:"activity"` - - // the time when the Workspace was paused, 0 if the Workspace is not paused - //+kubebuilder:example=1704067200 - PauseTime int64 `json:"pauseTime"` - - // if the current Pod does not reflect the current "desired" state (after redirects) - //+kubebuilder:example=false - PendingRestart bool `json:"pendingRestart"` - - // actual "target" podTemplateOptions, taking into account redirects - PodTemplateOptions Options `json:"podTemplateOptions"` - - // the current state of the Workspace - //+kubebuilder:validation:Enum:={"Running","Terminating","Paused","Pending","Error","Unknown"} - //+kubebuilder:example="Running" - State string `json:"state"` - - // a human-readable message about the state of the Workspace - // WARNING: this field is NOT FOR MACHINE USE, subject to change without notice - //+kubebuilder:example="Pod is not ready" - StateMessage string `json:"stateMessage"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The current state of the Workspace" -//+kubebuilder:subresource:status - -// Workspace is the Schema for the workspaces API -type Workspace struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec WorkspaceSpec `json:"spec,omitempty"` - Status WorkspaceStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// WorkspaceList contains a list of Workspace -type WorkspaceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Workspace `json:"items"` + PodTemplate WorkspacePodTemplate `json:"podTemplate"` } -func init() { - SchemeBuilder.Register(&Workspace{}, &WorkspaceList{}) -} - -type PodTemplate struct { +type WorkspacePodTemplate struct { // metadata to be applied to the Pod resource //+kubebuilder:validation:Optional - PodMetadata PodMetadata `json:"podMetadata,omitempty"` + PodMetadata WorkspacePodMetadata `json:"podMetadata,omitempty"` // volume configs - Volumes PodVolumes `json:"volumes"` + Volumes WorkspacePodVolumes `json:"volumes"` // spawner options, these are the user-selected options from the Workspace Spawner UI which determine the PodSpec of the Workspace Pod - Options Options `json:"options"` + Options WorkspacePodOptions `json:"options"` } -type PodMetadata struct { +type WorkspacePodMetadata struct { // labels to be applied to the Pod resource //+kubebuilder:validation:Optional Labels map[string]string `json:"labels,omitempty"` @@ -120,7 +71,7 @@ type PodMetadata struct { Annotations map[string]string `json:"annotations,omitempty"` } -type PodVolumes struct { +type WorkspacePodVolumes struct { // A PVC to mount as the home directory. // This PVC must already exist in the Namespace // This PVC must be RWX (ReadWriteMany, ReadWriteOnce) @@ -150,15 +101,7 @@ type PodVolumeMount struct { MountPath string `json:"mountPath"` } -type Activity struct { - //+kubebuilder:example=1704067200 - LastActivity int64 `json:"lastActivity"` - - //+kubebuilder:example=1704067200 - LastUpdate int64 `json:"lastUpdate"` -} - -type Options struct { +type WorkspacePodOptions struct { // the id of an image option // - options are defined in WorkspaceKind under // `spec.podTemplate.options.imageConfig.values[]` @@ -168,3 +111,94 @@ type Options struct { //+kubebuilder:example="big_gpu" PodConfig string `json:"podConfig"` } + +/* +=============================================================================== + Workspace - Status +=============================================================================== +*/ + +// WorkspaceStatus defines the observed state of Workspace +type WorkspaceStatus struct { + + // information populated by activity probes, used to determine when to cull + Activity WorkspaceActivity `json:"activity"` + + // the time when the Workspace was paused, 0 if the Workspace is not paused + //+kubebuilder:example=1704067200 + PauseTime int64 `json:"pauseTime"` + + // if the current Pod does not reflect the current "desired" state (after redirects) + //+kubebuilder:example=false + PendingRestart bool `json:"pendingRestart"` + + // actual "target" podTemplateOptions, taking into account redirects + PodTemplateOptions WorkspacePodOptions `json:"podTemplateOptions"` + + // the current state of the Workspace + //+kubebuilder:example="Running" + State WorkspaceState `json:"state"` + + // a human-readable message about the state of the Workspace + // WARNING: this field is NOT FOR MACHINE USE, subject to change without notice + //+kubebuilder:example="Pod is not ready" + StateMessage string `json:"stateMessage"` +} + +type WorkspaceActivity struct { + //+kubebuilder:example=1704067200 + LastActivity int64 `json:"lastActivity"` + + //+kubebuilder:example=1704067200 + LastUpdate int64 `json:"lastUpdate"` +} + +// +kubebuilder:validation:Enum:={"Running","Terminating","Paused","Pending","Error","Unknown"} +type WorkspaceState string + +const ( + WorkspaceStateRunning WorkspaceState = "Running" + WorkspaceStateTerminating WorkspaceState = "Terminating" + WorkspaceStatePaused WorkspaceState = "Paused" + WorkspaceStatePending WorkspaceState = "Pending" + WorkspaceStateError WorkspaceState = "Error" + WorkspaceStateUnknown WorkspaceState = "Unknown" +) + +/* +=============================================================================== + Workspace +=============================================================================== +*/ + +//+kubebuilder:object:root=true +//+kubebuilder:printcolumn:name="State",type="string",JSONPath=".status.state",description="The current state of the Workspace" +//+kubebuilder:subresource:status + +// Workspace is the Schema for the Workspaces API +type Workspace struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec WorkspaceSpec `json:"spec,omitempty"` + Status WorkspaceStatus `json:"status,omitempty"` +} + +/* +=============================================================================== + WorkspaceList +=============================================================================== +*/ + +//+kubebuilder:object:root=true + +// WorkspaceList contains a list of Workspace +type WorkspaceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Workspace `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Workspace{}, &WorkspaceList{}) +} diff --git a/workspaces/controller/api/v1beta1/workspacekind_types.go b/workspaces/controller/api/v1beta1/workspacekind_types.go index 12f6eb22..72d3ba44 100644 --- a/workspaces/controller/api/v1beta1/workspacekind_types.go +++ b/workspaces/controller/api/v1beta1/workspacekind_types.go @@ -22,58 +22,26 @@ import ( ) // Important: Run "make" to regenerate code after modifying this file -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +/* +=============================================================================== + WorkspaceKind - Spec +=============================================================================== +*/ // WorkspaceKindSpec defines the desired state of WorkspaceKind type WorkspaceKindSpec struct { // spawner config determines how the WorkspaceKind is displayed in the Workspace Spawner UI //+kubebuilder:validation:Required - Spawner Spawner `json:"spawner"` + Spawner WorkspaceKindSpawner `json:"spawner"` // podTemplate is the PodTemplate used to spawn Pods to run Workspaces of this WorkspaceKind //+kubebuilder:validation:Required PodTemplate WorkspaceKindPodTemplate `json:"podTemplate"` - - // in the future, there will be MORE template types like - // `virtualMachine` to run the Workspace on systems like KubeVirt/EC2 rather than in a Pod -} - -// WorkspaceKindStatus defines the observed state of WorkspaceKind -type WorkspaceKindStatus struct { - // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster - // Important: Run "make" to regenerate code after modifying this file -} - -//+kubebuilder:object:root=true -//+kubebuilder:printcolumn:name="Deprecated",type="boolean",JSONPath=".spec.spawner.deprecated",description="If this WorkspaceKind is deprecated" -//+kubebuilder:printcolumn:name="Hidden",type="boolean",JSONPath=".spec.spawner.hidden",description="If this WorkspaceKind is hidden from the spawner UI" -//+kubebuilder:subresource:status -//+kubebuilder:resource:scope=Cluster - -// WorkspaceKind is the Schema for the workspacekinds API -type WorkspaceKind struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec WorkspaceKindSpec `json:"spec,omitempty"` - Status WorkspaceKindStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// WorkspaceKindList contains a list of WorkspaceKind -type WorkspaceKindList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []WorkspaceKind `json:"items"` } -func init() { - SchemeBuilder.Register(&WorkspaceKind{}, &WorkspaceKindList{}) -} - -type Spawner struct { +type WorkspaceKindSpawner struct { // the display name of the WorkspaceKind //+kubebuilder:validation:MinLength:=2 //+kubebuilder:validation:MaxLength:=128 @@ -109,24 +77,44 @@ type Spawner struct { Logo WorkspaceKindIcon `json:"logo"` } +// +kubebuilder:validation:XValidation:message="must specify exactly one of 'url' or 'configMap'",rule="!(has(self.url) && has(self.configMap)) && (has(self.url) || has(self.configMap))" +type WorkspaceKindIcon struct { + //+kubebuilder:example="https://jupyter.org/assets/favicons/apple-touch-icon-152x152.png" + //+kubebuilder:validation:Optional + Url *string `json:"url,omitempty"` + + //+kubebuilder:validation:Optional + ConfigMap *WorkspaceKindConfigMap `json:"configMap,omitempty"` +} + +type WorkspaceKindConfigMap struct { + //+kubebuilder:example="my-logos" + //+kubebuilder:validation:Required + Name string `json:"name"` + + //+kubebuilder:example="apple-touch-icon-152x152.png" + //+kubebuilder:validation:Required + Key string `json:"key"` +} + type WorkspaceKindPodTemplate struct { // metadata for Workspace Pods (MUTABLE) PodMetadata WorkspaceKindPodMetadata `json:"podMetadata,omitempty"` // service account configs for Workspace Pods (NOT MUTABLE) //+kubebuilder:validation:Required - ServiceAccount ServiceAccount `json:"serviceAccount"` + ServiceAccount WorkspaceKindServiceAccount `json:"serviceAccount"` // culling configs for pausing inactive Workspaces (MUTABLE) Culling WorkspaceKindCullingConfig `json:"culling,omitempty"` // standard probes to determine Container health (MUTABLE) // updates to existing Workspaces are applied through the "pending restart" feature - Probes Probes `json:"probes,omitempty"` + Probes WorkspaceKindProbes `json:"probes,omitempty"` // volume mount paths (NOT MUTABLE) //+kubebuilder:validation:Required - VolumeMounts VolumeMounts `json:"volumeMounts"` + VolumeMounts WorkspaceKindVolumeMounts `json:"volumeMounts"` // http proxy configs (MUTABLE) HTTPProxy HTTPProxy `json:"httpProxy,omitempty"` @@ -139,30 +127,9 @@ type WorkspaceKindPodTemplate struct { ExtraEnv []v1.EnvVar `json:"extraEnv,omitempty"` // options are the user-selectable fields, they determine the PodSpec of the Workspace - Options KindOptions `json:"options"` -} - -// +kubebuilder:validation:XValidation:message="must specify exactly one of 'url' or 'configMap'",rule="!(has(self.url) && has(self.configMap)) && (has(self.url) || has(self.configMap))" -type WorkspaceKindIcon struct { - //+kubebuilder:example="https://jupyter.org/assets/favicons/apple-touch-icon-152x152.png" - //+kubebuilder:validation:Optional - Url *string `json:"url,omitempty"` - - //+kubebuilder:validation:Optional - ConfigMap *WorkspaceKindConfigMap `json:"configMap,omitempty"` + Options WorkspaceKindPodOptions `json:"options"` } -type WorkspaceKindConfigMap struct { - //+kubebuilder:example="my-logos" - //+kubebuilder:validation:Required - Name string `json:"name"` - - //+kubebuilder:example="apple-touch-icon-152x152.png" - //+kubebuilder:validation:Required - Key string `json:"key"` -} - -// metadata for Workspace Pods (MUTABLE) type WorkspaceKindPodMetadata struct { // labels to be applied to the Pod resource Labels map[string]string `json:"labels,omitempty"` @@ -171,7 +138,7 @@ type WorkspaceKindPodMetadata struct { Annotations map[string]string `json:"annotations,omitempty"` } -type ServiceAccount struct { +type WorkspaceKindServiceAccount struct { // the name of the ServiceAccount; this Service Account MUST already exist in the Namespace of the Workspace, // the controller will NOT create it. We will not show this WorkspaceKind in the Spawner UI if the SA does not exist in the Namespace. //+kubebuilder:validation:Required @@ -192,22 +159,22 @@ type WorkspaceKindCullingConfig struct { // the probe used to determine if the Workspace is active //+kubebuilder:validation:Required - ActivityProbe WorkspaceKindActivityProbe `json:"activityProbe"` + ActivityProbe ActivityProbe `json:"activityProbe"` } // +kubebuilder:validation:XValidation:message="must specify exactly one of 'exec' or 'jupyter'",rule="!(has(self.exec) && has(self.jupyter)) && (has(self.exec) || has(self.jupyter))" -type WorkspaceKindActivityProbe struct { +type ActivityProbe struct { // a "shell" command to run in the Workspace, if the Workspace had activity in the last 60 seconds, this command // should return status 0, otherwise it should return status 1 - Exec *WorkspaceKindActivityProbeExec `json:"exec,omitempty"` + Exec *ActivityProbeExec `json:"exec,omitempty"` // a Jupyter-specific probe will poll the /api/status endpoint of the Jupyter API, and use the last_activity field // Users need to be careful that their other probes don't trigger a "last_activity" update, // e.g. they should only check the health of Jupyter using the /api/status endpoint - Jupyter *WorkspaceKindActivityProbeJupyter `json:"jupyter,omitempty"` + Jupyter *ActivityProbeJupyter `json:"jupyter,omitempty"` } -type WorkspaceKindActivityProbeExec struct { +type ActivityProbeExec struct { //+kubebuilder:validation:Required //+kubebuilder:validation:MinItems:=1 //+kubebuilder:example={"bash", "-c", "exit 0"} @@ -215,13 +182,13 @@ type WorkspaceKindActivityProbeExec struct { } // +kubebuilder:validation:XValidation:message="'lastActivity' must be true",rule="has(self.lastActivity) && self.lastActivity" -type WorkspaceKindActivityProbeJupyter struct { +type ActivityProbeJupyter struct { //+kubebuilder:example=true //+kubebuilder:validation:Required LastActivity bool `json:"lastActivity"` } -type Probes struct { +type WorkspaceKindProbes struct { //+kubebuilder:validation:Optional StartupProbe *v1.Probe `json:"startupProbe,omitempty"` @@ -232,7 +199,7 @@ type Probes struct { ReadinessProbe *v1.Probe `json:"readinessProbe,omitempty"` } -type VolumeMounts struct { +type WorkspaceKindVolumeMounts struct { //+kubebuilder:validation:Required //+kubebuilder:validation:MinLength:=2 //+kubebuilder:validation:MaxLength:=4096 @@ -257,19 +224,24 @@ type HTTPProxy struct { // The following string templates are available: // - .PathPrefix: the path prefix of the Workspace (e.g. '/workspace/{profile_name}/{workspace_name}/') //+kubebuilder:validation:Optional - RequestHeaders []RequestHeader `json:"requestHeaders,omitempty"` + RequestHeaders IstioHeaderOperations `json:"requestHeaders,omitempty"` } -type RequestHeader struct { +type IstioHeaderOperations struct { + // Overwrite the headers specified by key with the given values //+kubebuilder:example:={ "X-RStudio-Root-Path": "{{ .PathPrefix }}" } Set map[string]string `json:"set,omitempty"` - Append map[string]string `json:"append,omitempty"` + // Append the given values to the headers specified by keys (will create a comma-separated list of values) + //+kubebuilder:example:={ "My-Header": "value-to-append" } + Add map[string]string `json:"add,omitempty"` + // Remove the specified headers + //+kubebuilder:example:={"Header-To-Remove"} Remove []string `json:"remove,omitempty"` } -type KindOptions struct { +type WorkspaceKindPodOptions struct { // imageConfig options determine the container image ImageConfig ImageConfig `json:"imageConfig"` @@ -302,31 +274,6 @@ type ImageConfigValue struct { Spec ImageConfigSpec `json:"spec"` } -type OptionSpawnerInfo struct { - //+kubebuilder:validation:Required - //+kubebuilder:validation:MinLength:=2 - //+kubebuilder:validation:MaxLength:=128 - DisplayName string `json:"displayName"` - - //+kubebuilder:validation:Optional - //+kubebuilder:validation:MinLength:=2 - //+kubebuilder:validation:MaxLength:=1024 - Description string `json:"description,omitempty"` - - // if this option should be hidden from the Workspace Spawner UI - //+kubebuilder:validation:Optional - //+kubebuilder:default:=false - Hidden bool `json:"hidden,omitempty"` -} - -type OptionRedirect struct { - //+kubebuilder:example:="jupyter_scipy_171" - To string `json:"to"` - - //+kubebuilder:example:=true - WaitForRestart bool `json:"waitForRestart"` -} - type ImageConfigSpec struct { // the container image to use //+kubebuilder:validation:Required @@ -334,6 +281,12 @@ type ImageConfigSpec struct { //+kubeflow:example="docker.io/kubeflownotebookswg/jupyter-scipy:v1.7.0" Image string `json:"image"` + // the pull policy for the container image + //+kubebuilder:validation:Optional + //+kubebuilder:default:="IfNotPresent" + //+kubebuilder:validation:Enum:={"Always","IfNotPresent","Never"} + ImagePullPolicy *v1.PullPolicy `json:"imagePullPolicy"` + // ports that the container listens on, currently, only HTTP is supported for `protocol` // if multiple ports are defined, the user will see multiple "Connect" buttons in a dropdown menu on the Workspace overview page //+kubebuilder:validation:Required @@ -358,14 +311,20 @@ type ImagePort struct { // the protocol of the port //+kubebuilder:validation:Required - //+kubebuilder:validation:Enum:={"HTTP"} //+kubebuilder:example:="HTTP" - Protocol string `json:"protocol"` + Protocol ImagePortProtocol `json:"protocol"` } +// +kubebuilder:validation:Enum:={"HTTP"} +type ImagePortProtocol string + +const ( + ImagePortProtocolHTTP ImagePortProtocol = "HTTP" +) + type PodConfig struct { // the id of the default pod config - //+kubebuilder:example="small-cpu" + //+kubebuilder:example="big_gpu" Default string `json:"default"` // the list of pod configs that are available @@ -377,7 +336,7 @@ type PodConfig struct { } type PodConfigValue struct { - //+kubebuilder:example="small-cpu" + //+kubebuilder:example="big_gpu" Id string `json:"id"` Spawner OptionSpawnerInfo `json:"spawner"` @@ -405,3 +364,155 @@ type PodConfigSpec struct { //+kubebuilder:validation:Optional Resources v1.ResourceRequirements `json:"resources,omitempty"` } + +type OptionSpawnerInfo struct { + // the display name of the option + //+kubebuilder:validation:Required + //+kubebuilder:validation:MinLength:=2 + //+kubebuilder:validation:MaxLength:=128 + DisplayName string `json:"displayName"` + + // a description of the option + //+kubebuilder:validation:Optional + //+kubebuilder:validation:MinLength:=2 + //+kubebuilder:validation:MaxLength:=1024 + Description string `json:"description,omitempty"` + + // labels for the option + //+kubebuilder:validation:Optional + //+kubebuilder:validation:MaxItems:=32 + //+listType:="map" + //+listMapKey:="key" + Labels []OptionSpawnerLabel `json:"labels,omitempty"` + + // if this option should be hidden from the Workspace Spawner UI + //+kubebuilder:validation:Optional + //+kubebuilder:default:=false + Hidden bool `json:"hidden,omitempty"` +} + +type OptionSpawnerLabel struct { + // the key of the label + //+kubebuilder:validation:Required + //+kubebuilder:validation:MinLength:=2 + //+kubebuilder:validation:MaxLength:=64 + Key string `json:"key"` + + // the value of the label + //+kubebuilder:validation:Required + //+kubebuilder:validation:MinLength:=2 + //+kubebuilder:validation:MaxLength:=64 + Value string `json:"value"` +} + +type OptionRedirect struct { + //+kubebuilder:example:="jupyter_scipy_171" + To string `json:"to"` + + //+kubebuilder:example:=true + WaitForRestart bool `json:"waitForRestart"` + + //+kubebuilder:validation:Optional + Message *RedirectMessage `json:"message,omitempty"` +} + +type RedirectMessage struct { + // the importance level of the message + //+kubebuilder:example:="Info" + Level RedirectMessageLevel `json:"level"` + + // the text of the message to show + //+kubebuilder:validation:MinLength:=2 + //+kubebuilder:validation:MaxLength:=1024 + //+kubebuilder:example:="This update will increase the version of JupyterLab to v1.7.1" + Text string `json:"text"` +} + +// +kubebuilder:validation:Enum:={"Info","Warning","Danger"} +type RedirectMessageLevel string + +const ( + RedirectMessageLevelInfo RedirectMessageLevel = "Info" + RedirectMessageLevelWarning RedirectMessageLevel = "Warning" + RedirectMessageLevelDanger RedirectMessageLevel = "Danger" +) + +/* +=============================================================================== + WorkspaceKind - Status +=============================================================================== +*/ + +// WorkspaceKindStatus defines the observed state of WorkspaceKind +type WorkspaceKindStatus struct { + + // the number of Workspaces that are using this WorkspaceKind + //+kubebuilder:example=3 + Workspaces int64 `json:"workspaces"` + + // metrics for podTemplate options + PodTemplateOptions PodTemplateOptionsMetrics `json:"podTemplateOptions"` +} + +type PodTemplateOptionsMetrics struct { + // metrics about the imageConfig options + //+listType:="map" + //+listMapKey:="id" + ImageConfig []OptionMetric `json:"imageConfig"` + + // metrics about the podConfig options + //+listType:="map" + //+listMapKey:="id" + PodConfig []OptionMetric `json:"podConfig"` +} + +type OptionMetric struct { + // the id of the option + //+kubebuilder:example="big_gpu" + Id string `json:"id"` + + // the number of Workspaces currently using the option + //+kubebuilder:example=3 + Workspaces int64 `json:"workspaces"` +} + +/* +=============================================================================== + WorkspaceKind +=============================================================================== +*/ + +//+kubebuilder:object:root=true +//+kubebuilder:printcolumn:name="Workspaces",type="integer",JSONPath=".status.workspaces",description="The number of Workspaces using this WorkspaceKind" +//+kubebuilder:printcolumn:name="Deprecated",type="boolean",JSONPath=".spec.spawner.deprecated",description="If this WorkspaceKind is deprecated" +//+kubebuilder:printcolumn:name="Hidden",type="boolean",JSONPath=".spec.spawner.hidden",description="If this WorkspaceKind is hidden from the spawner UI" +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Cluster + +// WorkspaceKind is the Schema for the WorkspaceKinds API +type WorkspaceKind struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec WorkspaceKindSpec `json:"spec,omitempty"` + Status WorkspaceKindStatus `json:"status,omitempty"` +} + +/* +=============================================================================== + WorkspaceKindList +=============================================================================== +*/ + +//+kubebuilder:object:root=true + +// WorkspaceKindList contains a list of WorkspaceKind +type WorkspaceKindList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []WorkspaceKind `json:"items"` +} + +func init() { + SchemeBuilder.Register(&WorkspaceKind{}, &WorkspaceKindList{}) +} diff --git a/workspaces/controller/api/v1beta1/zz_generated.deepcopy.go b/workspaces/controller/api/v1beta1/zz_generated.deepcopy.go index 41ed8ce6..4d11b9ff 100644 --- a/workspaces/controller/api/v1beta1/zz_generated.deepcopy.go +++ b/workspaces/controller/api/v1beta1/zz_generated.deepcopy.go @@ -26,30 +26,69 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Activity) DeepCopyInto(out *Activity) { +func (in *ActivityProbe) DeepCopyInto(out *ActivityProbe) { *out = *in + if in.Exec != nil { + in, out := &in.Exec, &out.Exec + *out = new(ActivityProbeExec) + (*in).DeepCopyInto(*out) + } + if in.Jupyter != nil { + in, out := &in.Jupyter, &out.Jupyter + *out = new(ActivityProbeJupyter) + **out = **in + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Activity. -func (in *Activity) DeepCopy() *Activity { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActivityProbe. +func (in *ActivityProbe) DeepCopy() *ActivityProbe { if in == nil { return nil } - out := new(Activity) + out := new(ActivityProbe) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPProxy) DeepCopyInto(out *HTTPProxy) { +func (in *ActivityProbeExec) DeepCopyInto(out *ActivityProbeExec) { *out = *in - if in.RequestHeaders != nil { - in, out := &in.RequestHeaders, &out.RequestHeaders - *out = make([]RequestHeader, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } + if in.Command != nil { + in, out := &in.Command, &out.Command + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActivityProbeExec. +func (in *ActivityProbeExec) DeepCopy() *ActivityProbeExec { + if in == nil { + return nil + } + out := new(ActivityProbeExec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ActivityProbeJupyter) DeepCopyInto(out *ActivityProbeJupyter) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ActivityProbeJupyter. +func (in *ActivityProbeJupyter) DeepCopy() *ActivityProbeJupyter { + if in == nil { + return nil } + out := new(ActivityProbeJupyter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPProxy) DeepCopyInto(out *HTTPProxy) { + *out = *in + in.RequestHeaders.DeepCopyInto(&out.RequestHeaders) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPProxy. @@ -87,6 +126,11 @@ func (in *ImageConfig) DeepCopy() *ImageConfig { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageConfigSpec) DeepCopyInto(out *ImageConfigSpec) { *out = *in + if in.ImagePullPolicy != nil { + in, out := &in.ImagePullPolicy, &out.ImagePullPolicy + *out = new(v1.PullPolicy) + **out = **in + } if in.Ports != nil { in, out := &in.Ports, &out.Ports *out = make([]ImagePort, len(*in)) @@ -107,8 +151,8 @@ func (in *ImageConfigSpec) DeepCopy() *ImageConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageConfigValue) DeepCopyInto(out *ImageConfigValue) { *out = *in - out.Spawner = in.Spawner - out.Redirect = in.Redirect + in.Spawner.DeepCopyInto(&out.Spawner) + in.Redirect.DeepCopyInto(&out.Redirect) in.Spec.DeepCopyInto(&out.Spec) } @@ -138,18 +182,50 @@ func (in *ImagePort) DeepCopy() *ImagePort { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *KindOptions) DeepCopyInto(out *KindOptions) { +func (in *IstioHeaderOperations) DeepCopyInto(out *IstioHeaderOperations) { *out = *in - in.ImageConfig.DeepCopyInto(&out.ImageConfig) - in.PodConfig.DeepCopyInto(&out.PodConfig) + if in.Set != nil { + in, out := &in.Set, &out.Set + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Add != nil { + in, out := &in.Add, &out.Add + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Remove != nil { + in, out := &in.Remove, &out.Remove + *out = make([]string, len(*in)) + copy(*out, *in) + } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KindOptions. -func (in *KindOptions) DeepCopy() *KindOptions { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IstioHeaderOperations. +func (in *IstioHeaderOperations) DeepCopy() *IstioHeaderOperations { if in == nil { return nil } - out := new(KindOptions) + out := new(IstioHeaderOperations) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *OptionMetric) DeepCopyInto(out *OptionMetric) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionMetric. +func (in *OptionMetric) DeepCopy() *OptionMetric { + if in == nil { + return nil + } + out := new(OptionMetric) in.DeepCopyInto(out) return out } @@ -157,6 +233,11 @@ func (in *KindOptions) DeepCopy() *KindOptions { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OptionRedirect) DeepCopyInto(out *OptionRedirect) { *out = *in + if in.Message != nil { + in, out := &in.Message, &out.Message + *out = new(RedirectMessage) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionRedirect. @@ -172,6 +253,11 @@ func (in *OptionRedirect) DeepCopy() *OptionRedirect { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OptionSpawnerInfo) DeepCopyInto(out *OptionSpawnerInfo) { *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make([]OptionSpawnerLabel, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionSpawnerInfo. @@ -185,16 +271,16 @@ func (in *OptionSpawnerInfo) DeepCopy() *OptionSpawnerInfo { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Options) DeepCopyInto(out *Options) { +func (in *OptionSpawnerLabel) DeepCopyInto(out *OptionSpawnerLabel) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Options. -func (in *Options) DeepCopy() *Options { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OptionSpawnerLabel. +func (in *OptionSpawnerLabel) DeepCopy() *OptionSpawnerLabel { if in == nil { return nil } - out := new(Options) + out := new(OptionSpawnerLabel) in.DeepCopyInto(out) return out } @@ -253,8 +339,8 @@ func (in *PodConfigSpec) DeepCopy() *PodConfigSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PodConfigValue) DeepCopyInto(out *PodConfigValue) { *out = *in - out.Spawner = in.Spawner - out.Redirect = in.Redirect + in.Spawner.DeepCopyInto(&out.Spawner) + in.Redirect.DeepCopyInto(&out.Redirect) in.Spec.DeepCopyInto(&out.Spec) } @@ -269,48 +355,26 @@ func (in *PodConfigValue) DeepCopy() *PodConfigValue { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PodMetadata) DeepCopyInto(out *PodMetadata) { +func (in *PodTemplateOptionsMetrics) DeepCopyInto(out *PodTemplateOptionsMetrics) { *out = *in - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Annotations != nil { - in, out := &in.Annotations, &out.Annotations - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } + if in.ImageConfig != nil { + in, out := &in.ImageConfig, &out.ImageConfig + *out = make([]OptionMetric, len(*in)) + copy(*out, *in) } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodMetadata. -func (in *PodMetadata) DeepCopy() *PodMetadata { - if in == nil { - return nil + if in.PodConfig != nil { + in, out := &in.PodConfig, &out.PodConfig + *out = make([]OptionMetric, len(*in)) + copy(*out, *in) } - out := new(PodMetadata) - in.DeepCopyInto(out) - return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PodTemplate) DeepCopyInto(out *PodTemplate) { - *out = *in - in.PodMetadata.DeepCopyInto(&out.PodMetadata) - in.Volumes.DeepCopyInto(&out.Volumes) - out.Options = in.Options -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodTemplate. -func (in *PodTemplate) DeepCopy() *PodTemplate { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodTemplateOptionsMetrics. +func (in *PodTemplateOptionsMetrics) DeepCopy() *PodTemplateOptionsMetrics { if in == nil { return nil } - out := new(PodTemplate) + out := new(PodTemplateOptionsMetrics) in.DeepCopyInto(out) return out } @@ -331,132 +395,16 @@ func (in *PodVolumeMount) DeepCopy() *PodVolumeMount { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PodVolumes) DeepCopyInto(out *PodVolumes) { - *out = *in - if in.Data != nil { - in, out := &in.Data, &out.Data - *out = make([]PodVolumeMount, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodVolumes. -func (in *PodVolumes) DeepCopy() *PodVolumes { - if in == nil { - return nil - } - out := new(PodVolumes) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Probes) DeepCopyInto(out *Probes) { - *out = *in - if in.StartupProbe != nil { - in, out := &in.StartupProbe, &out.StartupProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } - if in.LivenessProbe != nil { - in, out := &in.LivenessProbe, &out.LivenessProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } - if in.ReadinessProbe != nil { - in, out := &in.ReadinessProbe, &out.ReadinessProbe - *out = new(v1.Probe) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Probes. -func (in *Probes) DeepCopy() *Probes { - if in == nil { - return nil - } - out := new(Probes) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RequestHeader) DeepCopyInto(out *RequestHeader) { - *out = *in - if in.Set != nil { - in, out := &in.Set, &out.Set - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Append != nil { - in, out := &in.Append, &out.Append - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Remove != nil { - in, out := &in.Remove, &out.Remove - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestHeader. -func (in *RequestHeader) DeepCopy() *RequestHeader { - if in == nil { - return nil - } - out := new(RequestHeader) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceAccount) DeepCopyInto(out *ServiceAccount) { +func (in *RedirectMessage) DeepCopyInto(out *RedirectMessage) { *out = *in } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceAccount. -func (in *ServiceAccount) DeepCopy() *ServiceAccount { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RedirectMessage. +func (in *RedirectMessage) DeepCopy() *RedirectMessage { if in == nil { return nil } - out := new(ServiceAccount) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Spawner) DeepCopyInto(out *Spawner) { - *out = *in - in.Icon.DeepCopyInto(&out.Icon) - in.Logo.DeepCopyInto(&out.Logo) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Spawner. -func (in *Spawner) DeepCopy() *Spawner { - if in == nil { - return nil - } - out := new(Spawner) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VolumeMounts) DeepCopyInto(out *VolumeMounts) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeMounts. -func (in *VolumeMounts) DeepCopy() *VolumeMounts { - if in == nil { - return nil - } - out := new(VolumeMounts) + out := new(RedirectMessage) in.DeepCopyInto(out) return out } @@ -488,13 +436,28 @@ func (in *Workspace) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceActivity) DeepCopyInto(out *WorkspaceActivity) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceActivity. +func (in *WorkspaceActivity) DeepCopy() *WorkspaceActivity { + if in == nil { + return nil + } + out := new(WorkspaceActivity) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceKind) DeepCopyInto(out *WorkspaceKind) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status + in.Status.DeepCopyInto(&out.Status) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKind. @@ -515,66 +478,6 @@ func (in *WorkspaceKind) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceKindActivityProbe) DeepCopyInto(out *WorkspaceKindActivityProbe) { - *out = *in - if in.Exec != nil { - in, out := &in.Exec, &out.Exec - *out = new(WorkspaceKindActivityProbeExec) - (*in).DeepCopyInto(*out) - } - if in.Jupyter != nil { - in, out := &in.Jupyter, &out.Jupyter - *out = new(WorkspaceKindActivityProbeJupyter) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindActivityProbe. -func (in *WorkspaceKindActivityProbe) DeepCopy() *WorkspaceKindActivityProbe { - if in == nil { - return nil - } - out := new(WorkspaceKindActivityProbe) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceKindActivityProbeExec) DeepCopyInto(out *WorkspaceKindActivityProbeExec) { - *out = *in - if in.Command != nil { - in, out := &in.Command, &out.Command - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindActivityProbeExec. -func (in *WorkspaceKindActivityProbeExec) DeepCopy() *WorkspaceKindActivityProbeExec { - if in == nil { - return nil - } - out := new(WorkspaceKindActivityProbeExec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *WorkspaceKindActivityProbeJupyter) DeepCopyInto(out *WorkspaceKindActivityProbeJupyter) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindActivityProbeJupyter. -func (in *WorkspaceKindActivityProbeJupyter) DeepCopy() *WorkspaceKindActivityProbeJupyter { - if in == nil { - return nil - } - out := new(WorkspaceKindActivityProbeJupyter) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceKindConfigMap) DeepCopyInto(out *WorkspaceKindConfigMap) { *out = *in @@ -692,6 +595,23 @@ func (in *WorkspaceKindPodMetadata) DeepCopy() *WorkspaceKindPodMetadata { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceKindPodOptions) DeepCopyInto(out *WorkspaceKindPodOptions) { + *out = *in + in.ImageConfig.DeepCopyInto(&out.ImageConfig) + in.PodConfig.DeepCopyInto(&out.PodConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindPodOptions. +func (in *WorkspaceKindPodOptions) DeepCopy() *WorkspaceKindPodOptions { + if in == nil { + return nil + } + out := new(WorkspaceKindPodOptions) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceKindPodTemplate) DeepCopyInto(out *WorkspaceKindPodTemplate) { *out = *in @@ -721,6 +641,68 @@ func (in *WorkspaceKindPodTemplate) DeepCopy() *WorkspaceKindPodTemplate { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceKindProbes) DeepCopyInto(out *WorkspaceKindProbes) { + *out = *in + if in.StartupProbe != nil { + in, out := &in.StartupProbe, &out.StartupProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.LivenessProbe != nil { + in, out := &in.LivenessProbe, &out.LivenessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } + if in.ReadinessProbe != nil { + in, out := &in.ReadinessProbe, &out.ReadinessProbe + *out = new(v1.Probe) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindProbes. +func (in *WorkspaceKindProbes) DeepCopy() *WorkspaceKindProbes { + if in == nil { + return nil + } + out := new(WorkspaceKindProbes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceKindServiceAccount) DeepCopyInto(out *WorkspaceKindServiceAccount) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindServiceAccount. +func (in *WorkspaceKindServiceAccount) DeepCopy() *WorkspaceKindServiceAccount { + if in == nil { + return nil + } + out := new(WorkspaceKindServiceAccount) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceKindSpawner) DeepCopyInto(out *WorkspaceKindSpawner) { + *out = *in + in.Icon.DeepCopyInto(&out.Icon) + in.Logo.DeepCopyInto(&out.Logo) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindSpawner. +func (in *WorkspaceKindSpawner) DeepCopy() *WorkspaceKindSpawner { + if in == nil { + return nil + } + out := new(WorkspaceKindSpawner) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceKindSpec) DeepCopyInto(out *WorkspaceKindSpec) { *out = *in @@ -741,6 +723,7 @@ func (in *WorkspaceKindSpec) DeepCopy() *WorkspaceKindSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceKindStatus) DeepCopyInto(out *WorkspaceKindStatus) { *out = *in + in.PodTemplateOptions.DeepCopyInto(&out.PodTemplateOptions) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindStatus. @@ -753,6 +736,21 @@ func (in *WorkspaceKindStatus) DeepCopy() *WorkspaceKindStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspaceKindVolumeMounts) DeepCopyInto(out *WorkspaceKindVolumeMounts) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspaceKindVolumeMounts. +func (in *WorkspaceKindVolumeMounts) DeepCopy() *WorkspaceKindVolumeMounts { + if in == nil { + return nil + } + out := new(WorkspaceKindVolumeMounts) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceList) DeepCopyInto(out *WorkspaceList) { *out = *in @@ -785,6 +783,88 @@ func (in *WorkspaceList) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspacePodMetadata) DeepCopyInto(out *WorkspacePodMetadata) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspacePodMetadata. +func (in *WorkspacePodMetadata) DeepCopy() *WorkspacePodMetadata { + if in == nil { + return nil + } + out := new(WorkspacePodMetadata) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspacePodOptions) DeepCopyInto(out *WorkspacePodOptions) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspacePodOptions. +func (in *WorkspacePodOptions) DeepCopy() *WorkspacePodOptions { + if in == nil { + return nil + } + out := new(WorkspacePodOptions) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspacePodTemplate) DeepCopyInto(out *WorkspacePodTemplate) { + *out = *in + in.PodMetadata.DeepCopyInto(&out.PodMetadata) + in.Volumes.DeepCopyInto(&out.Volumes) + out.Options = in.Options +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspacePodTemplate. +func (in *WorkspacePodTemplate) DeepCopy() *WorkspacePodTemplate { + if in == nil { + return nil + } + out := new(WorkspacePodTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkspacePodVolumes) DeepCopyInto(out *WorkspacePodVolumes) { + *out = *in + if in.Data != nil { + in, out := &in.Data, &out.Data + *out = make([]PodVolumeMount, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkspacePodVolumes. +func (in *WorkspacePodVolumes) DeepCopy() *WorkspacePodVolumes { + if in == nil { + return nil + } + out := new(WorkspacePodVolumes) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *WorkspaceSpec) DeepCopyInto(out *WorkspaceSpec) { *out = *in diff --git a/workspaces/controller/config/crd/bases/kubeflow.org_workspacekinds.yaml b/workspaces/controller/config/crd/bases/kubeflow.org_workspacekinds.yaml index 56b9477e..26f5cea0 100644 --- a/workspaces/controller/config/crd/bases/kubeflow.org_workspacekinds.yaml +++ b/workspaces/controller/config/crd/bases/kubeflow.org_workspacekinds.yaml @@ -15,6 +15,10 @@ spec: scope: Cluster versions: - additionalPrinterColumns: + - description: The number of Workspaces using this WorkspaceKind + jsonPath: .status.workspaces + name: Workspaces + type: integer - description: If this WorkspaceKind is deprecated jsonPath: .spec.spawner.deprecated name: Deprecated @@ -26,7 +30,7 @@ spec: name: v1beta1 schema: openAPIV3Schema: - description: WorkspaceKind is the Schema for the workspacekinds API + description: WorkspaceKind is the Schema for the WorkspaceKinds API properties: apiVersion: description: |- @@ -252,24 +256,31 @@ spec: The following string templates are available: - .PathPrefix: the path prefix of the Workspace (e.g. '/workspace/{profile_name}/{workspace_name}/') - items: - properties: - append: - additionalProperties: - type: string - type: object - remove: - items: - type: string - type: array - set: - additionalProperties: - type: string - example: - X-RStudio-Root-Path: '{{ .PathPrefix }}' - type: object - type: object - type: array + 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 @@ -293,6 +304,29 @@ spec: type: string redirect: properties: + message: + properties: + level: + description: the importance level of the + message + enum: + - Info + - Warning + - Danger + example: Info + type: string + text: + description: the text of the message to + show + example: This update will increase the version + of JupyterLab to v1.7.1 + maxLength: 1024 + minLength: 2 + type: string + required: + - level + - text + type: object to: example: jupyter_scipy_171 type: string @@ -306,10 +340,12 @@ spec: spawner: properties: description: + description: a description of the option maxLength: 1024 minLength: 2 type: string displayName: + description: the display name of the option maxLength: 128 minLength: 2 type: string @@ -318,6 +354,29 @@ spec: description: if this option should be hidden from the Workspace Spawner UI type: boolean + labels: + description: labels for the option + items: + properties: + key: + description: the key of the label + maxLength: 64 + minLength: 2 + type: string + value: + description: the value of the label + maxLength: 64 + minLength: 2 + type: string + required: + - key + - value + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map required: - displayName type: object @@ -327,6 +386,15 @@ spec: description: the container image to use minLength: 2 type: string + imagePullPolicy: + default: IfNotPresent + description: the pull policy for the container + image + enum: + - Always + - IfNotPresent + - Never + type: string ports: description: |- ports that the container listens on, currently, only HTTP is supported for `protocol` @@ -383,17 +451,40 @@ spec: properties: default: description: the id of the default pod config - example: small-cpu + example: big_gpu type: string values: description: the list of pod configs that are available items: properties: id: - example: small-cpu + example: big_gpu type: string redirect: properties: + message: + properties: + level: + description: the importance level of the + message + enum: + - Info + - Warning + - Danger + example: Info + type: string + text: + description: the text of the message to + show + example: This update will increase the version + of JupyterLab to v1.7.1 + maxLength: 1024 + minLength: 2 + type: string + required: + - level + - text + type: object to: example: jupyter_scipy_171 type: string @@ -407,10 +498,12 @@ spec: spawner: properties: description: + description: a description of the option maxLength: 1024 minLength: 2 type: string displayName: + description: the display name of the option maxLength: 128 minLength: 2 type: string @@ -419,6 +512,29 @@ spec: description: if this option should be hidden from the Workspace Spawner UI type: boolean + labels: + description: labels for the option + items: + properties: + key: + description: the key of the label + maxLength: 64 + minLength: 2 + type: string + value: + description: the value of the label + maxLength: 64 + minLength: 2 + type: string + required: + - key + - value + type: object + maxItems: 32 + type: array + x-kubernetes-list-map-keys: + - key + x-kubernetes-list-type: map required: - displayName type: object @@ -2163,6 +2279,66 @@ spec: type: object status: description: WorkspaceKindStatus defines the observed state of WorkspaceKind + properties: + podTemplateOptions: + description: metrics for podTemplate options + properties: + imageConfig: + description: metrics about the imageConfig options + items: + properties: + id: + description: the id of the option + example: big_gpu + type: string + workspaces: + description: the number of Workspaces currently using the + option + example: 3 + format: int64 + type: integer + required: + - id + - workspaces + type: object + type: array + x-kubernetes-list-map-keys: + - id + x-kubernetes-list-type: map + podConfig: + description: metrics about the podConfig options + items: + properties: + id: + description: the id of the option + example: big_gpu + type: string + workspaces: + description: the number of Workspaces currently using the + option + example: 3 + format: int64 + type: integer + required: + - id + - workspaces + type: object + type: array + x-kubernetes-list-map-keys: + - id + x-kubernetes-list-type: map + required: + - imageConfig + - podConfig + type: object + workspaces: + description: the number of Workspaces that are using this WorkspaceKind + example: 3 + format: int64 + type: integer + required: + - podTemplateOptions + - workspaces type: object type: object served: true diff --git a/workspaces/controller/config/crd/bases/kubeflow.org_workspaces.yaml b/workspaces/controller/config/crd/bases/kubeflow.org_workspaces.yaml index a76d3f9c..2726be41 100644 --- a/workspaces/controller/config/crd/bases/kubeflow.org_workspaces.yaml +++ b/workspaces/controller/config/crd/bases/kubeflow.org_workspaces.yaml @@ -22,7 +22,7 @@ spec: name: v1beta1 schema: openAPIV3Schema: - description: Workspace is the Schema for the workspaces API + description: Workspace is the Schema for the Workspaces API properties: apiVersion: description: |- diff --git a/workspaces/controller/go.mod b/workspaces/controller/go.mod index d610edb5..6d5c34ac 100644 --- a/workspaces/controller/go.mod +++ b/workspaces/controller/go.mod @@ -8,6 +8,7 @@ require ( k8s.io/api v0.29.2 k8s.io/apimachinery v0.29.2 k8s.io/client-go v0.29.2 + k8s.io/utils v0.0.0-20230726121419-3b25d923346b sigs.k8s.io/controller-runtime v0.17.3 ) @@ -66,7 +67,6 @@ require ( k8s.io/component-base v0.29.2 // indirect k8s.io/klog/v2 v2.110.1 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect - k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.4.0 // indirect diff --git a/workspaces/controller/internal/controller/workspace_controller_test.go b/workspaces/controller/internal/controller/workspace_controller_test.go index e7e5d688..c1a86040 100644 --- a/workspaces/controller/internal/controller/workspace_controller_test.go +++ b/workspaces/controller/internal/controller/workspace_controller_test.go @@ -54,12 +54,12 @@ var _ = Describe("Workspace Controller", func() { Spec: kubefloworgv1beta1.WorkspaceSpec{ Paused: false, Kind: "juptyer-lab", - PodTemplate: kubefloworgv1beta1.PodTemplate{ - PodMetadata: kubefloworgv1beta1.PodMetadata{ + PodTemplate: kubefloworgv1beta1.WorkspacePodTemplate{ + PodMetadata: kubefloworgv1beta1.WorkspacePodMetadata{ Labels: nil, Annotations: nil, }, - Volumes: kubefloworgv1beta1.PodVolumes{ + Volumes: kubefloworgv1beta1.WorkspacePodVolumes{ Home: "my-home-pvc", Data: []kubefloworgv1beta1.PodVolumeMount{ { @@ -68,7 +68,7 @@ var _ = Describe("Workspace Controller", func() { }, }, }, - Options: kubefloworgv1beta1.Options{ + Options: kubefloworgv1beta1.WorkspacePodOptions{ ImageConfig: "jupyter_scipy_170", PodConfig: "big_gpu", }, diff --git a/workspaces/controller/internal/controller/workspacekind_controller_test.go b/workspaces/controller/internal/controller/workspacekind_controller_test.go index 9dffcb41..6124bcf3 100644 --- a/workspaces/controller/internal/controller/workspacekind_controller_test.go +++ b/workspaces/controller/internal/controller/workspacekind_controller_test.go @@ -54,7 +54,7 @@ var _ = Describe("WorkspaceKind Controller", func() { Name: resourceName, }, Spec: kubefloworgv1beta1.WorkspaceKindSpec{ - Spawner: kubefloworgv1beta1.Spawner{ + Spawner: kubefloworgv1beta1.WorkspaceKindSpawner{ DisplayName: "JupyterLab Notebook", Description: "A Workspace which runs JupyterLab in a Pod", Hidden: false, @@ -73,30 +73,28 @@ var _ = Describe("WorkspaceKind Controller", func() { PodTemplate: kubefloworgv1beta1.WorkspaceKindPodTemplate{ PodMetadata: kubefloworgv1beta1.WorkspaceKindPodMetadata{}, - ServiceAccount: kubefloworgv1beta1.ServiceAccount{ + ServiceAccount: kubefloworgv1beta1.WorkspaceKindServiceAccount{ Name: "default-editor", }, Culling: kubefloworgv1beta1.WorkspaceKindCullingConfig{ Enabled: true, MaxInactiveSeconds: 86400, - ActivityProbe: kubefloworgv1beta1.WorkspaceKindActivityProbe{ - Exec: &kubefloworgv1beta1.WorkspaceKindActivityProbeExec{ + ActivityProbe: kubefloworgv1beta1.ActivityProbe{ + Exec: &kubefloworgv1beta1.ActivityProbeExec{ Command: []string{"bash", "-c", "exit 0"}, }, }, }, - Probes: kubefloworgv1beta1.Probes{}, - VolumeMounts: kubefloworgv1beta1.VolumeMounts{ + Probes: kubefloworgv1beta1.WorkspaceKindProbes{}, + VolumeMounts: kubefloworgv1beta1.WorkspaceKindVolumeMounts{ Home: "/home/jovyan", }, HTTPProxy: kubefloworgv1beta1.HTTPProxy{ RemovePathPrefix: false, - RequestHeaders: []kubefloworgv1beta1.RequestHeader{ - { - Set: map[string]string{"X-RStudio-Root-Path": "{{ .PathPrefix }}"}, - Append: map[string]string{}, - Remove: []string{}, - }, + RequestHeaders: kubefloworgv1beta1.IstioHeaderOperations{ + Set: map[string]string{"X-RStudio-Root-Path": "{{ .PathPrefix }}"}, + Add: map[string]string{}, + Remove: []string{}, }, }, ExtraEnv: []v1.EnvVar{ @@ -105,7 +103,7 @@ var _ = Describe("WorkspaceKind Controller", func() { Value: "{{ .PathPrefix }}", }, }, - Options: kubefloworgv1beta1.KindOptions{ + Options: kubefloworgv1beta1.WorkspaceKindPodOptions{ ImageConfig: kubefloworgv1beta1.ImageConfig{ Default: "jupyter_scipy_171", Values: []kubefloworgv1beta1.ImageConfigValue{ @@ -119,6 +117,10 @@ var _ = Describe("WorkspaceKind Controller", func() { Redirect: kubefloworgv1beta1.OptionRedirect{ To: "jupyter_scipy_171", WaitForRestart: true, + Message: &kubefloworgv1beta1.RedirectMessage{ + Level: "Info", + Text: "This update will increase the version of JupyterLab to v1.7.1", + }, }, Spec: kubefloworgv1beta1.ImageConfigSpec{ Image: "docker.io/kubeflownotebookswg/jupyter-scipy:v1.7.0",