From 337ee88170797584c833c6f135740aa802e6d9ed Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 27 Jan 2026 11:21:29 +0400 Subject: [PATCH 1/2] feat(backups): updated dashboard for backupClass Signed-off-by: Andrey Kolkov --- .../controller/dashboard/static_refactored.go | 65 +++++++++++++------ 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index b2ace34b..4b244396 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -495,6 +495,27 @@ func CreateAllCustomFormsOverrides() []*dashboardv1alpha1.CustomFormsOverride { createFormItem("spec.ports", "Ports", "array"), }, }), + + // Plans form override - backups.cozystack.io/v1alpha1 + createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/plans", map[string]any{ + "formItems": []any{ + createFormItem("metadata.name", "Name", "text"), + createFormItem("metadata.namespace", "Namespace", "text"), + createFormItem("spec.applicationRef.kind", "Application Kind", "text"), + createFormItem("spec.applicationRef.name", "Application Name", "text"), + createFormItemWithAPI("spec.backupClassName", "Backup Class", "select", map[string]any{ + "api": map[string]any{ + "fetchUrl": "/api/clusters/{clusterName}/k8s/apis/backups.cozystack.io/v1alpha1/backupclasses", + "pathToItems": []any{"items"}, + "pathToValue": []any{"metadata", "name"}, + "pathToLabel": []any{"metadata", "name"}, + "clusterNameVar": "clusterName", + }, + }), + createFormItem("spec.schedule.type", "Schedule Type", "text"), + createFormItem("spec.schedule.cron", "Schedule Cron", "text"), + }, + }), } } @@ -1556,13 +1577,9 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), - antdFlexVertical("spec-storage-ref-block", 4, []any{ - antdText("storage-ref-label", true, "Storage", nil), - parsedText("storage-ref-value", "{reqsJsonPath[0]['.spec.storageRef.kind']['-']}.{reqsJsonPath[0]['.spec.storageRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.storageRef.name']['-']}", nil), - }), - antdFlexVertical("spec-strategy-ref-block", 4, []any{ - antdText("strategy-ref-label", true, "Strategy", nil), - parsedText("strategy-ref-value", "{reqsJsonPath[0]['.spec.strategyRef.kind']['-']}.{reqsJsonPath[0]['.spec.strategyRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.strategyRef.name']['-']}", nil), + antdFlexVertical("spec-backup-class-name-block", 4, []any{ + antdText("backup-class-name-label", true, "Backup Class", nil), + parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("spec-schedule-type-block", 4, []any{ antdText("schedule-type-label", true, "Schedule Type", nil), @@ -1680,13 +1697,9 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), - antdFlexVertical("spec-storage-ref-block", 4, []any{ - antdText("storage-ref-label", true, "Storage", nil), - parsedText("storage-ref-value", "{reqsJsonPath[0]['.spec.storageRef.kind']['-']}.{reqsJsonPath[0]['.spec.storageRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.storageRef.name']['-']}", nil), - }), - antdFlexVertical("spec-strategy-ref-block", 4, []any{ - antdText("strategy-ref-label", true, "Strategy", nil), - parsedText("strategy-ref-value", "{reqsJsonPath[0]['.spec.strategyRef.name']['-']}", nil), + antdFlexVertical("spec-backup-class-name-block", 4, []any{ + antdText("backup-class-name-label", true, "Backup Class", nil), + parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("status-backup-ref-block", 4, []any{ antdText("backup-ref-label", true, "Backup Ref", nil), @@ -1864,13 +1877,9 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { antdText("application-ref-label", true, "Application", nil), parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil), }), - antdFlexVertical("spec-storage-ref-block", 4, []any{ - antdText("storage-ref-label", true, "Storage", nil), - parsedText("storage-ref-value", "{reqsJsonPath[0]['.spec.storageRef.kind']['-']}.{reqsJsonPath[0]['.spec.storageRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.storageRef.name']['-']}", nil), - }), - antdFlexVertical("spec-strategy-ref-block", 4, []any{ - antdText("strategy-ref-label", true, "Strategy", nil), - parsedText("strategy-ref-value", "{reqsJsonPath[0]['.spec.strategyRef.kind']['-']}.{reqsJsonPath[0]['.spec.strategyRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.strategyRef.name']['-']}", nil), + antdFlexVertical("spec-backup-class-name-block", 4, []any{ + antdText("backup-class-name-label", true, "Backup Class", nil), + parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil), }), antdFlexVertical("status-artifact-uri-block", 4, []any{ antdText("artifact-uri-label", true, "Artifact URI", nil), @@ -2070,6 +2079,20 @@ func createFormItem(path, label, fieldType string) map[string]any { } } +// createFormItemWithAPI creates a form item with API endpoint for resource-based selects +func createFormItemWithAPI(path, label, fieldType string, apiConfig map[string]any) map[string]any { + item := map[string]any{ + "path": path, + "label": label, + "type": fieldType, + } + // Merge API configuration into the form item + for key, value := range apiConfig { + item[key] = value + } + return item +} + // ---------------- Workloadmonitor specific functions ---------------- // createNamespaceHeader creates a header specifically for namespace with correct colors and text From 919e70d184080a6998266db70f8b31724554a1f7 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 27 Jan 2026 12:04:35 +0400 Subject: [PATCH 2/2] add manifests to pkgs Signed-off-by: Andrey Kolkov --- .../backup-controller/templates/class.yaml | 14 ++++++++ .../cozyrds/backup-strategy.yaml | 32 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 packages/system/backup-controller/templates/class.yaml create mode 100644 packages/system/virtual-machine-rd/cozyrds/backup-strategy.yaml diff --git a/packages/system/backup-controller/templates/class.yaml b/packages/system/backup-controller/templates/class.yaml new file mode 100644 index 00000000..cb3164f2 --- /dev/null +++ b/packages/system/backup-controller/templates/class.yaml @@ -0,0 +1,14 @@ +kind: BackupClass +metadata: + name: velero +spec: + strategies: + - strategyRef: + apiGroup: strategy.backups.cozystack.io + kind: Velero + name: velero-backup-strategy-for-virtualmachines + application: + apiVersion: apps.cozystack.io + kind: VirtualMachine + parameters: + backupStorageLocationName: default diff --git a/packages/system/virtual-machine-rd/cozyrds/backup-strategy.yaml b/packages/system/virtual-machine-rd/cozyrds/backup-strategy.yaml new file mode 100644 index 00000000..1312540b --- /dev/null +++ b/packages/system/virtual-machine-rd/cozyrds/backup-strategy.yaml @@ -0,0 +1,32 @@ +apiVersion: strategy.backups.cozystack.io/v1alpha1 +kind: Velero +metadata: + name: velero-backup-strategy-for-virtualmachines +spec: + backupTemplate: + spec: # see https://velero.io/docs/v1.9/api-types/backup/ + includedNamespaces: + - {{ .metadata.namespace }} + + labelSelector: + apps.cozystack.io/application.Kind: {{ .kind }} + + includedResources: + - helmreleases.helm.toolkit.fluxcd.io + - virtualmachines.kubevirt.io + - virtualmachineinstances.kubevirt.io + - datavolumes.cdi.kubevirt.io + - persistentvolumeclaims + - services + - configmaps + - secrets + + storageLocation: {{ .Parameters.backupStorageLocationName }} + + volumeSnapshotLocations: + - {{ .Parameters.backupStorageLocationName }} + snapshotVolumes: true + snapshotMoveData: true + + ttl: 720h0m0s + itemOperationTimeout: 24h0m0s