feat(dashboard): add storageClass dropdown for all stateful apps
Replace plain text input with an API-backed listInput dropdown for
storageClass fields across all applications that expose them.
The dropdown fetches available StorageClasses from the cluster via
/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses,
following the same pattern as the instanceType dropdown for VMInstance.
Top-level spec.storageClass: ClickHouse, Harbor, HTTPCache, Kubernetes,
MariaDB, MongoDB, NATS, OpenBAO, Postgres, Qdrant, RabbitMQ, Redis, VMDisk.
Nested paths: FoundationDB (spec.storage.storageClass),
Kafka (spec.kafka.storageClass and spec.zookeeper.storageClass).
Co-Authored-By: Claude <noreply@anthropic.com>
Signed-off-by: Kirill Ilin <stitch14@yandex.ru>
This commit is contained in:
parent
b455405402
commit
c2bf8cf56f
2 changed files with 94 additions and 0 deletions
|
|
@ -214,6 +214,34 @@ func applyListInputOverrides(schema map[string]any, kind string, openAPIProps ma
|
|||
"keysToLabel": []any{"metadata", "name"},
|
||||
},
|
||||
}
|
||||
|
||||
case "ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB",
|
||||
"NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis", "VMDisk":
|
||||
specProps := ensureSchemaPath(schema, "spec")
|
||||
specProps["storageClass"] = storageClassListInput()
|
||||
|
||||
case "FoundationDB":
|
||||
storageProps := ensureSchemaPath(schema, "spec", "storage")
|
||||
storageProps["storageClass"] = storageClassListInput()
|
||||
|
||||
case "Kafka":
|
||||
kafkaProps := ensureSchemaPath(schema, "spec", "kafka")
|
||||
kafkaProps["storageClass"] = storageClassListInput()
|
||||
zkProps := ensureSchemaPath(schema, "spec", "zookeeper")
|
||||
zkProps["storageClass"] = storageClassListInput()
|
||||
}
|
||||
}
|
||||
|
||||
// storageClassListInput returns a listInput field config for a storageClass dropdown
|
||||
// backed by the cluster's available StorageClasses.
|
||||
func storageClassListInput() map[string]any {
|
||||
return map[string]any{
|
||||
"type": "listInput",
|
||||
"customProps": map[string]any{
|
||||
"valueUri": "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses",
|
||||
"keysToValue": []any{"metadata", "name"},
|
||||
"keysToLabel": []any{"metadata", "name"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -232,6 +232,72 @@ func TestApplyListInputOverrides_VMInstance(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestApplyListInputOverrides_StorageClassSimple(t *testing.T) {
|
||||
for _, kind := range []string{
|
||||
"ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB",
|
||||
"NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis", "VMDisk",
|
||||
} {
|
||||
t.Run(kind, func(t *testing.T) {
|
||||
schema := map[string]any{}
|
||||
applyListInputOverrides(schema, kind, map[string]any{})
|
||||
|
||||
specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)
|
||||
sc, ok := specProps["storageClass"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatalf("storageClass not found in spec.properties for kind %s", kind)
|
||||
}
|
||||
assertStorageClassListInput(t, sc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyListInputOverrides_StorageClassFoundationDB(t *testing.T) {
|
||||
schema := map[string]any{}
|
||||
applyListInputOverrides(schema, "FoundationDB", map[string]any{})
|
||||
|
||||
storageProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)["storage"].(map[string]any)["properties"].(map[string]any)
|
||||
sc, ok := storageProps["storageClass"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("storageClass not found in spec.storage.properties")
|
||||
}
|
||||
assertStorageClassListInput(t, sc)
|
||||
}
|
||||
|
||||
func TestApplyListInputOverrides_StorageClassKafka(t *testing.T) {
|
||||
schema := map[string]any{}
|
||||
applyListInputOverrides(schema, "Kafka", map[string]any{})
|
||||
|
||||
specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)
|
||||
|
||||
kafkaSC, ok := specProps["kafka"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("storageClass not found in spec.kafka.properties")
|
||||
}
|
||||
assertStorageClassListInput(t, kafkaSC)
|
||||
|
||||
zkSC, ok := specProps["zookeeper"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("storageClass not found in spec.zookeeper.properties")
|
||||
}
|
||||
assertStorageClassListInput(t, zkSC)
|
||||
}
|
||||
|
||||
// assertStorageClassListInput verifies that a field is a correctly configured storageClass listInput.
|
||||
func assertStorageClassListInput(t *testing.T, field map[string]any) {
|
||||
t.Helper()
|
||||
if field["type"] != "listInput" {
|
||||
t.Errorf("expected type listInput, got %v", field["type"])
|
||||
}
|
||||
customProps, ok := field["customProps"].(map[string]any)
|
||||
if !ok {
|
||||
t.Fatal("customProps not found")
|
||||
}
|
||||
expectedURI := "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses"
|
||||
if customProps["valueUri"] != expectedURI {
|
||||
t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyListInputOverrides_UnknownKind(t *testing.T) {
|
||||
schema := map[string]any{}
|
||||
applyListInputOverrides(schema, "SomeOtherKind", map[string]any{})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue