feat(ws): add ws counts to backend wsk model (#368)

Signed-off-by: rafriat <roee.afriat.ext@nokia.com>
Co-authored-by: rafriat <roee.afriat.ext@nokia.com>
This commit is contained in:
Roee Afriat 2025-07-24 22:36:01 +03:00 committed by Bhakti Narvekar
parent 4b88c153e2
commit f1f1c8336b
5 changed files with 117 additions and 25 deletions

View File

@ -455,5 +455,34 @@ func NewExampleWorkspaceKind(name string) *kubefloworgv1beta1.WorkspaceKind {
},
},
},
Status: kubefloworgv1beta1.WorkspaceKindStatus{
Workspaces: 1,
PodTemplateOptions: kubefloworgv1beta1.PodTemplateOptionsMetrics{
ImageConfig: []kubefloworgv1beta1.OptionMetric{
{
Id: "jupyterlab_scipy_180",
Workspaces: 1,
},
{
Id: "jupyterlab_scipy_190",
Workspaces: 0,
},
},
PodConfig: []kubefloworgv1beta1.OptionMetric{
{
Id: "tiny_cpu",
Workspaces: 1,
},
{
Id: "small_cpu",
Workspaces: 0,
},
{
Id: "big_gpu",
Workspaces: 0,
},
},
},
},
}
}

View File

@ -36,6 +36,8 @@ func NewWorkspaceKindModelFromWorkspaceKind(wsk *kubefloworgv1beta1.WorkspaceKin
podAnnotations[k] = v
}
}
statusImageConfigMap := buildOptionMetricsMap(wsk.Status.PodTemplateOptions.ImageConfig)
statusPodConfigMap := buildOptionMetricsMap(wsk.Status.PodTemplateOptions.PodConfig)
// TODO: icons can either be a remote URL or read from a ConfigMap.
// in BOTH cases, we should cache and serve the image under a path on the backend API:
@ -60,6 +62,10 @@ func NewWorkspaceKindModelFromWorkspaceKind(wsk *kubefloworgv1beta1.WorkspaceKin
Hidden: ptr.Deref(wsk.Spec.Spawner.Hidden, false),
Icon: iconRef,
Logo: logoRef,
// TODO: in the future will need to support including exactly one of clusterMetrics or namespaceMetrics based on request context
ClusterMetrics: clusterMetrics{
Workspaces: wsk.Status.Workspaces,
},
PodTemplate: PodTemplate{
PodMetadata: PodMetadata{
Labels: podLabels,
@ -71,18 +77,26 @@ func NewWorkspaceKindModelFromWorkspaceKind(wsk *kubefloworgv1beta1.WorkspaceKin
Options: PodTemplateOptions{
ImageConfig: ImageConfig{
Default: wsk.Spec.PodTemplate.Options.ImageConfig.Spawner.Default,
Values: buildImageConfigValues(wsk.Spec.PodTemplate.Options.ImageConfig),
Values: buildImageConfigValues(wsk.Spec.PodTemplate.Options.ImageConfig, statusImageConfigMap),
},
PodConfig: PodConfig{
Default: wsk.Spec.PodTemplate.Options.PodConfig.Spawner.Default,
Values: buildPodConfigValues(wsk.Spec.PodTemplate.Options.PodConfig),
Values: buildPodConfigValues(wsk.Spec.PodTemplate.Options.PodConfig, statusPodConfigMap),
},
},
},
}
}
func buildImageConfigValues(imageConfig kubefloworgv1beta1.ImageConfig) []ImageConfigValue {
func buildOptionMetricsMap(metrics []kubefloworgv1beta1.OptionMetric) map[string]int32 {
resultMap := make(map[string]int32)
for _, metric := range metrics {
resultMap[metric.Id] = metric.Workspaces
}
return resultMap
}
func buildImageConfigValues(imageConfig kubefloworgv1beta1.ImageConfig, statusImageConfigMap map[string]int32) []ImageConfigValue {
imageConfigValues := make([]ImageConfigValue, len(imageConfig.Values))
for i := range imageConfig.Values {
option := imageConfig.Values[i]
@ -93,12 +107,16 @@ func buildImageConfigValues(imageConfig kubefloworgv1beta1.ImageConfig) []ImageC
Labels: buildOptionLabels(option.Spawner.Labels),
Hidden: ptr.Deref(option.Spawner.Hidden, false),
Redirect: buildOptionRedirect(option.Redirect),
// TODO: in the future will need to support including exactly one of clusterMetrics or namespaceMetrics based on request context
ClusterMetrics: clusterMetrics{
Workspaces: statusImageConfigMap[option.Id],
},
}
}
return imageConfigValues
}
func buildPodConfigValues(podConfig kubefloworgv1beta1.PodConfig) []PodConfigValue {
func buildPodConfigValues(podConfig kubefloworgv1beta1.PodConfig, statusPodConfigMap map[string]int32) []PodConfigValue {
podConfigValues := make([]PodConfigValue, len(podConfig.Values))
for i := range podConfig.Values {
option := podConfig.Values[i]
@ -109,6 +127,10 @@ func buildPodConfigValues(podConfig kubefloworgv1beta1.PodConfig) []PodConfigVal
Labels: buildOptionLabels(option.Spawner.Labels),
Hidden: ptr.Deref(option.Spawner.Hidden, false),
Redirect: buildOptionRedirect(option.Redirect),
// TODO: in the future will need to support including exactly one of clusterMetrics or namespaceMetrics based on request context
ClusterMetrics: clusterMetrics{
Workspaces: statusPodConfigMap[option.Id],
},
}
}
return podConfigValues

View File

@ -17,15 +17,20 @@ limitations under the License.
package workspacekinds
type WorkspaceKind struct {
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Deprecated bool `json:"deprecated"`
DeprecationMessage string `json:"deprecationMessage"`
Hidden bool `json:"hidden"`
Icon ImageRef `json:"icon"`
Logo ImageRef `json:"logo"`
PodTemplate PodTemplate `json:"podTemplate"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Deprecated bool `json:"deprecated"`
DeprecationMessage string `json:"deprecationMessage"`
Hidden bool `json:"hidden"`
Icon ImageRef `json:"icon"`
Logo ImageRef `json:"logo"`
ClusterMetrics clusterMetrics `json:"clusterMetrics,omitempty"`
PodTemplate PodTemplate `json:"podTemplate"`
}
type clusterMetrics struct {
Workspaces int32 `json:"workspacesCount"`
}
type ImageRef struct {
@ -58,12 +63,13 @@ type ImageConfig struct {
}
type ImageConfigValue struct {
Id string `json:"id"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Labels []OptionLabel `json:"labels"`
Hidden bool `json:"hidden"`
Redirect *OptionRedirect `json:"redirect,omitempty"`
Id string `json:"id"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Labels []OptionLabel `json:"labels"`
Hidden bool `json:"hidden"`
Redirect *OptionRedirect `json:"redirect,omitempty"`
ClusterMetrics clusterMetrics `json:"clusterMetrics,omitempty"`
}
type PodConfig struct {
@ -72,12 +78,13 @@ type PodConfig struct {
}
type PodConfigValue struct {
Id string `json:"id"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Labels []OptionLabel `json:"labels"`
Hidden bool `json:"hidden"`
Redirect *OptionRedirect `json:"redirect,omitempty"`
Id string `json:"id"`
DisplayName string `json:"displayName"`
Description string `json:"description"`
Labels []OptionLabel `json:"labels"`
Hidden bool `json:"hidden"`
Redirect *OptionRedirect `json:"redirect,omitempty"`
ClusterMetrics clusterMetrics `json:"clusterMetrics,omitempty"`
}
type OptionLabel struct {

View File

@ -778,6 +778,9 @@ const docTemplate = `{
"workspacekinds.ImageConfigValue": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"description": {
"type": "string"
},
@ -848,6 +851,9 @@ const docTemplate = `{
"workspacekinds.PodConfigValue": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"description": {
"type": "string"
},
@ -948,6 +954,9 @@ const docTemplate = `{
"workspacekinds.WorkspaceKind": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"deprecated": {
"type": "boolean"
},
@ -977,6 +986,14 @@ const docTemplate = `{
}
}
},
"workspacekinds.clusterMetrics": {
"type": "object",
"properties": {
"workspacesCount": {
"type": "integer"
}
}
},
"workspaces.Activity": {
"type": "object",
"properties": {

View File

@ -776,6 +776,9 @@
"workspacekinds.ImageConfigValue": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"description": {
"type": "string"
},
@ -846,6 +849,9 @@
"workspacekinds.PodConfigValue": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"description": {
"type": "string"
},
@ -946,6 +952,9 @@
"workspacekinds.WorkspaceKind": {
"type": "object",
"properties": {
"clusterMetrics": {
"$ref": "#/definitions/workspacekinds.clusterMetrics"
},
"deprecated": {
"type": "boolean"
},
@ -975,6 +984,14 @@
}
}
},
"workspacekinds.clusterMetrics": {
"type": "object",
"properties": {
"workspacesCount": {
"type": "integer"
}
}
},
"workspaces.Activity": {
"type": "object",
"properties": {