From e95a47dfdba9bb71ce13ef25e02d99bb635d6a2f Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 11 Jun 2026 11:34:46 +0200 Subject: [PATCH 01/57] define the CompatibilityMode field --- hack/api-reference/api.md | 11 +++++++++++ pkg/apis/stackit/v1alpha1/types_controlplane.go | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md index 77bdac1f..3b0c99a7 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -252,6 +252,17 @@ string

+ + +compatibilityMode
+ +string + + + +

+ + diff --git a/pkg/apis/stackit/v1alpha1/types_controlplane.go b/pkg/apis/stackit/v1alpha1/types_controlplane.go index 5c10c28a..5b28637d 100644 --- a/pkg/apis/stackit/v1alpha1/types_controlplane.go +++ b/pkg/apis/stackit/v1alpha1/types_controlplane.go @@ -58,7 +58,8 @@ type Storage struct { } type CSI struct { - Name string `json:"name"` + Name string `json:"name"` + CompatibilityMode string `json:"compatibilityMode,omitempty"` } // CSIManila contains configuration for CSI Manila driver (support for NFS volumes) From 0395e83c719efb25bc2b90881132ead92bdd5d51 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 11 Jun 2026 12:02:18 +0200 Subject: [PATCH 02/57] add the default value for CSI.CompatibilityMode --- pkg/apis/stackit/v1alpha1/constants.go | 2 ++ pkg/apis/stackit/v1alpha1/defaults.go | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pkg/apis/stackit/v1alpha1/constants.go b/pkg/apis/stackit/v1alpha1/constants.go index 86bbdc81..c7270e7f 100644 --- a/pkg/apis/stackit/v1alpha1/constants.go +++ b/pkg/apis/stackit/v1alpha1/constants.go @@ -5,6 +5,8 @@ package v1alpha1 const ( // DefaultCSIName defines the default CSI (Container Storage Interface) name for STACKIT DefaultCSIName = "stackit" + // DefaultCSICompatibilityMode defines the default CSI driver's compatibility mode. + DefaultCSICompatibilityMode = "default" // DefaultCCMName defines the default CCM (Cloud Controller Manager) controller to use DefaultCCMName = "stackit" ) diff --git a/pkg/apis/stackit/v1alpha1/defaults.go b/pkg/apis/stackit/v1alpha1/defaults.go index d72cba93..e91caf34 100644 --- a/pkg/apis/stackit/v1alpha1/defaults.go +++ b/pkg/apis/stackit/v1alpha1/defaults.go @@ -43,4 +43,7 @@ func SetDefaults_ControlPlaneConfig(obj *ControlPlaneConfig) { if obj.Storage.CSI.Name == "" { obj.Storage.CSI.Name = DefaultCSIName } + if obj.Storage.CSI.CompatibilityMode == "" { + obj.Storage.CSI.CompatibilityMode = DefaultCSICompatibilityMode + } } From 9e3f0cf7d9e6f46bd940fa658a3810a69b920be4 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Fri, 12 Jun 2026 09:31:46 +0200 Subject: [PATCH 03/57] provide access to the new CompatibilityMode field --- pkg/apis/stackit/v1alpha1/constants.go | 8 ++++++++ pkg/controller/controlplane/valuesprovider.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/pkg/apis/stackit/v1alpha1/constants.go b/pkg/apis/stackit/v1alpha1/constants.go index c7270e7f..42400ae6 100644 --- a/pkg/apis/stackit/v1alpha1/constants.go +++ b/pkg/apis/stackit/v1alpha1/constants.go @@ -17,3 +17,11 @@ const ( STACKIT ControllerName = "stackit" OPENSTACK ControllerName = "openstack" ) + +type CSICompatibilityMode string + +const ( + DEFAULT CSICompatibilityMode = "default" + COMPAT CSICompatibilityMode = "compat" + COMPATBLOCK CSICompatibilityMode = "compatblock" +) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 180ac234..c8490254 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1216,6 +1216,10 @@ func getCSIDriver(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1. return stackitv1alpha1.ControllerName(cpConfig.Storage.CSI.Name) } +func getCSICompatibilityMode(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.CSICompatibilityMode { + return stackitv1alpha1.CSICompatibilityMode(cpConfig.Storage.CSI.CompatibilityMode) +} + func getCCMController(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.ControllerName { return stackitv1alpha1.ControllerName(cpConfig.CloudControllerManager.Name) } From b6327d4367627333481f6fb970622c2b312d5c09 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Fri, 12 Jun 2026 13:01:58 +0200 Subject: [PATCH 04/57] render the second DaemonSet in compatibility mode, if parameterized accordingly --- .../templates/daemonset.yaml | 150 ++++++++++++++++++ .../values.yaml | 4 + 2 files changed, 154 insertions(+) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index cb04e3cf..e5e4e83d 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -144,3 +144,153 @@ spec: configMap: defaultMode: 420 name: {{ .Values.prefix }}-cloud-provider-config +--- +{{- if .Values.csiDriverCompatibility.enabled }} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: {{ .Values.prefix }}-csi-driver-compatibility-node + namespace: {{ .Release.Namespace }} + labels: + node.gardener.cloud/critical-component: "true" + app: {{ .Values.prefix }}-csi-compatibility + role: disk-driver +spec: + selector: + matchLabels: + app: {{ .Values.prefix }}-csi-compatibility + role: disk-driver + template: + metadata: + annotations: + checksum/configmap-cloud-provider-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + node.gardener.cloud/wait-for-csi-node-stackit: block-storage.csi.stackit.cloud + labels: + node.gardener.cloud/critical-component: "true" + app: {{ .Values.prefix }}-csi-compatibility + role: disk-driver + spec: + hostNetwork: true + priorityClassName: system-node-critical + serviceAccountName: {{ .Values.prefix }}-csi-driver-node + tolerations: + - effect: NoSchedule + operator: Exists + - key: CriticalAddonsOnly + operator: Exists + - effect: NoExecute + operator: Exists + securityContext: + seccompProfile: + type: RuntimeDefault + containers: + - name: csi-driver-stackit + image: {{ index .Values.images "csi-driver-stackit" }} + args: + - /bin/stackit-csi-plugin + - --endpoint=$(CSI_ENDPOINT) + - --cloud-config=/etc/config/cloud.yaml + {{- range $userAgentHeader := .Values.userAgentHeaders }} + - --user-agent={{ $userAgentHeader }} + {{- end }} + - --v=2 + - --provide-controller-service=false + - --legacy-storage-mode=true + - --legacy-volume-creation={{ .Values.csiDriverCompatibility.legacyVolumeCreation }} + env: + - name: CSI_ENDPOINT + value: unix://{{ .Values.socketPath }} +{{- if .Values.resources.driver }} + resources: +{{ toYaml .Values.resources.driver | indent 10 }} +{{- end }} + securityContext: + privileged: true + capabilities: + add: ["SYS_ADMIN"] + allowPrivilegeEscalation: true + ports: + - name: healthz + containerPort: 9909 + protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: healthz + initialDelaySeconds: 10 + timeoutSeconds: 180 + periodSeconds: 30 + failureThreshold: 5 + volumeMounts: + - name: kubelet-dir + mountPath: /var/lib/kubelet + mountPropagation: "Bidirectional" + - name: plugin-dir + mountPath: /csi + - name: device-dir + mountPath: /dev + mountPropagation: "HostToContainer" + - name: cloud-provider-config + mountPath: /etc/config + + - name: csi-node-driver-registrar + image: {{ index .Values.images "csi-node-driver-registrar" }} + args: + - --csi-address=$(ADDRESS) + - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) + - --v=5 + env: + - name: ADDRESS + value: {{ .Values.socketPath }} + - name: DRIVER_REG_SOCK_PATH + value: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/csi.sock +{{- if .Values.resources.nodeDriverRegistrar }} + resources: +{{ toYaml .Values.resources.nodeDriverRegistrar | indent 10 }} +{{- end }} + volumeMounts: + - name: plugin-dir + mountPath: /csi + - name: registration-dir + mountPath: /registration + securityContext: + allowPrivilegeEscalation: false + + - name: csi-liveness-probe + image: {{ index .Values.images "csi-liveness-probe" }} + args: + - --probe-timeout=3m + - --csi-address={{ .Values.socketPath }} + - --health-port=9909 +{{- if .Values.resources.livenessProbe }} + resources: +{{ toYaml .Values.resources.livenessProbe | indent 10 }} +{{- end }} + volumeMounts: + - name: plugin-dir + mountPath: /csi + securityContext: + allowPrivilegeEscalation: false + + volumes: + - name: kubelet-dir + hostPath: + path: /var/lib/kubelet + type: Directory + - name: plugin-dir + hostPath: + path: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/ + type: DirectoryOrCreate + - name: registration-dir + hostPath: + path: /var/lib/kubelet/plugins_registry/ + type: Directory + - name: device-dir + hostPath: + path: /dev + type: Directory + - name: cloud-provider-config + configMap: + defaultMode: 420 + name: {{ .Values.prefix }}-cloud-provider-config + {{- end }} diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml index c5e073af..5eceba97 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml @@ -3,6 +3,10 @@ driverName: block-storage.csi.stackit.cloud rescanBlockStorageOnResize: "true" +csiDriverCompatibility: + enabled: false + legacyVolumeCreation: true + images: csi-driver-stackit: image-repository:image-tag csi-node-driver-registrar: image-repository:image-tag From 2e7ef9f772710c5b33fa1a426ffc3c9e44e93c2b Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 11:08:37 +0200 Subject: [PATCH 05/57] install control-plane stackit-blockstorage chart (compat) as ManagedResource Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index c8490254..3f4e7d02 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -21,10 +21,12 @@ import ( "github.com/gardener/gardener/pkg/apis/core/v1beta1" v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" + "github.com/gardener/gardener/pkg/chartrenderer" gardenerutils "github.com/gardener/gardener/pkg/utils" "github.com/gardener/gardener/pkg/utils/chart" gutil "github.com/gardener/gardener/pkg/utils/gardener" kutil "github.com/gardener/gardener/pkg/utils/kubernetes" + "github.com/gardener/gardener/pkg/utils/managedresources" secretutils "github.com/gardener/gardener/pkg/utils/secrets" secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" @@ -43,6 +45,7 @@ import ( "k8s.io/apimachinery/pkg/types" utilruntime "k8s.io/apimachinery/pkg/util/runtime" vpaautoscalingv1 "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/apis/autoscaling.k8s.io/v1" + "k8s.io/client-go/rest" "k8s.io/utils/ptr" k8sclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/manager" @@ -359,6 +362,7 @@ var ( func NewValuesProvider(mgr manager.Manager, deployALBIngressController bool, customLabelDomain string) genericactuator.ValuesProvider { return &valuesProvider{ client: mgr.GetClient(), + config: mgr.GetConfig(), decoder: serializer.NewCodecFactory(mgr.GetScheme(), serializer.EnableStrict).UniversalDecoder(), deployALBIngressController: deployALBIngressController, customLabelDomain: customLabelDomain, @@ -369,6 +373,7 @@ func NewValuesProvider(mgr manager.Manager, deployALBIngressController bool, cus type valuesProvider struct { genericactuator.NoopValuesProvider client k8sclient.Client + config *rest.Config decoder runtime.Decoder deployALBIngressController bool customLabelDomain string @@ -750,6 +755,12 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf controlPlaneValues[openstack.CSIControllerName] = map[string]any{ "enabled": false, } + if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { + err := vp.deploySeedCSICompatibilityMode(ctx, cluster.Shoot.GetNamespace(), csiSTACKIT) + if err != nil { + return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + } + } default: return nil, fmt.Errorf("unsupported storage CSI Driver: %s", storageCSIDriver) } @@ -1176,6 +1187,31 @@ func (vp *valuesProvider) checkEmergencyLoadBalancerAccess(ctx context.Context, return apiURL, apiToken, nil } +func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { + renderer, err := chartrenderer.NewForConfig(vp.config) + if err != nil { + return nil + } + + releaseName := "csi-compatibility-mode" + chartName := "stackit-blockstorage-csi-driver" + + values["prefix"] = "stackit-compat" + + renderedChart, err := renderer.RenderEmbeddedFS( + charts.InternalChart, "shoot-system-components/stackit-blockstorage-csi-driver", releaseName, v1beta1constants.GardenNamespace, values, + ) + if err != nil { + return err + } + + data := map[string][]byte{chartName: renderedChart.Manifest()} + return managedresources.Create( + ctx, vp.client, namespace, chartName, map[string]string{}, + false, "seed", data, new(false), nil, new(false), + ) +} + // decodeLoadBalancerAPIEmergencySecret decodes a [corev1.Secret] for emergency loadbalancer access and // returns the apiURL and apiToken to use or an error. // The apiURL and apiToken are only set if both values exist inside the secret and are not empty. From ded54b713f1f793db801ceb8445281dcbb60a783 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 11:10:55 +0200 Subject: [PATCH 06/57] fix chart path Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 3f4e7d02..cd8893e6 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1199,7 +1199,7 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na values["prefix"] = "stackit-compat" renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, "shoot-system-components/stackit-blockstorage-csi-driver", releaseName, v1beta1constants.GardenNamespace, values, + charts.InternalChart, "seed-controlplane/stackit-blockstorage-csi-driver", releaseName, v1beta1constants.GardenNamespace, values, ) if err != nil { return err From 315a7b0596c0e325606a353d2ba4c0932ce78728 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 11:39:04 +0200 Subject: [PATCH 07/57] fix chart path and releaseName Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index cd8893e6..e20a898e 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1193,13 +1193,12 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na return nil } - releaseName := "csi-compatibility-mode" chartName := "stackit-blockstorage-csi-driver" values["prefix"] = "stackit-compat" renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, "seed-controlplane/stackit-blockstorage-csi-driver", releaseName, v1beta1constants.GardenNamespace, values, + charts.InternalChart, "seed-controlplane/charts/stackit-blockstorage-csi-driver", chartName, namespace, values, ) if err != nil { return err From 7ea066fde0f8e9b900f4061cc412666efa6ab0a7 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 11:49:51 +0200 Subject: [PATCH 08/57] fix path? Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index e20a898e..f6162512 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1198,7 +1198,7 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na values["prefix"] = "stackit-compat" renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, "seed-controlplane/charts/stackit-blockstorage-csi-driver", chartName, namespace, values, + charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, namespace, values, ) if err != nil { return err From 700c9cffbd40d72a322be9064a6b8f5f5c16f262 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 11:58:27 +0200 Subject: [PATCH 09/57] Move some things around Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index f6162512..b3de76a8 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -741,6 +741,15 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf return nil, err } + maps.Copy(controlPlaneValues, map[string]any{ + "global": map[string]any{ + "genericTokenKubeconfigSecretName": extensionscontroller.GenericTokenKubeconfigSecretNameFromCluster(cluster), + }, + openstack.CloudControllerManagerName: ccm, + openstack.STACKITCloudControllerManagerName: stackitccm, + stackit.PodIdentityWebhookName: podIdentityWebhook, + }) + storageCSIDriver := getCSIDriver(cpConfig) switch storageCSIDriver { case stackitv1alpha1.OPENSTACK: @@ -756,7 +765,7 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf "enabled": false, } if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { - err := vp.deploySeedCSICompatibilityMode(ctx, cluster.Shoot.GetNamespace(), csiSTACKIT) + err := vp.deploySeedCSICompatibilityMode(ctx, cluster.Shoot.GetNamespace(), controlPlaneValues) if err != nil { return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) } @@ -765,15 +774,6 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf return nil, fmt.Errorf("unsupported storage CSI Driver: %s", storageCSIDriver) } - maps.Copy(controlPlaneValues, map[string]any{ - "global": map[string]any{ - "genericTokenKubeconfigSecretName": extensionscontroller.GenericTokenKubeconfigSecretNameFromCluster(cluster), - }, - openstack.CloudControllerManagerName: ccm, - openstack.STACKITCloudControllerManagerName: stackitccm, - stackit.PodIdentityWebhookName: podIdentityWebhook, - }) - if vp.deployALBIngressController { fmt.Println("deploying ALB Ingress Controller") albcm, err := getSTACKITALBCMChartValues(cpConfig, cluster, infra, stackitCredentialsConfig, apiEndpoints, scaledDown, stackitRegion) From 599e6c59d27b6c3d20b1f4012e229705b7340bcd Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 12:00:36 +0200 Subject: [PATCH 10/57] fix values? Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index b3de76a8..2a018f14 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1195,7 +1195,8 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na chartName := "stackit-blockstorage-csi-driver" - values["prefix"] = "stackit-compat" + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]string) + csiStackitValues["prefix"] = "stackit-compat" renderedChart, err := renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, namespace, values, From 69803f52af15ab7ac0072e2d034026ea4c76bf40 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 12:06:34 +0200 Subject: [PATCH 11/57] fix type assertion Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 2a018f14..68e71247 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1195,7 +1195,7 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na chartName := "stackit-blockstorage-csi-driver" - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]string) + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]interface{}) csiStackitValues["prefix"] = "stackit-compat" renderedChart, err := renderer.RenderEmbeddedFS( From 256fe3075b82ab867846ae08da1b57661c1ebdd8 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 12:11:30 +0200 Subject: [PATCH 12/57] fix namespace Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 68e71247..7f12b38e 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -765,7 +765,7 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf "enabled": false, } if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { - err := vp.deploySeedCSICompatibilityMode(ctx, cluster.Shoot.GetNamespace(), controlPlaneValues) + err := vp.deploySeedCSICompatibilityMode(ctx, cp.GetNamespace(), controlPlaneValues) if err != nil { return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) } From d8d047cbf58e608f15e83641ad9223dd0269e526 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 12:29:42 +0200 Subject: [PATCH 13/57] use different name for MR release Signed-off-by: Niclas Schad --- pkg/controller/controlplane/valuesprovider.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 7f12b38e..c852aaa2 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1207,7 +1207,7 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na data := map[string][]byte{chartName: renderedChart.Manifest()} return managedresources.Create( - ctx, vp.client, namespace, chartName, map[string]string{}, + ctx, vp.client, namespace, "stackit-csi-compat-chart", map[string]string{}, false, "seed", data, new(false), nil, new(false), ) } From 053f355b7e6c8418e2d75a41fceeb7947db82b52 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Mon, 15 Jun 2026 14:55:53 +0200 Subject: [PATCH 14/57] apply fixes and inject images manually Signed-off-by: Niclas Schad --- .../values.yaml | 1 - pkg/controller/controlplane/valuesprovider.go | 40 +++++++++++++++++-- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml index 60ff609a..02f0e30b 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml @@ -12,7 +12,6 @@ images: csi-resizer: image-repository:image-tag csi-liveness-probe: image-repository:image-tag csi-snapshot-controller: image-repository:image-tag - csi-snapshot-validation-webhook: image-repository:image-tag socketPath: /var/lib/csi/sockets/pluginproxy region: "" diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index c852aaa2..b242c926 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -764,6 +764,7 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf controlPlaneValues[openstack.CSIControllerName] = map[string]any{ "enabled": false, } + // TODO: make it nice if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { err := vp.deploySeedCSICompatibilityMode(ctx, cp.GetNamespace(), controlPlaneValues) if err != nil { @@ -1195,17 +1196,48 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na chartName := "stackit-blockstorage-csi-driver" - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]interface{}) - csiStackitValues["prefix"] = "stackit-compat" + foo := map[string]interface{}{} + foo = maps.Clone(values) + // Get the chart Values + csiStackitValues := foo[openstack.CSISTACKITControllerName].(map[string]interface{}) + // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key + chartValues := gardenerutils.MergeMaps(foo, csiStackitValues) + // Override chart values + chartValues["prefix"] = "stackit-compat" + + //TODO: Use gardener tools for this? If possible + imagesToFind := []string{ + "csi-driver-stackit", + "csi-provisioner", + "csi-attacher", + "csi-snapshotter", + "csi-resizer", + "csi-liveness-probe", + "csi-snapshot-controller"} + images := imagevector.ImageVector() + imageMap := make(map[string]interface{}) + + for _, image := range imagesToFind { + foundImage, err := images.FindImage(image) + if err != nil { + return err + } + imageMap[image] = foundImage.String() + } + chartValues["images"] = imageMap renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, namespace, values, + charts.InternalChart, + filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), + chartName, + namespace, + chartValues, ) if err != nil { return err } - data := map[string][]byte{chartName: renderedChart.Manifest()} + data := renderedChart.AsSecretData() return managedresources.Create( ctx, vp.client, namespace, "stackit-csi-compat-chart", map[string]string{}, false, "seed", data, new(false), nil, new(false), From d8f5d45f7d92e29d4d56144d1be3f4683849304f Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 16 Jun 2026 10:12:51 +0200 Subject: [PATCH 15/57] remove the duplicated DaemonSet as it is not needed --- .../templates/daemonset.yaml | 150 ------------------ .../values.yaml | 4 - 2 files changed, 154 deletions(-) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index e5e4e83d..cb04e3cf 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -144,153 +144,3 @@ spec: configMap: defaultMode: 420 name: {{ .Values.prefix }}-cloud-provider-config ---- -{{- if .Values.csiDriverCompatibility.enabled }} -apiVersion: apps/v1 -kind: DaemonSet -metadata: - name: {{ .Values.prefix }}-csi-driver-compatibility-node - namespace: {{ .Release.Namespace }} - labels: - node.gardener.cloud/critical-component: "true" - app: {{ .Values.prefix }}-csi-compatibility - role: disk-driver -spec: - selector: - matchLabels: - app: {{ .Values.prefix }}-csi-compatibility - role: disk-driver - template: - metadata: - annotations: - checksum/configmap-cloud-provider-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - node.gardener.cloud/wait-for-csi-node-stackit: block-storage.csi.stackit.cloud - labels: - node.gardener.cloud/critical-component: "true" - app: {{ .Values.prefix }}-csi-compatibility - role: disk-driver - spec: - hostNetwork: true - priorityClassName: system-node-critical - serviceAccountName: {{ .Values.prefix }}-csi-driver-node - tolerations: - - effect: NoSchedule - operator: Exists - - key: CriticalAddonsOnly - operator: Exists - - effect: NoExecute - operator: Exists - securityContext: - seccompProfile: - type: RuntimeDefault - containers: - - name: csi-driver-stackit - image: {{ index .Values.images "csi-driver-stackit" }} - args: - - /bin/stackit-csi-plugin - - --endpoint=$(CSI_ENDPOINT) - - --cloud-config=/etc/config/cloud.yaml - {{- range $userAgentHeader := .Values.userAgentHeaders }} - - --user-agent={{ $userAgentHeader }} - {{- end }} - - --v=2 - - --provide-controller-service=false - - --legacy-storage-mode=true - - --legacy-volume-creation={{ .Values.csiDriverCompatibility.legacyVolumeCreation }} - env: - - name: CSI_ENDPOINT - value: unix://{{ .Values.socketPath }} -{{- if .Values.resources.driver }} - resources: -{{ toYaml .Values.resources.driver | indent 10 }} -{{- end }} - securityContext: - privileged: true - capabilities: - add: ["SYS_ADMIN"] - allowPrivilegeEscalation: true - ports: - - name: healthz - containerPort: 9909 - protocol: TCP - livenessProbe: - httpGet: - path: /healthz - port: healthz - initialDelaySeconds: 10 - timeoutSeconds: 180 - periodSeconds: 30 - failureThreshold: 5 - volumeMounts: - - name: kubelet-dir - mountPath: /var/lib/kubelet - mountPropagation: "Bidirectional" - - name: plugin-dir - mountPath: /csi - - name: device-dir - mountPath: /dev - mountPropagation: "HostToContainer" - - name: cloud-provider-config - mountPath: /etc/config - - - name: csi-node-driver-registrar - image: {{ index .Values.images "csi-node-driver-registrar" }} - args: - - --csi-address=$(ADDRESS) - - --kubelet-registration-path=$(DRIVER_REG_SOCK_PATH) - - --v=5 - env: - - name: ADDRESS - value: {{ .Values.socketPath }} - - name: DRIVER_REG_SOCK_PATH - value: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/csi.sock -{{- if .Values.resources.nodeDriverRegistrar }} - resources: -{{ toYaml .Values.resources.nodeDriverRegistrar | indent 10 }} -{{- end }} - volumeMounts: - - name: plugin-dir - mountPath: /csi - - name: registration-dir - mountPath: /registration - securityContext: - allowPrivilegeEscalation: false - - - name: csi-liveness-probe - image: {{ index .Values.images "csi-liveness-probe" }} - args: - - --probe-timeout=3m - - --csi-address={{ .Values.socketPath }} - - --health-port=9909 -{{- if .Values.resources.livenessProbe }} - resources: -{{ toYaml .Values.resources.livenessProbe | indent 10 }} -{{- end }} - volumeMounts: - - name: plugin-dir - mountPath: /csi - securityContext: - allowPrivilegeEscalation: false - - volumes: - - name: kubelet-dir - hostPath: - path: /var/lib/kubelet - type: Directory - - name: plugin-dir - hostPath: - path: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/ - type: DirectoryOrCreate - - name: registration-dir - hostPath: - path: /var/lib/kubelet/plugins_registry/ - type: Directory - - name: device-dir - hostPath: - path: /dev - type: Directory - - name: cloud-provider-config - configMap: - defaultMode: 420 - name: {{ .Values.prefix }}-cloud-provider-config - {{- end }} diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml index 5eceba97..c5e073af 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml @@ -3,10 +3,6 @@ driverName: block-storage.csi.stackit.cloud rescanBlockStorageOnResize: "true" -csiDriverCompatibility: - enabled: false - legacyVolumeCreation: true - images: csi-driver-stackit: image-repository:image-tag csi-node-driver-registrar: image-repository:image-tag From 3992bb6ced9f251d26d920c7db34cd4aea274854 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 17 Jun 2026 13:52:34 +0200 Subject: [PATCH 16/57] add a symmetric method for the Shoot controlplane in Compatibility Mode --- pkg/controller/controlplane/valuesprovider.go | 73 +++++++++++++++++-- 1 file changed, 67 insertions(+), 6 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index b242c926..57f094a1 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1088,6 +1088,11 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c case stackitv1alpha1.OPENSTACK: values[openstack.CSINodeName] = csiNodeDriverValues values[openstack.CSISTACKITNodeName] = map[string]any{"enabled": false} + if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { + if err := vp.deployShootCSICompatibilityMode(ctx, cp.Namespace, values); err != nil { + return nil, fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) + } + } default: return nil, fmt.Errorf("unsupported CSI driver type: %s", csiDriverInUse) } @@ -1194,14 +1199,13 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na return nil } + // TODO: constant chartName := "stackit-blockstorage-csi-driver" - foo := map[string]interface{}{} - foo = maps.Clone(values) // Get the chart Values - csiStackitValues := foo[openstack.CSISTACKITControllerName].(map[string]interface{}) + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key - chartValues := gardenerutils.MergeMaps(foo, csiStackitValues) + chartValues := gardenerutils.MergeMaps(values, csiStackitValues) // Override chart values chartValues["prefix"] = "stackit-compat" @@ -1213,9 +1217,10 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na "csi-snapshotter", "csi-resizer", "csi-liveness-probe", - "csi-snapshot-controller"} + "csi-snapshot-controller", + } images := imagevector.ImageVector() - imageMap := make(map[string]interface{}) + imageMap := make(map[string]any) for _, image := range imagesToFind { foundImage, err := images.FindImage(image) @@ -1244,6 +1249,62 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na ) } +func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { + renderer, err := chartrenderer.NewForConfig(vp.config) + if err != nil { + return err + } + + // TODO: constant + chartName := "stackit-blockstorage-csi-driver" + + // Get the chart Values + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) + // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key + chartValues := gardenerutils.MergeMaps(values, csiStackitValues) + // Override chart values + chartValues["prefix"] = "stackit-compat" + + //TODO: Use gardener tools for this? If possible + imagesToFind := []string{ + "csi-driver-stackit", + "csi-provisioner", + "csi-attacher", + "csi-snapshotter", + "csi-resizer", + "csi-liveness-probe", + "csi-snapshot-controller", + } + images := imagevector.ImageVector() + imageMap := make(map[string]any) + + for _, image := range imagesToFind { + foundImage, err := images.FindImage(image) + if err != nil { + return err + } + imageMap[image] = foundImage.String() + } + chartValues["images"] = imageMap + + renderedChart, err := renderer.RenderEmbeddedFS( + charts.InternalChart, + filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), + chartName, + namespace, + chartValues, + ) + if err != nil { + return err + } + + data := renderedChart.AsSecretData() + return managedresources.Create( + ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", map[string]string{}, + false, "shoot", data, new(false), nil, new(false), + ) +} + // decodeLoadBalancerAPIEmergencySecret decodes a [corev1.Secret] for emergency loadbalancer access and // returns the apiURL and apiToken to use or an error. // The apiURL and apiToken are only set if both values exist inside the secret and are not empty. From 847086965e7cc86e9d78e68f1013f9884720b792 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 17 Jun 2026 14:13:51 +0200 Subject: [PATCH 17/57] use the correct image vector for the Shoot controlplane --- pkg/controller/controlplane/valuesprovider.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 57f094a1..af0a341b 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1268,12 +1268,8 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n //TODO: Use gardener tools for this? If possible imagesToFind := []string{ "csi-driver-stackit", - "csi-provisioner", - "csi-attacher", - "csi-snapshotter", - "csi-resizer", + "csi-node-driver-registrar", "csi-liveness-probe", - "csi-snapshot-controller", } images := imagevector.ImageVector() imageMap := make(map[string]any) From 38631b63b31d2c49ef135454ac5de7635bf6775f Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 18 Jun 2026 12:46:55 +0200 Subject: [PATCH 18/57] delete compatibility mode managed resources when not needed --- pkg/controller/controlplane/valuesprovider.go | 29 +++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index af0a341b..a8b30333 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -765,11 +765,17 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf "enabled": false, } // TODO: make it nice - if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { + if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { + // TODO: handle COMPATBLOCK err := vp.deploySeedCSICompatibilityMode(ctx, cp.GetNamespace(), controlPlaneValues) if err != nil { return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) } + } else { + err := vp.deleteSeedCSICompatibilityMode(ctx, cp.GetNamespace()) + if err != nil { + return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + } } default: return nil, fmt.Errorf("unsupported storage CSI Driver: %s", storageCSIDriver) @@ -1082,16 +1088,21 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c csiDriverInUse := getCSIDriver(cpConfig) switch csiDriverInUse { - case stackitv1alpha1.STACKIT: - values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues - values[openstack.CSINodeName] = map[string]any{"enabled": false} case stackitv1alpha1.OPENSTACK: values[openstack.CSINodeName] = csiNodeDriverValues values[openstack.CSISTACKITNodeName] = map[string]any{"enabled": false} - if getCSICompatibilityMode(cpConfig) == stackitv1alpha1.COMPAT { + case stackitv1alpha1.STACKIT: + values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues + values[openstack.CSINodeName] = map[string]any{"enabled": false} + if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { + // TODO: handle COMPATBLOCk if err := vp.deployShootCSICompatibilityMode(ctx, cp.Namespace, values); err != nil { return nil, fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) } + } else { + if err := vp.deleteShootCSICompatibilityMode(ctx, cp.Namespace); err != nil { + return nil, fmt.Errorf("delete shoot CSI compatibility mode: %w", err) + } } default: return nil, fmt.Errorf("unsupported CSI driver type: %s", csiDriverInUse) @@ -1249,6 +1260,10 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na ) } +func (vp *valuesProvider) deleteSeedCSICompatibilityMode(ctx context.Context, namespace string) error { + return managedresources.Delete(ctx, vp.client, namespace, "stackit-csi-compat-chart", false) +} + func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { renderer, err := chartrenderer.NewForConfig(vp.config) if err != nil { @@ -1301,6 +1316,10 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n ) } +func (vp *valuesProvider) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { + return managedresources.Delete(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", false) +} + // decodeLoadBalancerAPIEmergencySecret decodes a [corev1.Secret] for emergency loadbalancer access and // returns the apiURL and apiToken to use or an error. // The apiURL and apiToken are only set if both values exist inside the secret and are not empty. From da9924aed103dabe575771434899e01b37ade992 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 18 Jun 2026 15:31:04 +0200 Subject: [PATCH 19/57] control the legacy-volume-creation flag --- .../templates/daemonset.yaml | 10 ++++-- .../values.yaml | 6 ++++ pkg/controller/controlplane/valuesprovider.go | 31 ++++++++++--------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index cb04e3cf..103209a8 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -48,6 +48,12 @@ spec: {{- end }} - --v=2 - --provide-controller-service=false + {{- if .Values.csi.enableCompatibilityMode }} + - --legacy-storage-mode=true + {{- end }} + {{- if .Values.csi.blockLegacyCreation }} + - --legacy-volume-creation=false + {{- end }} env: - name: CSI_ENDPOINT value: unix://{{ .Values.socketPath }} @@ -62,7 +68,7 @@ spec: allowPrivilegeEscalation: true ports: - name: healthz - containerPort: 9908 + containerPort: {{ .Values.healthzPort }} protocol: TCP livenessProbe: httpGet: @@ -112,7 +118,7 @@ spec: args: - --probe-timeout=3m - --csi-address={{ .Values.socketPath }} - - --health-port=9908 + - --health-port={{ .Values.healthzPort }} {{- if .Values.resources.livenessProbe }} resources: {{ toYaml .Values.resources.livenessProbe | indent 10 }} diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml index c5e073af..d2ac0bad 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml @@ -3,6 +3,12 @@ driverName: block-storage.csi.stackit.cloud rescanBlockStorageOnResize: "true" +healthzPort: 9908 + +csi: + enableCompatibilityMode: false + blockLegacyCreation: false + images: csi-driver-stackit: image-repository:image-tag csi-node-driver-registrar: image-repository:image-tag diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index a8b30333..a0d5c182 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1094,9 +1094,10 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c case stackitv1alpha1.STACKIT: values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues values[openstack.CSINodeName] = map[string]any{"enabled": false} - if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { - // TODO: handle COMPATBLOCk - if err := vp.deployShootCSICompatibilityMode(ctx, cp.Namespace, values); err != nil { + compatibilityMode := getCSICompatibilityMode(cpConfig) + if compatibilityMode != stackitv1alpha1.DEFAULT { + blockLegacyCreation := (compatibilityMode == stackitv1alpha1.COMPATBLOCK) + if err := vp.deployShootCSICompatibilityMode(ctx, cp.Namespace, values, blockLegacyCreation); err != nil { return nil, fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) } } else { @@ -1254,17 +1255,14 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na } data := renderedChart.AsSecretData() - return managedresources.Create( - ctx, vp.client, namespace, "stackit-csi-compat-chart", map[string]string{}, - false, "seed", data, new(false), nil, new(false), - ) + return managedresources.CreateForSeed(ctx, vp.client, namespace, "stackit-csi-compat-chart", false, data) } func (vp *valuesProvider) deleteSeedCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.Delete(ctx, vp.client, namespace, "stackit-csi-compat-chart", false) + return managedresources.DeleteForSeed(ctx, vp.client, namespace, "stackit-csi-compat-chart") } -func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { +func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any, blockLegacyCreation bool) error { renderer, err := chartrenderer.NewForConfig(vp.config) if err != nil { return err @@ -1297,6 +1295,14 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n imageMap[image] = foundImage.String() } chartValues["images"] = imageMap + chartValues["healthzPort"] = 9909 + csiValues := map[string]any{ + "enableCompatibilityMode": true, + } + if blockLegacyCreation { + csiValues["blockLegacyCreation"] = true + } + chartValues["csi"] = csiValues renderedChart, err := renderer.RenderEmbeddedFS( charts.InternalChart, @@ -1310,14 +1316,11 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n } data := renderedChart.AsSecretData() - return managedresources.Create( - ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", map[string]string{}, - false, "shoot", data, new(false), nil, new(false), - ) + return managedresources.CreateForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", "gardener", false, data) } func (vp *valuesProvider) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.Delete(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", false) + return managedresources.DeleteForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart") } // decodeLoadBalancerAPIEmergencySecret decodes a [corev1.Secret] for emergency loadbalancer access and From 553c2071184e507a261dc5a89240a4a2eb322f7c Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 18 Jun 2026 16:50:55 +0200 Subject: [PATCH 20/57] make sure to use the correct namespace and origin --- pkg/controller/controlplane/valuesprovider.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index a0d5c182..80e67119 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -765,14 +765,14 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf "enabled": false, } // TODO: make it nice + namespace := cp.Namespace if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { - // TODO: handle COMPATBLOCK - err := vp.deploySeedCSICompatibilityMode(ctx, cp.GetNamespace(), controlPlaneValues) + err := vp.deploySeedCSICompatibilityMode(ctx, namespace, controlPlaneValues) if err != nil { return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) } } else { - err := vp.deleteSeedCSICompatibilityMode(ctx, cp.GetNamespace()) + err := vp.deleteSeedCSICompatibilityMode(ctx, namespace) if err != nil { return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) } @@ -1095,13 +1095,14 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues values[openstack.CSINodeName] = map[string]any{"enabled": false} compatibilityMode := getCSICompatibilityMode(cpConfig) + namespace := cp.Namespace if compatibilityMode != stackitv1alpha1.DEFAULT { - blockLegacyCreation := (compatibilityMode == stackitv1alpha1.COMPATBLOCK) - if err := vp.deployShootCSICompatibilityMode(ctx, cp.Namespace, values, blockLegacyCreation); err != nil { + blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK + if err := vp.deployShootCSICompatibilityMode(ctx, namespace, values, blockLegacyCreation); err != nil { return nil, fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) } } else { - if err := vp.deleteShootCSICompatibilityMode(ctx, cp.Namespace); err != nil { + if err := vp.deleteShootCSICompatibilityMode(ctx, namespace); err != nil { return nil, fmt.Errorf("delete shoot CSI compatibility mode: %w", err) } } @@ -1247,7 +1248,7 @@ func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, na charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, - namespace, + "kube-system", chartValues, ) if err != nil { @@ -1308,7 +1309,7 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n charts.InternalChart, filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), chartName, - namespace, + "kube-system", chartValues, ) if err != nil { @@ -1316,7 +1317,7 @@ func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, n } data := renderedChart.AsSecretData() - return managedresources.CreateForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", "gardener", false, data) + return managedresources.CreateForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", "gardener-extension-provider-stackit", false, data) } func (vp *valuesProvider) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { From eb9840b56f7e65832237471b7907a69624aabdbc Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Fri, 19 Jun 2026 13:08:54 +0200 Subject: [PATCH 21/57] make generate --- hack/api-reference/api.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md index 3b0c99a7..e1c00e93 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -268,6 +268,16 @@ string +

CSICompatibilityMode +

+

Underlying type: string

+ + +

+ +

+ +

CSIManila

From 680824cb909785a52a17e9a250f1012e0d511414 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 22 Jun 2026 15:42:49 +0200 Subject: [PATCH 22/57] extract the CSI compatibility related code and use dependency injections to minimize the impact on existing test cases --- pkg/controller/controlplane/add.go | 3 +- .../controlplane/csi_compatibility.go | 178 ++++++++++++++++++ pkg/controller/controlplane/valuesprovider.go | 159 ++-------------- .../controlplane/valuesprovider_test.go | 15 +- 4 files changed, 206 insertions(+), 149 deletions(-) create mode 100644 pkg/controller/controlplane/csi_compatibility.go diff --git a/pkg/controller/controlplane/add.go b/pkg/controller/controlplane/add.go index a0e11e6f..980766c9 100644 --- a/pkg/controller/controlplane/add.go +++ b/pkg/controller/controlplane/add.go @@ -46,7 +46,8 @@ func AddToManagerWithOptions(ctx context.Context, mgr manager.Manager, opts AddO genericActuator, err := genericactuator.NewActuator(mgr, stackit.Name, secretConfigsFunc, shootAccessSecretsFunc, configChart, controlPlaneChart, controlPlaneShootChart, controlPlaneShootCRDsChart, storageClassChart, - NewValuesProvider(mgr, DeployALBIngressController, opts.CustomLabelDomain), extensionscontroller.ChartRendererFactoryFunc(util.NewChartRendererForShoot), + NewValuesProvider(mgr, DeployALBIngressController, opts.CustomLabelDomain, NewCompatCSICompatibilityHandler(mgr.GetClient(), mgr.GetConfig())), + extensionscontroller.ChartRendererFactoryFunc(util.NewChartRendererForShoot), imagevector.ImageVector(), "", nil, opts.WebhookServerNamespace) if err != nil { return err diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go new file mode 100644 index 00000000..5dcb5986 --- /dev/null +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -0,0 +1,178 @@ +package controlplane + +import ( + "context" + "fmt" + "path/filepath" + + "github.com/gardener/gardener/pkg/chartrenderer" + gardenerutils "github.com/gardener/gardener/pkg/utils" + "github.com/gardener/gardener/pkg/utils/managedresources" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/charts" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/imagevector" + stackitv1alpha1 "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/apis/stackit/v1alpha1" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" +) + +func NewCompatCSICompatibilityHandler(client client.Client, config *rest.Config) *CompatCSICompatibilityHandler { + return &CompatCSICompatibilityHandler{ + client: client, + config: config, + } +} + +type CompatCSICompatibilityHandler struct { + client client.Client + config *rest.Config +} + +func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { + if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { + err := ch.deploySeedCSICompatibilityMode(ctx, namespace, controlPlaneValues) + if err != nil { + return fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + } + } else { + err := ch.deleteSeedCSICompatibilityMode(ctx, namespace) + if err != nil { + return fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + } + } + return nil +} + +func (ch *CompatCSICompatibilityHandler) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { + renderer, err := chartrenderer.NewForConfig(ch.config) + if err != nil { + return nil + } + + // TODO: constant + chartName := "stackit-blockstorage-csi-driver" + + // Get the chart Values + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) + // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key + chartValues := gardenerutils.MergeMaps(values, csiStackitValues) + // Override chart values + chartValues["prefix"] = "stackit-compat" + + //TODO: Use gardener tools for this? If possible + imagesToFind := []string{ + "csi-driver-stackit", + "csi-provisioner", + "csi-attacher", + "csi-snapshotter", + "csi-resizer", + "csi-liveness-probe", + "csi-snapshot-controller", + } + images := imagevector.ImageVector() + imageMap := make(map[string]any) + + for _, image := range imagesToFind { + foundImage, err := images.FindImage(image) + if err != nil { + return err + } + imageMap[image] = foundImage.String() + } + chartValues["images"] = imageMap + + renderedChart, err := renderer.RenderEmbeddedFS( + charts.InternalChart, + filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), + chartName, + "kube-system", + chartValues, + ) + if err != nil { + return err + } + + data := renderedChart.AsSecretData() + return managedresources.CreateForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart", false, data) +} + +func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx context.Context, namespace string) error { + return managedresources.DeleteForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart") +} + +func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { + compatibilityMode := getCSICompatibilityMode(cpConfig) + if compatibilityMode != stackitv1alpha1.DEFAULT { + blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK + if err := ch.deployShootCSICompatibilityMode(ctx, namespace, values, blockLegacyCreation); err != nil { + return fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) + } + } else { + if err := ch.deleteShootCSICompatibilityMode(ctx, namespace); err != nil { + return fmt.Errorf("delete shoot CSI compatibility mode: %w", err) + } + } + return nil +} + +func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any, blockLegacyCreation bool) error { + renderer, err := chartrenderer.NewForConfig(ch.config) + if err != nil { + return err + } + + // TODO: constant + chartName := "stackit-blockstorage-csi-driver" + + // Get the chart Values + csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) + // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key + chartValues := gardenerutils.MergeMaps(values, csiStackitValues) + // Override chart values + chartValues["prefix"] = "stackit-compat" + + //TODO: Use gardener tools for this? If possible + imagesToFind := []string{ + "csi-driver-stackit", + "csi-node-driver-registrar", + "csi-liveness-probe", + } + images := imagevector.ImageVector() + imageMap := make(map[string]any) + + for _, image := range imagesToFind { + foundImage, err := images.FindImage(image) + if err != nil { + return err + } + imageMap[image] = foundImage.String() + } + chartValues["images"] = imageMap + chartValues["healthzPort"] = 9909 + csiValues := map[string]any{ + "enableCompatibilityMode": true, + } + if blockLegacyCreation { + csiValues["blockLegacyCreation"] = true + } + chartValues["csi"] = csiValues + + renderedChart, err := renderer.RenderEmbeddedFS( + charts.InternalChart, + filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), + chartName, + "kube-system", + chartValues, + ) + if err != nil { + return err + } + + data := renderedChart.AsSecretData() + return managedresources.CreateForShoot(ctx, ch.client, namespace, "stackit-csi-compat-shoot-chart", "gardener-extension-provider-stackit", false, data) +} + +func (ch *CompatCSICompatibilityHandler) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { + return managedresources.DeleteForShoot(ctx, ch.client, namespace, "stackit-csi-compat-shoot-chart") +} diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 80e67119..ea9d5c94 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -21,12 +21,10 @@ import ( "github.com/gardener/gardener/pkg/apis/core/v1beta1" v1beta1constants "github.com/gardener/gardener/pkg/apis/core/v1beta1/constants" extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" - "github.com/gardener/gardener/pkg/chartrenderer" gardenerutils "github.com/gardener/gardener/pkg/utils" "github.com/gardener/gardener/pkg/utils/chart" gutil "github.com/gardener/gardener/pkg/utils/gardener" kutil "github.com/gardener/gardener/pkg/utils/kubernetes" - "github.com/gardener/gardener/pkg/utils/managedresources" secretutils "github.com/gardener/gardener/pkg/utils/secrets" secretsmanager "github.com/gardener/gardener/pkg/utils/secrets/manager" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" @@ -358,14 +356,19 @@ var ( } ) +type CSICompatibilityHandler interface { + HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error + HandleShootCSICompatility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error +} + // NewValuesProvider creates a new ValuesProvider for the generic actuator. -func NewValuesProvider(mgr manager.Manager, deployALBIngressController bool, customLabelDomain string) genericactuator.ValuesProvider { +func NewValuesProvider(mgr manager.Manager, deployALBIngressController bool, customLabelDomain string, csiCompatibilityHandler CSICompatibilityHandler) genericactuator.ValuesProvider { return &valuesProvider{ client: mgr.GetClient(), - config: mgr.GetConfig(), decoder: serializer.NewCodecFactory(mgr.GetScheme(), serializer.EnableStrict).UniversalDecoder(), deployALBIngressController: deployALBIngressController, customLabelDomain: customLabelDomain, + csiCompatibilityHandler: csiCompatibilityHandler, } } @@ -377,6 +380,7 @@ type valuesProvider struct { decoder runtime.Decoder deployALBIngressController bool customLabelDomain string + csiCompatibilityHandler CSICompatibilityHandler } // GetConfigChartValues returns the values for the config chart applied by the generic actuator. @@ -764,18 +768,9 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf controlPlaneValues[openstack.CSIControllerName] = map[string]any{ "enabled": false, } - // TODO: make it nice - namespace := cp.Namespace - if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { - err := vp.deploySeedCSICompatibilityMode(ctx, namespace, controlPlaneValues) - if err != nil { - return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) - } - } else { - err := vp.deleteSeedCSICompatibilityMode(ctx, namespace) - if err != nil { - return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) - } + err := vp.csiCompatibilityHandler.HandleSeedCSICompatibility(ctx, cp.Namespace, cpConfig, controlPlaneValues) + if err != nil { + return nil, err } default: return nil, fmt.Errorf("unsupported storage CSI Driver: %s", storageCSIDriver) @@ -1094,17 +1089,9 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c case stackitv1alpha1.STACKIT: values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues values[openstack.CSINodeName] = map[string]any{"enabled": false} - compatibilityMode := getCSICompatibilityMode(cpConfig) - namespace := cp.Namespace - if compatibilityMode != stackitv1alpha1.DEFAULT { - blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK - if err := vp.deployShootCSICompatibilityMode(ctx, namespace, values, blockLegacyCreation); err != nil { - return nil, fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) - } - } else { - if err := vp.deleteShootCSICompatibilityMode(ctx, namespace); err != nil { - return nil, fmt.Errorf("delete shoot CSI compatibility mode: %w", err) - } + err := vp.csiCompatibilityHandler.HandleShootCSICompatility(ctx, cp.Namespace, cpConfig, values) + if err != nil { + return nil, err } default: return nil, fmt.Errorf("unsupported CSI driver type: %s", csiDriverInUse) @@ -1206,124 +1193,6 @@ func (vp *valuesProvider) checkEmergencyLoadBalancerAccess(ctx context.Context, return apiURL, apiToken, nil } -func (vp *valuesProvider) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { - renderer, err := chartrenderer.NewForConfig(vp.config) - if err != nil { - return nil - } - - // TODO: constant - chartName := "stackit-blockstorage-csi-driver" - - // Get the chart Values - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) - // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key - chartValues := gardenerutils.MergeMaps(values, csiStackitValues) - // Override chart values - chartValues["prefix"] = "stackit-compat" - - //TODO: Use gardener tools for this? If possible - imagesToFind := []string{ - "csi-driver-stackit", - "csi-provisioner", - "csi-attacher", - "csi-snapshotter", - "csi-resizer", - "csi-liveness-probe", - "csi-snapshot-controller", - } - images := imagevector.ImageVector() - imageMap := make(map[string]any) - - for _, image := range imagesToFind { - foundImage, err := images.FindImage(image) - if err != nil { - return err - } - imageMap[image] = foundImage.String() - } - chartValues["images"] = imageMap - - renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, - filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), - chartName, - "kube-system", - chartValues, - ) - if err != nil { - return err - } - - data := renderedChart.AsSecretData() - return managedresources.CreateForSeed(ctx, vp.client, namespace, "stackit-csi-compat-chart", false, data) -} - -func (vp *valuesProvider) deleteSeedCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.DeleteForSeed(ctx, vp.client, namespace, "stackit-csi-compat-chart") -} - -func (vp *valuesProvider) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any, blockLegacyCreation bool) error { - renderer, err := chartrenderer.NewForConfig(vp.config) - if err != nil { - return err - } - - // TODO: constant - chartName := "stackit-blockstorage-csi-driver" - - // Get the chart Values - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) - // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key - chartValues := gardenerutils.MergeMaps(values, csiStackitValues) - // Override chart values - chartValues["prefix"] = "stackit-compat" - - //TODO: Use gardener tools for this? If possible - imagesToFind := []string{ - "csi-driver-stackit", - "csi-node-driver-registrar", - "csi-liveness-probe", - } - images := imagevector.ImageVector() - imageMap := make(map[string]any) - - for _, image := range imagesToFind { - foundImage, err := images.FindImage(image) - if err != nil { - return err - } - imageMap[image] = foundImage.String() - } - chartValues["images"] = imageMap - chartValues["healthzPort"] = 9909 - csiValues := map[string]any{ - "enableCompatibilityMode": true, - } - if blockLegacyCreation { - csiValues["blockLegacyCreation"] = true - } - chartValues["csi"] = csiValues - - renderedChart, err := renderer.RenderEmbeddedFS( - charts.InternalChart, - filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), - chartName, - "kube-system", - chartValues, - ) - if err != nil { - return err - } - - data := renderedChart.AsSecretData() - return managedresources.CreateForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart", "gardener-extension-provider-stackit", false, data) -} - -func (vp *valuesProvider) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.DeleteForShoot(ctx, vp.client, namespace, "stackit-csi-compat-shoot-chart") -} - // decodeLoadBalancerAPIEmergencySecret decodes a [corev1.Secret] for emergency loadbalancer access and // returns the apiURL and apiToken to use or an error. // The apiURL and apiToken are only set if both values exist inside the secret and are not empty. diff --git a/pkg/controller/controlplane/valuesprovider_test.go b/pkg/controller/controlplane/valuesprovider_test.go index 7ac628d5..9ef90bc7 100644 --- a/pkg/controller/controlplane/valuesprovider_test.go +++ b/pkg/controller/controlplane/valuesprovider_test.go @@ -313,7 +313,7 @@ var _ = Describe("ValuesProvider", func() { mgr = &testutils.FakeManager{Scheme: scheme, Client: c} - vp = NewValuesProvider(mgr, true, "kubernetes.io") + vp = NewValuesProvider(mgr, true, "kubernetes.io", new(noopCSICompatibilityHandler)) }) AfterEach(func() { @@ -665,7 +665,7 @@ var _ = Describe("ValuesProvider", func() { stackitCCMDeletion(ctx, c) } - vpStackitConf := NewValuesProvider(mgr, true, "kubernetes.io") + vpStackitConf := NewValuesProvider(mgr, true, "kubernetes.io", new(noopCSICompatibilityHandler)) values, err := vpStackitConf.GetControlPlaneChartValues(ctx, cp, &testCluster, fakeSecretsManager, checksums, false) Expect(err).NotTo(HaveOccurred()) Expect(values).To(HaveKey(openstack.STACKITCloudControllerManagerName)) @@ -777,7 +777,7 @@ var _ = Describe("ValuesProvider", func() { } testCluster.CloudProfile = cloudProfile - vpCustomDomain := NewValuesProvider(mgr, true, customDomain) + vpCustomDomain := NewValuesProvider(mgr, true, customDomain, new(noopCSICompatibilityHandler)) values, err := vpCustomDomain.GetControlPlaneChartValues(ctx, cp, &testCluster, fakeSecretsManager, checksums, false) Expect(err).NotTo(HaveOccurred()) @@ -1142,3 +1142,12 @@ func stackitCCMDeletion(ctx context.Context, c *mockclient.MockClient) { c.EXPECT().Delete(ctx, &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: openstack.STACKITCloudControllerManagerName, Namespace: namespace}}) c.EXPECT().Delete(ctx, &vpaautoscalingv1.VerticalPodAutoscaler{ObjectMeta: metav1.ObjectMeta{Name: openstack.STACKITCloudControllerManagerName + "-vpa", Namespace: namespace}}) } + +type noopCSICompatibilityHandler struct{} + +func (*noopCSICompatibilityHandler) HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { + return nil +} +func (*noopCSICompatibilityHandler) HandleShootCSICompatility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { + return nil +} From fc3d2b7f979bf1e8a15e2a7d57a90d76724b487f Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 22 Jun 2026 16:39:36 +0200 Subject: [PATCH 23/57] fix typo --- pkg/controller/controlplane/csi_compatibility.go | 2 +- pkg/controller/controlplane/valuesprovider.go | 6 +++--- pkg/controller/controlplane/valuesprovider_test.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 5dcb5986..e6c29b46 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -101,7 +101,7 @@ func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx cont return managedresources.DeleteForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart") } -func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { +func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) if compatibilityMode != stackitv1alpha1.DEFAULT { blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index ea9d5c94..8ca99d19 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -358,7 +358,7 @@ var ( type CSICompatibilityHandler interface { HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error - HandleShootCSICompatility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error + HandleShootCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error } // NewValuesProvider creates a new ValuesProvider for the generic actuator. @@ -1089,7 +1089,7 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c case stackitv1alpha1.STACKIT: values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues values[openstack.CSINodeName] = map[string]any{"enabled": false} - err := vp.csiCompatibilityHandler.HandleShootCSICompatility(ctx, cp.Namespace, cpConfig, values) + err := vp.csiCompatibilityHandler.HandleShootCSICompatibility(ctx, cp.Namespace, cpConfig, values) if err != nil { return nil, err } @@ -1234,7 +1234,7 @@ func getCSIDriver(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1. } func getCSICompatibilityMode(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.CSICompatibilityMode { - return stackitv1alpha1.CSICompatibilityMode(cpConfig.Storage.CSI.CompatibilityMode) + return stackitv1alpha1.CSICompatibilityMode(cpConfig.Storage.CSI.Name) } func getCCMController(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.ControllerName { diff --git a/pkg/controller/controlplane/valuesprovider_test.go b/pkg/controller/controlplane/valuesprovider_test.go index 9ef90bc7..5137d2db 100644 --- a/pkg/controller/controlplane/valuesprovider_test.go +++ b/pkg/controller/controlplane/valuesprovider_test.go @@ -1148,6 +1148,6 @@ type noopCSICompatibilityHandler struct{} func (*noopCSICompatibilityHandler) HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { return nil } -func (*noopCSICompatibilityHandler) HandleShootCSICompatility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { +func (*noopCSICompatibilityHandler) HandleShootCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { return nil } From 480a667648de2b1a9c7ceff8fe25aaabc4f4f1a9 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 13:07:43 +0200 Subject: [PATCH 24/57] add a unit test scaffolding --- .../controlplane/csi_compatibility_test.go | 234 ++++++++++++++++++ 1 file changed, 234 insertions(+) create mode 100644 pkg/controller/controlplane/csi_compatibility_test.go diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go new file mode 100644 index 00000000..da45c6ed --- /dev/null +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -0,0 +1,234 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package controlplane + +import ( + "bytes" + "context" + "io" + "net/http" + + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + "github.com/gardener/gardener/pkg/client/kubernetes" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + + stackitv1alpha1 "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/apis/stackit/v1alpha1" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" +) + +type mockRoundTripper struct{} + +func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { + var body string + switch req.URL.Path { + case "/version": + body = `{"major":"1","minor":"29","gitVersion":"v1.29.0"}` + case "/api": + body = `{"kind":"APIVersions","versions":["v1"]}` + case "/apis": + body = `{"kind":"APIGroupList","groups":[]}` + default: + body = `{"kind":"Status","status":"Failure","message":"Not Found","reason":"NotFound","code":404}` + } + + return &http.Response{ + StatusCode: http.StatusOK, + Header: http.Header{"Content-Type": []string{"application/json"}}, + Body: io.NopCloser(bytes.NewReader([]byte(body))), + }, nil +} + +var _ = Describe("CompatCSICompatibilityHandler", func() { + var ( + ctx context.Context + fakeClient client.Client + handler *CompatCSICompatibilityHandler + namespace string + config *rest.Config + ) + + BeforeEach(func() { + ctx = context.Background() + namespace = "test-namespace" + + fakeClient = fakeclient.NewClientBuilder(). + WithScheme(kubernetes.SeedScheme). + Build() + + config = &rest.Config{ + Host: "https://localhost", + Transport: &mockRoundTripper{}, + } + + handler = NewCompatCSICompatibilityHandler(fakeClient, config) + }) + + Describe("#HandleSeedCSICompatibility", func() { + Context("when CSICompatibilityMode is DEFAULT", func() { + It("should delete the managed resource", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.DEFAULT), + }, + }, + } + + // Create the managed resource and secret beforehand to ensure deletion works + mr := &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stackit-csi-compat-chart", + Namespace: namespace, + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "managedresource-stackit-csi-compat-chart", + Namespace: namespace, + }, + } + Expect(fakeClient.Create(ctx, mr)).To(Succeed()) + Expect(fakeClient.Create(ctx, secret)).To(Succeed()) + + err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, nil) + Expect(err).NotTo(HaveOccurred()) + + // Check deletion + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + Expect(client.IgnoreNotFound(err)).To(Succeed()) + Expect(err).ToNot(Succeed()) + }) + }) + + Context("when CSICompatibilityMode is COMPAT", func() { + It("should deploy the seed csi compatibility mode", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.COMPAT), + }, + }, + } + + controlPlaneValues := map[string]any{ + "global": map[string]any{ + "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", + }, + openstack.CSISTACKITControllerName: map[string]any{ + "foo": "bar", + }, + } + + err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + Expect(err).NotTo(HaveOccurred()) + + mr := &resourcesv1alpha1.ManagedResource{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) + + Describe("#HandleShootCSICompatibility", func() { + Context("when CSICompatibilityMode is DEFAULT", func() { + It("should delete the managed resource", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.DEFAULT), + }, + }, + } + + // Create the managed resource and secret beforehand to ensure deletion works + mr := &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: "stackit-csi-compat-shoot-chart", + Namespace: namespace, + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "managedresource-stackit-csi-compat-shoot-chart", + Namespace: namespace, + }, + } + Expect(fakeClient.Create(ctx, mr)).To(Succeed()) + Expect(fakeClient.Create(ctx, secret)).To(Succeed()) + + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, nil) + Expect(err).NotTo(HaveOccurred()) + + // Check deletion + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + Expect(client.IgnoreNotFound(err)).To(Succeed()) + Expect(err).ToNot(Succeed()) + }) + }) + + Context("when CSICompatibilityMode is COMPAT", func() { + It("should deploy the shoot csi compatibility mode with blockLegacyCreation = false", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.COMPAT), + }, + }, + } + + values := map[string]any{ + "global": map[string]any{ + "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", + }, + openstack.CSISTACKITControllerName: map[string]any{ + "foo": "bar", + }, + } + + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, values) + Expect(err).NotTo(HaveOccurred()) + + mr := &resourcesv1alpha1.ManagedResource{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + + Context("when CSICompatibilityMode is COMPATBLOCK", func() { + It("should deploy the shoot csi compatibility mode with blockLegacyCreation = true", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.COMPATBLOCK), + }, + }, + } + + values := map[string]any{ + "global": map[string]any{ + "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", + }, + openstack.CSISTACKITControllerName: map[string]any{ + "foo": "bar", + }, + } + + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, values) + Expect(err).NotTo(HaveOccurred()) + + mr := &resourcesv1alpha1.ManagedResource{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) +}) From f6159d0656ca8803acf878366630b44b3bb05c6e Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 14:26:16 +0200 Subject: [PATCH 25/57] add checks for the relevant command line flags in the DaemonSets --- .../controlplane/csi_compatibility_test.go | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index da45c6ed..6250a1f7 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -7,19 +7,23 @@ package controlplane import ( "bytes" "context" + "fmt" "io" "net/http" + "strings" resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" "github.com/gardener/gardener/pkg/client/kubernetes" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake" + "sigs.k8s.io/yaml" stackitv1alpha1 "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/apis/stackit/v1alpha1" "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" @@ -72,6 +76,38 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { handler = NewCompatCSICompatibilityHandler(fakeClient, config) }) + getDaemonSetFromSecret := func(prefix string) *appsv1.DaemonSet { + GinkgoHelper() + secretList := &corev1.SecretList{} + Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + var matchedSecret *corev1.Secret + var names []string + for _, s := range secretList.Items { + names = append(names, s.Name) + if strings.HasPrefix(s.Name, prefix) { + matchedSecret = &s + break + } + } + + if matchedSecret == nil { + Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) + } + + for _, data := range matchedSecret.Data { + docs := bytes.Split(data, []byte("\n---")) + for _, doc := range docs { + if bytes.Contains(doc, []byte("kind: DaemonSet")) { + ds := &appsv1.DaemonSet{} + Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) + return ds + } + } + } + Fail("DaemonSet not found in secret " + matchedSecret.Name) + return nil + } + Describe("#HandleSeedCSICompatibility", func() { Context("when CSICompatibilityMode is DEFAULT", func() { It("should delete the managed resource", func() { @@ -200,6 +236,18 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{} err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) + + ds := getDaemonSetFromSecret("managedresource-stackit-csi-compat-shoot-chart-") + var csiContainer *corev1.Container + for i := range ds.Spec.Template.Spec.Containers { + if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { + csiContainer = &ds.Spec.Template.Spec.Containers[i] + break + } + } + Expect(csiContainer).NotTo(BeNil(), "csi-driver-stackit container not found") + Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) + Expect(csiContainer.Args).NotTo(ContainElement("--legacy-volume-creation=false")) }) }) @@ -228,6 +276,18 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{} err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) + + ds := getDaemonSetFromSecret("managedresource-stackit-csi-compat-shoot-chart-") + var csiContainer *corev1.Container + for i := range ds.Spec.Template.Spec.Containers { + if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { + csiContainer = &ds.Spec.Template.Spec.Containers[i] + break + } + } + Expect(csiContainer).NotTo(BeNil(), "csi-driver-stackit container not found") + Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) + Expect(csiContainer.Args).To(ContainElement("--legacy-volume-creation=false")) }) }) }) From faa4eddd97392654ba4fe1c7f0ba24c28c2391fc Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 14:32:58 +0200 Subject: [PATCH 26/57] extract function --- .../controlplane/csi_compatibility_test.go | 98 ++++++++++++------- 1 file changed, 65 insertions(+), 33 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index 6250a1f7..d340c889 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -76,37 +76,37 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { handler = NewCompatCSICompatibilityHandler(fakeClient, config) }) - getDaemonSetFromSecret := func(prefix string) *appsv1.DaemonSet { - GinkgoHelper() - secretList := &corev1.SecretList{} - Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) - var matchedSecret *corev1.Secret - var names []string - for _, s := range secretList.Items { - names = append(names, s.Name) - if strings.HasPrefix(s.Name, prefix) { - matchedSecret = &s - break - } - } - - if matchedSecret == nil { - Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) - } - - for _, data := range matchedSecret.Data { - docs := bytes.Split(data, []byte("\n---")) - for _, doc := range docs { - if bytes.Contains(doc, []byte("kind: DaemonSet")) { - ds := &appsv1.DaemonSet{} - Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) - return ds - } - } - } - Fail("DaemonSet not found in secret " + matchedSecret.Name) - return nil - } + // getDaemonSetFromSecret := func(prefix string) *appsv1.DaemonSet { + // GinkgoHelper() + // secretList := &corev1.SecretList{} + // Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + // var matchedSecret *corev1.Secret + // var names []string + // for _, s := range secretList.Items { + // names = append(names, s.Name) + // if strings.HasPrefix(s.Name, prefix) { + // matchedSecret = &s + // break + // } + // } + + // if matchedSecret == nil { + // Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) + // } + + // for _, data := range matchedSecret.Data { + // docs := bytes.Split(data, []byte("\n---")) + // for _, doc := range docs { + // if bytes.Contains(doc, []byte("kind: DaemonSet")) { + // ds := &appsv1.DaemonSet{} + // Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) + // return ds + // } + // } + // } + // Fail("DaemonSet not found in secret " + matchedSecret.Name) + // return nil + // } Describe("#HandleSeedCSICompatibility", func() { Context("when CSICompatibilityMode is DEFAULT", func() { @@ -237,7 +237,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - ds := getDaemonSetFromSecret("managedresource-stackit-csi-compat-shoot-chart-") + ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-stackit-csi-compat-shoot-chart-") var csiContainer *corev1.Container for i := range ds.Spec.Template.Spec.Containers { if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { @@ -277,7 +277,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - ds := getDaemonSetFromSecret("managedresource-stackit-csi-compat-shoot-chart-") + ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-stackit-csi-compat-shoot-chart-") var csiContainer *corev1.Container for i := range ds.Spec.Template.Spec.Containers { if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { @@ -292,3 +292,35 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }) }) }) + +func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, namespace string, prefix string) *appsv1.DaemonSet { + GinkgoHelper() + secretList := &corev1.SecretList{} + Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + var matchedSecret *corev1.Secret + var names []string + for _, s := range secretList.Items { + names = append(names, s.Name) + if strings.HasPrefix(s.Name, prefix) { + matchedSecret = &s + break + } + } + + if matchedSecret == nil { + Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) + } + + for _, data := range matchedSecret.Data { + docs := bytes.Split(data, []byte("\n---")) + for _, doc := range docs { + if bytes.Contains(doc, []byte("kind: DaemonSet")) { + ds := &appsv1.DaemonSet{} + Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) + return ds + } + } + } + Fail("DaemonSet not found in secret " + matchedSecret.Name) + return nil +} From 3b4737fbf55c547d44ff634a23e3e0d5f6a48ae7 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 14:57:33 +0200 Subject: [PATCH 27/57] separate chart rendering and deployment --- .../controlplane/csi_compatibility.go | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index e6c29b46..ecc548b1 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -31,23 +31,27 @@ type CompatCSICompatibilityHandler struct { func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { - err := ch.deploySeedCSICompatibilityMode(ctx, namespace, controlPlaneValues) + chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues) if err != nil { - return fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) + } + err = ch.deploySeedCSICompatibilityMode(ctx, namespace, chart) + if err != nil { + return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) } } else { err := ch.deleteSeedCSICompatibilityMode(ctx, namespace) if err != nil { - return fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) } } return nil } -func (ch *CompatCSICompatibilityHandler) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any) error { +func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { renderer, err := chartrenderer.NewForConfig(ch.config) if err != nil { - return nil + return nil, err } // TODO: constant @@ -76,23 +80,22 @@ func (ch *CompatCSICompatibilityHandler) deploySeedCSICompatibilityMode(ctx cont for _, image := range imagesToFind { foundImage, err := images.FindImage(image) if err != nil { - return err + return nil, err } imageMap[image] = foundImage.String() } chartValues["images"] = imageMap - renderedChart, err := renderer.RenderEmbeddedFS( + return renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, "kube-system", chartValues, ) - if err != nil { - return err - } +} +func (ch *CompatCSICompatibilityHandler) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, renderedChart *chartrenderer.RenderedChart) error { data := renderedChart.AsSecretData() return managedresources.CreateForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart", false, data) } @@ -105,21 +108,27 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context compatibilityMode := getCSICompatibilityMode(cpConfig) if compatibilityMode != stackitv1alpha1.DEFAULT { blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK - if err := ch.deployShootCSICompatibilityMode(ctx, namespace, values, blockLegacyCreation); err != nil { + chart, err := ch.renderShootCSICompatibilityMode(values, blockLegacyCreation) + if err != nil { + return fmt.Errorf("render shoot CSI compatibility mode: %w", err) + } + err = ch.deployShootCSICompatibilityMode(ctx, namespace, chart) + if err != nil { return fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) } } else { - if err := ch.deleteShootCSICompatibilityMode(ctx, namespace); err != nil { + err := ch.deleteShootCSICompatibilityMode(ctx, namespace) + if err != nil { return fmt.Errorf("delete shoot CSI compatibility mode: %w", err) } } return nil } -func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx context.Context, namespace string, values map[string]any, blockLegacyCreation bool) error { +func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { renderer, err := chartrenderer.NewForConfig(ch.config) if err != nil { - return err + return nil, err } // TODO: constant @@ -144,7 +153,7 @@ func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx con for _, image := range imagesToFind { foundImage, err := images.FindImage(image) if err != nil { - return err + return nil, err } imageMap[image] = foundImage.String() } @@ -158,17 +167,16 @@ func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx con } chartValues["csi"] = csiValues - renderedChart, err := renderer.RenderEmbeddedFS( + return renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), chartName, "kube-system", chartValues, ) - if err != nil { - return err - } +} +func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx context.Context, namespace string, renderedChart *chartrenderer.RenderedChart) error { data := renderedChart.AsSecretData() return managedresources.CreateForShoot(ctx, ch.client, namespace, "stackit-csi-compat-shoot-chart", "gardener-extension-provider-stackit", false, data) } From 6dd7153aa8aa1bc232026b8782117d061d359f54 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 16:14:07 +0200 Subject: [PATCH 28/57] create the renderer only once --- pkg/controller/controlplane/add.go | 6 +++- .../controlplane/csi_compatibility.go | 30 ++++++++----------- .../controlplane/csi_compatibility_test.go | 2 +- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/pkg/controller/controlplane/add.go b/pkg/controller/controlplane/add.go index 980766c9..ab687708 100644 --- a/pkg/controller/controlplane/add.go +++ b/pkg/controller/controlplane/add.go @@ -43,10 +43,14 @@ type AddOptions struct { // AddToManagerWithOptions adds a controller with the given Options to the given manager. // The opts.Reconciler is being set with a newly instantiated actuator. func AddToManagerWithOptions(ctx context.Context, mgr manager.Manager, opts AddOptions) error { + csiCompatibilityHandler, err := NewCompatCSICompatibilityHandler(mgr.GetClient(), mgr.GetConfig()) + if err != nil { + return err + } genericActuator, err := genericactuator.NewActuator(mgr, stackit.Name, secretConfigsFunc, shootAccessSecretsFunc, configChart, controlPlaneChart, controlPlaneShootChart, controlPlaneShootCRDsChart, storageClassChart, - NewValuesProvider(mgr, DeployALBIngressController, opts.CustomLabelDomain, NewCompatCSICompatibilityHandler(mgr.GetClient(), mgr.GetConfig())), + NewValuesProvider(mgr, DeployALBIngressController, opts.CustomLabelDomain, csiCompatibilityHandler), extensionscontroller.ChartRendererFactoryFunc(util.NewChartRendererForShoot), imagevector.ImageVector(), "", nil, opts.WebhookServerNamespace) if err != nil { diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index ecc548b1..d7414115 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -17,16 +17,20 @@ import ( "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" ) -func NewCompatCSICompatibilityHandler(client client.Client, config *rest.Config) *CompatCSICompatibilityHandler { - return &CompatCSICompatibilityHandler{ - client: client, - config: config, +func NewCompatCSICompatibilityHandler(client client.Client, config *rest.Config) (*CompatCSICompatibilityHandler, error) { + renderer, err := chartrenderer.NewForConfig(config) + if err != nil { + return nil, err } + return &CompatCSICompatibilityHandler{ + client: client, + renderer: renderer, + }, nil } type CompatCSICompatibilityHandler struct { - client client.Client - config *rest.Config + client client.Client + renderer chartrenderer.Interface } func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { @@ -49,11 +53,6 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. } func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { - renderer, err := chartrenderer.NewForConfig(ch.config) - if err != nil { - return nil, err - } - // TODO: constant chartName := "stackit-blockstorage-csi-driver" @@ -86,7 +85,7 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m } chartValues["images"] = imageMap - return renderer.RenderEmbeddedFS( + return ch.renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), chartName, @@ -126,11 +125,6 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context } func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { - renderer, err := chartrenderer.NewForConfig(ch.config) - if err != nil { - return nil, err - } - // TODO: constant chartName := "stackit-blockstorage-csi-driver" @@ -167,7 +161,7 @@ func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values } chartValues["csi"] = csiValues - return renderer.RenderEmbeddedFS( + return ch.renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), chartName, diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index d340c889..b8e68841 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -73,7 +73,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Transport: &mockRoundTripper{}, } - handler = NewCompatCSICompatibilityHandler(fakeClient, config) + handler, _ = NewCompatCSICompatibilityHandler(fakeClient, config) }) // getDaemonSetFromSecret := func(prefix string) *appsv1.DaemonSet { From d15c80042e16cbfee42705ff11648616b78c5446 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 23 Jun 2026 16:36:30 +0200 Subject: [PATCH 29/57] fix the compatibility mode accessor --- pkg/controller/controlplane/csi_compatibility.go | 3 ++- pkg/controller/controlplane/valuesprovider.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index d7414115..1b9a72f3 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -34,7 +34,8 @@ type CompatCSICompatibilityHandler struct { } func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { - if getCSICompatibilityMode(cpConfig) != stackitv1alpha1.DEFAULT { + compatibilityMode := getCSICompatibilityMode(cpConfig) + if compatibilityMode != stackitv1alpha1.DEFAULT { chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues) if err != nil { return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 8ca99d19..86a2768f 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -1234,7 +1234,7 @@ func getCSIDriver(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1. } func getCSICompatibilityMode(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.CSICompatibilityMode { - return stackitv1alpha1.CSICompatibilityMode(cpConfig.Storage.CSI.Name) + return stackitv1alpha1.CSICompatibilityMode(cpConfig.Storage.CSI.CompatibilityMode) } func getCCMController(cpConfig *stackitv1alpha1.ControlPlaneConfig) stackitv1alpha1.ControllerName { From 88c40ea4692fa77875963273b297fa0b00ef6aa5 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 24 Jun 2026 09:42:12 +0200 Subject: [PATCH 30/57] simplify test assertions --- pkg/controller/controlplane/csi_compatibility_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index b8e68841..fad75411 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -18,6 +18,7 @@ import ( . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" @@ -140,8 +141,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { // Check deletion err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) - Expect(client.IgnoreNotFound(err)).To(Succeed()) - Expect(err).ToNot(Succeed()) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) }) }) @@ -206,8 +206,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { // Check deletion err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) - Expect(client.IgnoreNotFound(err)).To(Succeed()) - Expect(err).ToNot(Succeed()) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) }) }) From 7ece65f8434b79a34b35a2c8a77714f51c452eb2 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 24 Jun 2026 11:16:58 +0200 Subject: [PATCH 31/57] extract helper methods and constants --- .../controlplane/csi_compatibility.go | 96 ++++++++------- .../controlplane/csi_compatibility_test.go | 111 ++++++------------ 2 files changed, 88 insertions(+), 119 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 1b9a72f3..5c365765 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -17,6 +17,13 @@ import ( "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" ) +const ( + csiDriverChartName = "stackit-blockstorage-csi-driver" + csiCompatibilityPrefix = "stackit-csi-compat" + csiCompatSeedChartName = csiCompatibilityPrefix + "-chart" + csiCompatShootChartName = csiCompatibilityPrefix + "-shoort-chart" +) + func NewCompatCSICompatibilityHandler(client client.Client, config *rest.Config) (*CompatCSICompatibilityHandler, error) { renderer, err := chartrenderer.NewForConfig(config) if err != nil { @@ -54,18 +61,12 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. } func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { - // TODO: constant - chartName := "stackit-blockstorage-csi-driver" + chartValues := composeCompatibilityChartValues(values) - // Get the chart Values - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) - // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key - chartValues := gardenerutils.MergeMaps(values, csiStackitValues) // Override chart values - chartValues["prefix"] = "stackit-compat" + chartValues["prefix"] = csiCompatibilityPrefix - //TODO: Use gardener tools for this? If possible - imagesToFind := []string{ + imageMap, err := findImages( "csi-driver-stackit", "csi-provisioner", "csi-attacher", @@ -73,23 +74,16 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m "csi-resizer", "csi-liveness-probe", "csi-snapshot-controller", - } - images := imagevector.ImageVector() - imageMap := make(map[string]any) - - for _, image := range imagesToFind { - foundImage, err := images.FindImage(image) - if err != nil { - return nil, err - } - imageMap[image] = foundImage.String() + ) + if err != nil { + return nil, err } chartValues["images"] = imageMap return ch.renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), - chartName, + csiDriverChartName, "kube-system", chartValues, ) @@ -97,11 +91,11 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m func (ch *CompatCSICompatibilityHandler) deploySeedCSICompatibilityMode(ctx context.Context, namespace string, renderedChart *chartrenderer.RenderedChart) error { data := renderedChart.AsSecretData() - return managedresources.CreateForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart", false, data) + return managedresources.CreateForSeed(ctx, ch.client, namespace, csiCompatSeedChartName, false, data) } func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.DeleteForSeed(ctx, ch.client, namespace, "stackit-csi-compat-chart") + return client.IgnoreNotFound(managedresources.DeleteForSeed(ctx, ch.client, namespace, csiCompatSeedChartName)) } func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { @@ -126,33 +120,21 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context } func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { - // TODO: constant - chartName := "stackit-blockstorage-csi-driver" + chartValues := composeCompatibilityChartValues(values) - // Get the chart Values - csiStackitValues := values[openstack.CSISTACKITControllerName].(map[string]any) - // Merge csiStackitValues to topLevel. Basically removes the openstack.CSISTACKITControllerName key - chartValues := gardenerutils.MergeMaps(values, csiStackitValues) // Override chart values - chartValues["prefix"] = "stackit-compat" + chartValues["prefix"] = csiCompatibilityPrefix - //TODO: Use gardener tools for this? If possible - imagesToFind := []string{ + imageMap, err := findImages( "csi-driver-stackit", "csi-node-driver-registrar", "csi-liveness-probe", - } - images := imagevector.ImageVector() - imageMap := make(map[string]any) - - for _, image := range imagesToFind { - foundImage, err := images.FindImage(image) - if err != nil { - return nil, err - } - imageMap[image] = foundImage.String() + ) + if err != nil { + return nil, err } chartValues["images"] = imageMap + chartValues["healthzPort"] = 9909 csiValues := map[string]any{ "enableCompatibilityMode": true, @@ -165,7 +147,7 @@ func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values return ch.renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "shoot-system-components/charts/stackit-blockstorage-csi-driver"), - chartName, + csiDriverChartName, "kube-system", chartValues, ) @@ -173,9 +155,35 @@ func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values func (ch *CompatCSICompatibilityHandler) deployShootCSICompatibilityMode(ctx context.Context, namespace string, renderedChart *chartrenderer.RenderedChart) error { data := renderedChart.AsSecretData() - return managedresources.CreateForShoot(ctx, ch.client, namespace, "stackit-csi-compat-shoot-chart", "gardener-extension-provider-stackit", false, data) + return managedresources.CreateForShoot(ctx, ch.client, namespace, csiCompatShootChartName, "gardener-extension-provider-stackit", false, data) } func (ch *CompatCSICompatibilityHandler) deleteShootCSICompatibilityMode(ctx context.Context, namespace string) error { - return managedresources.DeleteForShoot(ctx, ch.client, namespace, "stackit-csi-compat-shoot-chart") + return client.IgnoreNotFound(managedresources.DeleteForShoot(ctx, ch.client, namespace, csiCompatShootChartName)) +} + +// composeCompatibilityChartValues returns a copy of the given values map merged with the csiStackitValues on topLevel. +// Basically removes the openstack.CSISTACKITControllerName key +func composeCompatibilityChartValues(values map[string]any) map[string]any { + if values == nil { + return map[string]any{} + } + csiStackitValues, ok := values[openstack.CSISTACKITControllerName].(map[string]any) + if !ok { + csiStackitValues = nil + } + return gardenerutils.MergeMaps(values, csiStackitValues) +} + +func findImages(imagesToFind ...string) (map[string]any, error) { + images := imagevector.ImageVector() + result := make(map[string]any) + for _, image := range imagesToFind { + foundImage, err := images.FindImage(image) + if err != nil { + return nil, err + } + result[image] = foundImage.String() + } + return result, nil } diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index fad75411..dc9863d4 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -54,11 +54,12 @@ func (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) var _ = Describe("CompatCSICompatibilityHandler", func() { var ( - ctx context.Context - fakeClient client.Client - handler *CompatCSICompatibilityHandler - namespace string - config *rest.Config + ctx context.Context + fakeClient client.Client + handler *CompatCSICompatibilityHandler + namespace string + config *rest.Config + controlPlaneValues map[string]any ) BeforeEach(func() { @@ -75,39 +76,16 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { } handler, _ = NewCompatCSICompatibilityHandler(fakeClient, config) - }) - // getDaemonSetFromSecret := func(prefix string) *appsv1.DaemonSet { - // GinkgoHelper() - // secretList := &corev1.SecretList{} - // Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) - // var matchedSecret *corev1.Secret - // var names []string - // for _, s := range secretList.Items { - // names = append(names, s.Name) - // if strings.HasPrefix(s.Name, prefix) { - // matchedSecret = &s - // break - // } - // } - - // if matchedSecret == nil { - // Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) - // } - - // for _, data := range matchedSecret.Data { - // docs := bytes.Split(data, []byte("\n---")) - // for _, doc := range docs { - // if bytes.Contains(doc, []byte("kind: DaemonSet")) { - // ds := &appsv1.DaemonSet{} - // Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) - // return ds - // } - // } - // } - // Fail("DaemonSet not found in secret " + matchedSecret.Name) - // return nil - // } + controlPlaneValues = map[string]any{ + "global": map[string]any{ + "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", + }, + openstack.CSISTACKITControllerName: map[string]any{ + "foo": "bar", + }, + } + }) Describe("#HandleSeedCSICompatibility", func() { Context("when CSICompatibilityMode is DEFAULT", func() { @@ -115,7 +93,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ - Name: string(stackitv1alpha1.DEFAULT), + CompatibilityMode: string(stackitv1alpha1.DEFAULT), }, }, } @@ -123,24 +101,25 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { // Create the managed resource and secret beforehand to ensure deletion works mr := &resourcesv1alpha1.ManagedResource{ ObjectMeta: metav1.ObjectMeta{ - Name: "stackit-csi-compat-chart", - Namespace: namespace, + Name: csiCompatSeedChartName, + Namespace: "kube-system", }, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "managedresource-stackit-csi-compat-chart", - Namespace: namespace, + Name: "managedresource-" + csiCompatSeedChartName, + Namespace: "kube-system", }, } Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, nil) + err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatSeedChartName, Namespace: namespace}, mr) + Expect(err).To(HaveOccurred()) Expect(apierrors.IsNotFound(err)).To(BeTrue()) }) }) @@ -150,20 +129,11 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ - Name: string(stackitv1alpha1.COMPAT), + CompatibilityMode: string(stackitv1alpha1.COMPAT), }, }, } - controlPlaneValues := map[string]any{ - "global": map[string]any{ - "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", - }, - openstack.CSISTACKITControllerName: map[string]any{ - "foo": "bar", - }, - } - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) @@ -180,7 +150,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ - Name: string(stackitv1alpha1.DEFAULT), + CompatibilityMode: string(stackitv1alpha1.DEFAULT), }, }, } @@ -188,24 +158,24 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { // Create the managed resource and secret beforehand to ensure deletion works mr := &resourcesv1alpha1.ManagedResource{ ObjectMeta: metav1.ObjectMeta{ - Name: "stackit-csi-compat-shoot-chart", + Name: csiCompatShootChartName, Namespace: namespace, }, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Name: "managedresource-stackit-csi-compat-shoot-chart", + Name: "managedresource-" + csiCompatShootChartName, Namespace: namespace, }, } Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, nil) + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatShootChartName, Namespace: namespace}, mr) Expect(apierrors.IsNotFound(err)).To(BeTrue()) }) }) @@ -215,28 +185,19 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ - Name: string(stackitv1alpha1.COMPAT), + CompatibilityMode: string(stackitv1alpha1.COMPAT), }, }, } - values := map[string]any{ - "global": map[string]any{ - "genericTokenKubeconfigSecretName": "generic-token-kubeconfig-92e9ae14", - }, - openstack.CSISTACKITControllerName: map[string]any{ - "foo": "bar", - }, - } - - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, values) + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatShootChartName, Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-stackit-csi-compat-shoot-chart-") + ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatShootChartName) var csiContainer *corev1.Container for i := range ds.Spec.Template.Spec.Containers { if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { @@ -255,7 +216,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ - Name: string(stackitv1alpha1.COMPATBLOCK), + CompatibilityMode: string(stackitv1alpha1.COMPATBLOCK), }, }, } @@ -273,10 +234,10 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-shoot-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatShootChartName, Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-stackit-csi-compat-shoot-chart-") + ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatShootChartName) var csiContainer *corev1.Container for i := range ds.Spec.Template.Spec.Containers { if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { From ed07511a9d014e88f5127cf4e136e9ab49cca1ed Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 24 Jun 2026 13:26:31 +0200 Subject: [PATCH 32/57] handle unset compatibility mode --- .../controlplane/csi_compatibility.go | 10 +-- .../controlplane/csi_compatibility_test.go | 71 +++++++++++++++++++ 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 5c365765..b3344075 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -42,7 +42,8 @@ type CompatCSICompatibilityHandler struct { func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) - if compatibilityMode != stackitv1alpha1.DEFAULT { + switch compatibilityMode { + case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues) if err != nil { return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) @@ -51,7 +52,7 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. if err != nil { return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) } - } else { + default: err := ch.deleteSeedCSICompatibilityMode(ctx, namespace) if err != nil { return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) @@ -100,7 +101,8 @@ func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx cont func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) - if compatibilityMode != stackitv1alpha1.DEFAULT { + switch compatibilityMode { + case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK chart, err := ch.renderShootCSICompatibilityMode(values, blockLegacyCreation) if err != nil { @@ -110,7 +112,7 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context if err != nil { return fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) } - } else { + default: err := ch.deleteShootCSICompatibilityMode(ctx, namespace) if err != nil { return fmt.Errorf("delete shoot CSI compatibility mode: %w", err) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index dc9863d4..bf5ba7d6 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -124,6 +124,42 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }) }) + Context("when CSICompatibilityMode is not set", func() { + It("should delete the managed resource", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + CompatibilityMode: "", + }, + }, + } + + // Create the managed resource and secret beforehand to ensure deletion works + mr := &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: csiCompatSeedChartName, + Namespace: "kube-system", + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "managedresource-" + csiCompatSeedChartName, + Namespace: "kube-system", + }, + } + Expect(fakeClient.Create(ctx, mr)).To(Succeed()) + Expect(fakeClient.Create(ctx, secret)).To(Succeed()) + + err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + Expect(err).NotTo(HaveOccurred()) + + // Check deletion + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatSeedChartName, Namespace: namespace}, mr) + Expect(err).To(HaveOccurred()) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }) + }) + Context("when CSICompatibilityMode is COMPAT", func() { It("should deploy the seed csi compatibility mode", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ @@ -180,6 +216,41 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }) }) + Context("when CSICompatibilityMode is not set", func() { + It("should delete the managed resource", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + CompatibilityMode: "", + }, + }, + } + + // Create the managed resource and secret beforehand to ensure deletion works + mr := &resourcesv1alpha1.ManagedResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: csiCompatShootChartName, + Namespace: namespace, + }, + } + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: "managedresource-" + csiCompatShootChartName, + Namespace: namespace, + }, + } + Expect(fakeClient.Create(ctx, mr)).To(Succeed()) + Expect(fakeClient.Create(ctx, secret)).To(Succeed()) + + err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + Expect(err).NotTo(HaveOccurred()) + + // Check deletion + err = fakeClient.Get(ctx, types.NamespacedName{Name: csiCompatShootChartName, Namespace: namespace}, mr) + Expect(apierrors.IsNotFound(err)).To(BeTrue()) + }) + }) + Context("when CSICompatibilityMode is COMPAT", func() { It("should deploy the shoot csi compatibility mode with blockLegacyCreation = false", func() { cpConfig := &stackitv1alpha1.ControlPlaneConfig{ From 6116e66b5ac6737c1d4182b8982efb5c52015043 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 24 Jun 2026 13:32:20 +0200 Subject: [PATCH 33/57] go mod tidy --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 297897e4..e7cf1a54 100644 --- a/go.mod +++ b/go.mod @@ -45,6 +45,7 @@ require ( k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 sigs.k8s.io/controller-runtime v0.23.3 sigs.k8s.io/controller-runtime/tools/setup-envtest v0.0.0-20231015215740-bf15e44028f9 + sigs.k8s.io/yaml v1.6.0 ) require ( @@ -214,5 +215,4 @@ require ( sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect - sigs.k8s.io/yaml v1.6.0 // indirect ) From 89bee297a4a27e2ef855717e60fc80e96689b5db Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 24 Jun 2026 13:37:36 +0200 Subject: [PATCH 34/57] modernize --- pkg/controller/controlplane/csi_compatibility_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index bf5ba7d6..d1d6f2a7 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -340,11 +340,12 @@ func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, names if matchedSecret == nil { Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) + return nil // will never be reached, but makes the linter very happy } for _, data := range matchedSecret.Data { - docs := bytes.Split(data, []byte("\n---")) - for _, doc := range docs { + docs := bytes.SplitSeq(data, []byte("\n---")) + for doc := range docs { if bytes.Contains(doc, []byte("kind: DaemonSet")) { ds := &appsv1.DaemonSet{} Expect(yaml.Unmarshal(doc, ds)).To(Succeed()) From e324f1fe015ae5302ca5200530747be0171424ba Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 25 Jun 2026 11:15:24 +0200 Subject: [PATCH 35/57] move the legacy-volume-creation flag to the seed control plane --- .../deployment-csi-driver-controller.yaml | 6 ++ .../values.yaml | 4 + .../templates/daemonset.yaml | 3 - .../values.yaml | 1 - .../controlplane/csi_compatibility.go | 21 +++-- .../controlplane/csi_compatibility_test.go | 85 ++++++++++++++++++- 6 files changed, 104 insertions(+), 16 deletions(-) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml index df9581f8..5e225220 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml @@ -52,6 +52,12 @@ spec: {{- end }} - --v=3 - --provide-node-service=false + {{- if .Values.csi.enableCompatibilityMode }} + - --legacy-storage-mode=true + {{- end }} + {{- if .Values.csi.blockLegacyCreation }} + - --legacy-volume-creation=false + {{- end }} env: - name: CSI_ENDPOINT value: unix://{{ .Values.socketPath }}/csi.sock diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml index 02f0e30b..bfec1436 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml @@ -4,6 +4,10 @@ kubernetesVersion: 1.30.0 prefix: stackit-blockstorage projectID: "" +csi: + enableCompatibilityMode: false + blockLegacyCreation: false + images: csi-driver-stackit: image-repository:image-tag csi-provisioner: image-repository:image-tag diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index 103209a8..fc95e890 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -51,9 +51,6 @@ spec: {{- if .Values.csi.enableCompatibilityMode }} - --legacy-storage-mode=true {{- end }} - {{- if .Values.csi.blockLegacyCreation }} - - --legacy-volume-creation=false - {{- end }} env: - name: CSI_ENDPOINT value: unix://{{ .Values.socketPath }} diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml index d2ac0bad..89178b01 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/values.yaml @@ -7,7 +7,6 @@ healthzPort: 9908 csi: enableCompatibilityMode: false - blockLegacyCreation: false images: csi-driver-stackit: image-repository:image-tag diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index b3344075..26eb322a 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -44,7 +44,8 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. compatibilityMode := getCSICompatibilityMode(cpConfig) switch compatibilityMode { case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: - chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues) + blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK + chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues, blockLegacyCreation) if err != nil { return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) } @@ -61,7 +62,7 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. return nil } -func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { +func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { chartValues := composeCompatibilityChartValues(values) // Override chart values @@ -81,6 +82,14 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m } chartValues["images"] = imageMap + csiValues := map[string]any{ + "enableCompatibilityMode": true, + } + if blockLegacyCreation { + csiValues["blockLegacyCreation"] = true + } + chartValues["csi"] = csiValues + return ch.renderer.RenderEmbeddedFS( charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), @@ -103,8 +112,7 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context compatibilityMode := getCSICompatibilityMode(cpConfig) switch compatibilityMode { case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: - blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK - chart, err := ch.renderShootCSICompatibilityMode(values, blockLegacyCreation) + chart, err := ch.renderShootCSICompatibilityMode(values) if err != nil { return fmt.Errorf("render shoot CSI compatibility mode: %w", err) } @@ -121,7 +129,7 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context return nil } -func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { +func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { chartValues := composeCompatibilityChartValues(values) // Override chart values @@ -141,9 +149,6 @@ func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values csiValues := map[string]any{ "enableCompatibilityMode": true, } - if blockLegacyCreation { - csiValues["blockLegacyCreation"] = true - } chartValues["csi"] = csiValues return ch.renderer.RenderEmbeddedFS( diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index d1d6f2a7..145e6992 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -176,6 +176,49 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{} err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) + + deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-csi-compat-csi-driver-controller") + var csiContainer *corev1.Container + for i := range deployment.Spec.Template.Spec.Containers { + if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { + csiContainer = &deployment.Spec.Template.Spec.Containers[i] + break + } + } + Expect(csiContainer).NotTo(BeNil(), "stackit-csi-driver container not found") + Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) + Expect(csiContainer.Args).NotTo(ContainElement("--legacy-volume-creation=false")) + }) + }) + + Context("when CSICompatibilityMode is COMPATBLOCK", func() { + It("should deploy the seed csi compatibility mode", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + CompatibilityMode: string(stackitv1alpha1.COMPATBLOCK), + }, + }, + } + + err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + Expect(err).NotTo(HaveOccurred()) + + mr := &resourcesv1alpha1.ManagedResource{} + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + Expect(err).NotTo(HaveOccurred()) + + deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-csi-compat-csi-driver-controller") + var csiContainer *corev1.Container + for i := range deployment.Spec.Template.Spec.Containers { + if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { + csiContainer = &deployment.Spec.Template.Spec.Containers[i] + break + } + } + Expect(csiContainer).NotTo(BeNil(), "stackit-csi-driver container not found") + Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) + Expect(csiContainer.Args).To(ContainElement("--legacy-volume-creation=false")) }) }) }) @@ -318,13 +361,12 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { } Expect(csiContainer).NotTo(BeNil(), "csi-driver-stackit container not found") Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) - Expect(csiContainer.Args).To(ContainElement("--legacy-volume-creation=false")) }) }) }) }) -func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, namespace string, prefix string) *appsv1.DaemonSet { +func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, namespace string, secretNamePrefix string) *appsv1.DaemonSet { GinkgoHelper() secretList := &corev1.SecretList{} Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) @@ -332,14 +374,14 @@ func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, names var names []string for _, s := range secretList.Items { names = append(names, s.Name) - if strings.HasPrefix(s.Name, prefix) { + if strings.HasPrefix(s.Name, secretNamePrefix) { matchedSecret = &s break } } if matchedSecret == nil { - Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", prefix, names)) + Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", secretNamePrefix, names)) return nil // will never be reached, but makes the linter very happy } @@ -356,3 +398,38 @@ func getDaemonSetFromSecret(ctx context.Context, fakeClient client.Client, names Fail("DaemonSet not found in secret " + matchedSecret.Name) return nil } + +func getDeploymentFromSecret(ctx context.Context, fakeClient client.Client, namespace string, secretName string, deploymentName string) *appsv1.Deployment { + GinkgoHelper() + secretList := &corev1.SecretList{} + Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + var matchedSecret *corev1.Secret + var names []string + for _, s := range secretList.Items { + names = append(names, s.Name) + if strings.HasPrefix(s.Name, secretName) { + matchedSecret = &s + break + } + } + + if matchedSecret == nil { + Fail(fmt.Sprintf("Secret starting with prefix %s not found. Found secrets: %v", secretName, names)) + return nil // will never be reached, but makes the linter very happy + } + + for _, data := range matchedSecret.Data { + docs := bytes.SplitSeq(data, []byte("\n---")) + for doc := range docs { + if bytes.Contains(doc, []byte("kind: Deployment")) { + deployment := &appsv1.Deployment{} + Expect(yaml.Unmarshal(doc, deployment)).To(Succeed()) + if deployment.Name == deploymentName { + return deployment + } + } + } + } + Fail(fmt.Sprintf("Deployment %s not found in secret %s", deploymentName, matchedSecret.Name)) + return nil // will never be reached, but makes the linter very happy +} From f6cca4e11cb2ee800f20d46c74f3a3e27edfe93d Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 25 Jun 2026 11:17:25 +0200 Subject: [PATCH 36/57] use a different healthz port in the second csi drive instance, also in the seed control plane --- .../templates/deployment-csi-driver-controller.yaml | 2 +- .../charts/stackit-blockstorage-csi-driver/values.yaml | 2 ++ pkg/controller/controlplane/csi_compatibility.go | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml index 5e225220..5cb77dd7 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml @@ -78,7 +78,7 @@ spec: {{- end }} ports: - name: healthz - containerPort: 9808 + containerPort: {{ .Values.healthzPort }} protocol: TCP livenessProbe: httpGet: diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml index bfec1436..cf5a4243 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/values.yaml @@ -4,6 +4,8 @@ kubernetesVersion: 1.30.0 prefix: stackit-blockstorage projectID: "" +healthzPort: 9808 + csi: enableCompatibilityMode: false blockLegacyCreation: false diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 26eb322a..e0cb492f 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -82,6 +82,7 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m } chartValues["images"] = imageMap + chartValues["healthzPort"] = 9809 csiValues := map[string]any{ "enableCompatibilityMode": true, } From 48bbe2fa8b3730d4038b3965500ca82b2c08d160 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 25 Jun 2026 12:12:42 +0200 Subject: [PATCH 37/57] fix typo --- pkg/controller/controlplane/csi_compatibility.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index e0cb492f..382a3194 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -21,7 +21,7 @@ const ( csiDriverChartName = "stackit-blockstorage-csi-driver" csiCompatibilityPrefix = "stackit-csi-compat" csiCompatSeedChartName = csiCompatibilityPrefix + "-chart" - csiCompatShootChartName = csiCompatibilityPrefix + "-shoort-chart" + csiCompatShootChartName = csiCompatibilityPrefix + "-shoot-chart" ) func NewCompatCSICompatibilityHandler(client client.Client, config *rest.Config) (*CompatCSICompatibilityHandler, error) { From 3d9af0179c51df934280cfa3b6aa2d931d3e1a9e Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 25 Jun 2026 13:01:32 +0200 Subject: [PATCH 38/57] use a better prefix --- pkg/controller/controlplane/csi_compatibility.go | 2 +- pkg/controller/controlplane/csi_compatibility_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 382a3194..57221c2c 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -19,7 +19,7 @@ import ( const ( csiDriverChartName = "stackit-blockstorage-csi-driver" - csiCompatibilityPrefix = "stackit-csi-compat" + csiCompatibilityPrefix = "stackit-compatibility" csiCompatSeedChartName = csiCompatibilityPrefix + "-chart" csiCompatShootChartName = csiCompatibilityPrefix + "-shoot-chart" ) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index 145e6992..4069b26d 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -174,10 +174,10 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-compatibility-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-csi-compat-csi-driver-controller") + deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-driver-controller") var csiContainer *corev1.Container for i := range deployment.Spec.Template.Spec.Containers { if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { @@ -205,10 +205,10 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} - err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-csi-compat-chart", Namespace: namespace}, mr) + err = fakeClient.Get(ctx, types.NamespacedName{Name: "stackit-compatibility-chart", Namespace: namespace}, mr) Expect(err).NotTo(HaveOccurred()) - deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-csi-compat-csi-driver-controller") + deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-driver-controller") var csiContainer *corev1.Container for i := range deployment.Spec.Template.Spec.Containers { if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { From 38c6ad36eb11695a46f2fc9035b8a6edad3629f2 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Thu, 25 Jun 2026 15:39:10 +0200 Subject: [PATCH 39/57] fix: deploy seed part in correct namespace; findImage by specific version Signed-off-by: Niclas Schad --- .../controlplane/csi_compatibility.go | 35 +++++++------------ .../controlplane/csi_compatibility_test.go | 25 +++++++------ pkg/controller/controlplane/valuesprovider.go | 8 ++--- .../controlplane/valuesprovider_test.go | 4 +-- 4 files changed, 32 insertions(+), 40 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 57221c2c..a37a64c7 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -7,6 +7,7 @@ import ( "github.com/gardener/gardener/pkg/chartrenderer" gardenerutils "github.com/gardener/gardener/pkg/utils" + imagevectorutils "github.com/gardener/gardener/pkg/utils/imagevector" "github.com/gardener/gardener/pkg/utils/managedresources" "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" @@ -40,12 +41,12 @@ type CompatCSICompatibilityHandler struct { renderer chartrenderer.Interface } -func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { +func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, version string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) switch compatibilityMode { case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK - chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues, blockLegacyCreation) + chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues, namespace, version, blockLegacyCreation) if err != nil { return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) } @@ -62,21 +63,13 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. return nil } -func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { +func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any, namespace string, version string, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { chartValues := composeCompatibilityChartValues(values) // Override chart values chartValues["prefix"] = csiCompatibilityPrefix - imageMap, err := findImages( - "csi-driver-stackit", - "csi-provisioner", - "csi-attacher", - "csi-snapshotter", - "csi-resizer", - "csi-liveness-probe", - "csi-snapshot-controller", - ) + imageMap, err := findImages(version, "csi-driver-stackit", "csi-provisioner", "csi-attacher", "csi-snapshotter", "csi-resizer", "csi-liveness-probe", "csi-snapshot-controller") if err != nil { return nil, err } @@ -95,7 +88,7 @@ func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values m charts.InternalChart, filepath.Join(charts.InternalChartsPath, "seed-controlplane/charts/stackit-blockstorage-csi-driver"), csiDriverChartName, - "kube-system", + namespace, chartValues, ) } @@ -109,11 +102,11 @@ func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx cont return client.IgnoreNotFound(managedresources.DeleteForSeed(ctx, ch.client, namespace, csiCompatSeedChartName)) } -func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { +func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, version string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) switch compatibilityMode { case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: - chart, err := ch.renderShootCSICompatibilityMode(values) + chart, err := ch.renderShootCSICompatibilityMode(values, version) if err != nil { return fmt.Errorf("render shoot CSI compatibility mode: %w", err) } @@ -130,17 +123,13 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context return nil } -func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any) (*chartrenderer.RenderedChart, error) { +func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, version string) (*chartrenderer.RenderedChart, error) { chartValues := composeCompatibilityChartValues(values) // Override chart values chartValues["prefix"] = csiCompatibilityPrefix - imageMap, err := findImages( - "csi-driver-stackit", - "csi-node-driver-registrar", - "csi-liveness-probe", - ) + imageMap, err := findImages(version, "csi-driver-stackit", "csi-node-driver-registrar", "csi-liveness-probe") if err != nil { return nil, err } @@ -183,11 +172,11 @@ func composeCompatibilityChartValues(values map[string]any) map[string]any { return gardenerutils.MergeMaps(values, csiStackitValues) } -func findImages(imagesToFind ...string) (map[string]any, error) { +func findImages(version string, imagesToFind ...string) (map[string]any, error) { images := imagevector.ImageVector() result := make(map[string]any) for _, image := range imagesToFind { - foundImage, err := images.FindImage(image) + foundImage, err := images.FindImage(image, imagevectorutils.TargetVersion(version)) if err != nil { return nil, err } diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index 4069b26d..b2488a8c 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -59,12 +59,14 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { handler *CompatCSICompatibilityHandler namespace string config *rest.Config + shootVersion string controlPlaneValues map[string]any ) BeforeEach(func() { ctx = context.Background() namespace = "test-namespace" + shootVersion = "1.33.5" fakeClient = fakeclient.NewClientBuilder(). WithScheme(kubernetes.SeedScheme). @@ -90,6 +92,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Describe("#HandleSeedCSICompatibility", func() { Context("when CSICompatibilityMode is DEFAULT", func() { It("should delete the managed resource", func() { + namespace = "shoot--foo--bar" cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ @@ -102,19 +105,19 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{ ObjectMeta: metav1.ObjectMeta{ Name: csiCompatSeedChartName, - Namespace: "kube-system", + Namespace: namespace, }, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "managedresource-" + csiCompatSeedChartName, - Namespace: "kube-system", + Namespace: namespace, }, } Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleSeedCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion @@ -138,7 +141,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{ ObjectMeta: metav1.ObjectMeta{ Name: csiCompatSeedChartName, - Namespace: "kube-system", + Namespace: "shoot--foo--bar", }, } secret := &corev1.Secret{ @@ -150,7 +153,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleSeedCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion @@ -170,7 +173,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }, } - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleSeedCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} @@ -201,7 +204,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }, } - err := handler.HandleSeedCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleSeedCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} @@ -250,7 +253,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleShootCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion @@ -285,7 +288,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(fakeClient.Create(ctx, mr)).To(Succeed()) Expect(fakeClient.Create(ctx, secret)).To(Succeed()) - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleShootCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) // Check deletion @@ -304,7 +307,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }, } - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, controlPlaneValues) + err := handler.HandleShootCSICompatibility(ctx, namespace, shootVersion, cpConfig, controlPlaneValues) Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} @@ -344,7 +347,7 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { }, } - err := handler.HandleShootCSICompatibility(ctx, namespace, cpConfig, values) + err := handler.HandleShootCSICompatibility(ctx, namespace, shootVersion, cpConfig, values) Expect(err).NotTo(HaveOccurred()) mr := &resourcesv1alpha1.ManagedResource{} diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 86a2768f..9f97ee88 100644 --- a/pkg/controller/controlplane/valuesprovider.go +++ b/pkg/controller/controlplane/valuesprovider.go @@ -357,8 +357,8 @@ var ( ) type CSICompatibilityHandler interface { - HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error - HandleShootCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error + HandleSeedCSICompatibility(context.Context, string, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error + HandleShootCSICompatibility(context.Context, string, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error } // NewValuesProvider creates a new ValuesProvider for the generic actuator. @@ -768,7 +768,7 @@ func (vp *valuesProvider) getControlPlaneChartValues(ctx context.Context, cpConf controlPlaneValues[openstack.CSIControllerName] = map[string]any{ "enabled": false, } - err := vp.csiCompatibilityHandler.HandleSeedCSICompatibility(ctx, cp.Namespace, cpConfig, controlPlaneValues) + err := vp.csiCompatibilityHandler.HandleSeedCSICompatibility(ctx, cp.Namespace, cluster.Shoot.Spec.Kubernetes.Version, cpConfig, controlPlaneValues) if err != nil { return nil, err } @@ -1089,7 +1089,7 @@ func (vp *valuesProvider) getControlPlaneShootChartValues(ctx context.Context, c case stackitv1alpha1.STACKIT: values[openstack.CSISTACKITNodeName] = csiDriverSTACKITValues values[openstack.CSINodeName] = map[string]any{"enabled": false} - err := vp.csiCompatibilityHandler.HandleShootCSICompatibility(ctx, cp.Namespace, cpConfig, values) + err := vp.csiCompatibilityHandler.HandleShootCSICompatibility(ctx, cp.Namespace, cluster.Shoot.Spec.Kubernetes.Version, cpConfig, values) if err != nil { return nil, err } diff --git a/pkg/controller/controlplane/valuesprovider_test.go b/pkg/controller/controlplane/valuesprovider_test.go index 5137d2db..12a9f8a6 100644 --- a/pkg/controller/controlplane/valuesprovider_test.go +++ b/pkg/controller/controlplane/valuesprovider_test.go @@ -1145,9 +1145,9 @@ func stackitCCMDeletion(ctx context.Context, c *mockclient.MockClient) { type noopCSICompatibilityHandler struct{} -func (*noopCSICompatibilityHandler) HandleSeedCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { +func (*noopCSICompatibilityHandler) HandleSeedCSICompatibility(context.Context, string, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { return nil } -func (*noopCSICompatibilityHandler) HandleShootCSICompatibility(context.Context, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { +func (*noopCSICompatibilityHandler) HandleShootCSICompatibility(context.Context, string, string, *stackitv1alpha1.ControlPlaneConfig, map[string]any) error { return nil } From 2cbfc21b670aefbb85acea44939c799c58b30251 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Thu, 25 Jun 2026 16:38:29 +0200 Subject: [PATCH 40/57] make stackit-blockstorage-csi-driver (shoot) chart customizable for different driverName Signed-off-by: Niclas Schad --- .../templates/daemonset.yaml | 10 +++++++--- pkg/controller/controlplane/csi_compatibility.go | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index fc95e890..273f2941 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -17,7 +17,11 @@ spec: metadata: annotations: checksum/configmap-cloud-provider-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - node.gardener.cloud/wait-for-csi-node-stackit: block-storage.csi.stackit.cloud + {{- if eq .Values.driverName "cinder.csi.openstack.org" }} + node.gardener.cloud/wait-for-csi-node-openstack: {{ .Values.driverName }} + {{- else }} + node.gardener.cloud/wait-for-csi-node-stackit: {{ .Values.driverName }} + {{- end }} labels: node.gardener.cloud/critical-component: "true" app: {{ .Values.prefix }}-csi @@ -97,7 +101,7 @@ spec: - name: ADDRESS value: {{ .Values.socketPath }} - name: DRIVER_REG_SOCK_PATH - value: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/csi.sock + value: /var/lib/kubelet/plugins/{{ .Values.driverName }}/csi.sock {{- if .Values.resources.nodeDriverRegistrar }} resources: {{ toYaml .Values.resources.nodeDriverRegistrar | indent 10 }} @@ -133,7 +137,7 @@ spec: type: Directory - name: plugin-dir hostPath: - path: /var/lib/kubelet/plugins/block-storage.csi.stackit.cloud/ + path: /var/lib/kubelet/plugins/{{ .Values.driverName }}/ type: DirectoryOrCreate - name: registration-dir hostPath: diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index a37a64c7..db59f27a 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -136,6 +136,7 @@ func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values chartValues["images"] = imageMap chartValues["healthzPort"] = 9909 + chartValues["driverName"] = openstack.CSIStorageProvisioner csiValues := map[string]any{ "enableCompatibilityMode": true, } From e85d5cfafa0684650ff33fa57bae7392ed9b5bf8 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Thu, 25 Jun 2026 16:59:42 +0200 Subject: [PATCH 41/57] make health-port of CSI livenessprobe configurable Signed-off-by: Niclas Schad --- .../templates/deployment-csi-driver-controller.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml index 5cb77dd7..46d8db73 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-driver-controller.yaml @@ -260,6 +260,7 @@ spec: args: - --probe-timeout=3m - --csi-address=/csi/csi.sock + - --health-port={{.Values.healthzPort}} {{- if .Values.resources.livenessProbe }} resources: {{ toYaml .Values.resources.livenessProbe | indent 10 }} From 24f65b78ad81329298535a3251570060130c5b0e Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Thu, 25 Jun 2026 18:36:09 +0200 Subject: [PATCH 42/57] only deploy snapshot-controller once Signed-off-by: Niclas Schad --- .../templates/csi-snapshot-controller-poddisruptionbudget.yaml | 2 ++ .../templates/csi-snapshot-controller-vpa.yaml | 3 ++- .../templates/deployment-csi-snapshot-controller.yaml | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml index 3b1f29ad..9e5e3eef 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml @@ -1,3 +1,4 @@ +{{- if .Values.csi.enableCompatibilityMode }} apiVersion: policy/v1 kind: PodDisruptionBudget metadata: @@ -13,3 +14,4 @@ spec: app: {{.Values.prefix}}-csi-snapshot-controller role: controller unhealthyPodEvictionPolicy: AlwaysAllow +{{- end }} diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml index bbe8278f..d5e49601 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml @@ -1,4 +1,4 @@ ---- +{{- if .Values.csi.enableCompatibilityMode }} apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: @@ -15,3 +15,4 @@ spec: name: {{.Values.prefix}}-csi-snapshot-controller updatePolicy: updateMode: Auto +{{- end }} diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml index 66c4daf0..5dbe585a 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml @@ -1,3 +1,4 @@ +{{- if .Values.csi.enableCompatibilityMode }} apiVersion: apps/v1 kind: Deployment metadata: @@ -70,3 +71,4 @@ spec: path: token name: shoot-access-csi-snapshot-controller optional: false +{{- end }} From 5610a2d5ec34b51c78deef8dbea37473cff95c0d Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Fri, 26 Jun 2026 07:58:45 +0200 Subject: [PATCH 43/57] use the correct namespace --- pkg/controller/controlplane/csi_compatibility_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index b2488a8c..6d57d256 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -92,7 +92,6 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Describe("#HandleSeedCSICompatibility", func() { Context("when CSICompatibilityMode is DEFAULT", func() { It("should delete the managed resource", func() { - namespace = "shoot--foo--bar" cpConfig := &stackitv1alpha1.ControlPlaneConfig{ Storage: &stackitv1alpha1.Storage{ CSI: &stackitv1alpha1.CSI{ @@ -141,13 +140,13 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { mr := &resourcesv1alpha1.ManagedResource{ ObjectMeta: metav1.ObjectMeta{ Name: csiCompatSeedChartName, - Namespace: "shoot--foo--bar", + Namespace: namespace, }, } secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: "managedresource-" + csiCompatSeedChartName, - Namespace: "kube-system", + Namespace: namespace, }, } Expect(fakeClient.Create(ctx, mr)).To(Succeed()) From 05ef57215c09810c72e6d1e184f67e091791c715 Mon Sep 17 00:00:00 2001 From: Niclas Schad Date: Fri, 26 Jun 2026 10:20:39 +0200 Subject: [PATCH 44/57] fix: deploy snapshotcontroller by default; but NOT if in compatibility mode Signed-off-by: Niclas Schad --- .../templates/csi-snapshot-controller-poddisruptionbudget.yaml | 2 +- .../templates/csi-snapshot-controller-vpa.yaml | 2 +- .../templates/deployment-csi-snapshot-controller.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml index 9e5e3eef..e8de9025 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-poddisruptionbudget.yaml @@ -1,4 +1,4 @@ -{{- if .Values.csi.enableCompatibilityMode }} +{{- if not .Values.csi.enableCompatibilityMode }} apiVersion: policy/v1 kind: PodDisruptionBudget metadata: diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml index d5e49601..fd4164ec 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/csi-snapshot-controller-vpa.yaml @@ -1,4 +1,4 @@ -{{- if .Values.csi.enableCompatibilityMode }} +{{- if not .Values.csi.enableCompatibilityMode }} apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: diff --git a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml index 5dbe585a..f3e26cfc 100644 --- a/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml +++ b/charts/internal/seed-controlplane/charts/stackit-blockstorage-csi-driver/templates/deployment-csi-snapshot-controller.yaml @@ -1,4 +1,4 @@ -{{- if .Values.csi.enableCompatibilityMode }} +{{- if not .Values.csi.enableCompatibilityMode }} apiVersion: apps/v1 kind: Deployment metadata: From 08043736c65db5b7882589fe155ac8dfc5278d29 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 29 Jun 2026 11:26:53 +0200 Subject: [PATCH 45/57] check the liveness-probe healthz port --- .../controlplane/csi_compatibility_test.go | 68 ++++++++++--------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index 6d57d256..0e2a2780 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -180,16 +180,14 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-driver-controller") - var csiContainer *corev1.Container - for i := range deployment.Spec.Template.Spec.Containers { - if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { - csiContainer = &deployment.Spec.Template.Spec.Containers[i] - break - } - } - Expect(csiContainer).NotTo(BeNil(), "stackit-csi-driver container not found") + csiContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-driver") + Expect(found).To(BeTrue(), "stackit-csi-driver container not found") Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) Expect(csiContainer.Args).NotTo(ContainElement("--legacy-volume-creation=false")) + + livenessContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-liveness-probe") + Expect(found).To(BeTrue(), "stackit-csi-liveness-probe container not found") + Expect(livenessContainer.Args).To(ContainElement("--health-port=9809")) }) }) @@ -211,16 +209,15 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) deployment := getDeploymentFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-driver-controller") - var csiContainer *corev1.Container - for i := range deployment.Spec.Template.Spec.Containers { - if deployment.Spec.Template.Spec.Containers[i].Name == "stackit-csi-driver" { - csiContainer = &deployment.Spec.Template.Spec.Containers[i] - break - } - } - Expect(csiContainer).NotTo(BeNil(), "stackit-csi-driver container not found") + + csiContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-driver") + Expect(found).To(BeTrue(), "stackit-csi-driver container not found") Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) Expect(csiContainer.Args).To(ContainElement("--legacy-volume-creation=false")) + + livenessContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-liveness-probe") + Expect(found).To(BeTrue(), "stackit-csi-liveness-probe container not found") + Expect(livenessContainer.Args).To(ContainElement("--health-port=9809")) }) }) }) @@ -314,16 +311,15 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatShootChartName) - var csiContainer *corev1.Container - for i := range ds.Spec.Template.Spec.Containers { - if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { - csiContainer = &ds.Spec.Template.Spec.Containers[i] - break - } - } - Expect(csiContainer).NotTo(BeNil(), "csi-driver-stackit container not found") + + csiContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-driver-stackit") + Expect(found).To(BeTrue(), "csi-driver-stackit container not found") Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) Expect(csiContainer.Args).NotTo(ContainElement("--legacy-volume-creation=false")) + + livenessContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-liveness-probe") + Expect(found).To(BeTrue(), "csi-liveness-probe container not found") + Expect(livenessContainer.Args).To(ContainElement("--health-port=9909")) }) }) @@ -354,15 +350,14 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { Expect(err).NotTo(HaveOccurred()) ds := getDaemonSetFromSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatShootChartName) - var csiContainer *corev1.Container - for i := range ds.Spec.Template.Spec.Containers { - if ds.Spec.Template.Spec.Containers[i].Name == "csi-driver-stackit" { - csiContainer = &ds.Spec.Template.Spec.Containers[i] - break - } - } - Expect(csiContainer).NotTo(BeNil(), "csi-driver-stackit container not found") + + csiContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-driver-stackit") + Expect(found).To(BeTrue(), "csi-driver-stackit container not found") Expect(csiContainer.Args).To(ContainElement("--legacy-storage-mode=true")) + + livenessContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-liveness-probe") + Expect(found).To(BeTrue(), "csi-liveness-probe container not found") + Expect(livenessContainer.Args).To(ContainElement("--health-port=9909")) }) }) }) @@ -435,3 +430,12 @@ func getDeploymentFromSecret(ctx context.Context, fakeClient client.Client, name Fail(fmt.Sprintf("Deployment %s not found in secret %s", deploymentName, matchedSecret.Name)) return nil // will never be reached, but makes the linter very happy } + +func findContainerInPod(podSpec *corev1.PodSpec, name string) (*corev1.Container, bool) { + for i := range podSpec.Containers { + if podSpec.Containers[i].Name == name { + return &podSpec.Containers[i], true + } + } + return nil, false +} From f78f7649ccd24b2eb19ec76217fa1a9cd33ff72c Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 29 Jun 2026 12:03:11 +0200 Subject: [PATCH 46/57] check the csi drive name in COMPAT(BLOCK) mode --- pkg/controller/controlplane/csi_compatibility_test.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index 0e2a2780..f32cf08c 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -320,6 +320,9 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { livenessContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-liveness-probe") Expect(found).To(BeTrue(), "csi-liveness-probe container not found") Expect(livenessContainer.Args).To(ContainElement("--health-port=9909")) + + Expect(ds.Spec.Template.Annotations).To(HaveKeyWithValue("node.gardener.cloud/wait-for-csi-node-openstack", openstack.CSIStorageProvisioner)) + Expect(ds.Spec.Template.Annotations).NotTo(HaveKey("node.gardener.cloud/wait-for-csi-node-stackit")) }) }) @@ -358,6 +361,9 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { livenessContainer, found := findContainerInPod(&ds.Spec.Template.Spec, "csi-liveness-probe") Expect(found).To(BeTrue(), "csi-liveness-probe container not found") Expect(livenessContainer.Args).To(ContainElement("--health-port=9909")) + + Expect(ds.Spec.Template.Annotations).To(HaveKeyWithValue("node.gardener.cloud/wait-for-csi-node-openstack", openstack.CSIStorageProvisioner)) + Expect(ds.Spec.Template.Annotations).NotTo(HaveKey("node.gardener.cloud/wait-for-csi-node-stackit")) }) }) }) From 9899a456da1b3eed553b050ca4f01aafb9bbc181 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 29 Jun 2026 12:21:51 +0200 Subject: [PATCH 47/57] check that there is no snapshot controller deployed with the compatibility driver --- .../controlplane/csi_compatibility_test.go | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pkg/controller/controlplane/csi_compatibility_test.go b/pkg/controller/controlplane/csi_compatibility_test.go index f32cf08c..253d6adb 100644 --- a/pkg/controller/controlplane/csi_compatibility_test.go +++ b/pkg/controller/controlplane/csi_compatibility_test.go @@ -188,6 +188,8 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { livenessContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-liveness-probe") Expect(found).To(BeTrue(), "stackit-csi-liveness-probe container not found") Expect(livenessContainer.Args).To(ContainElement("--health-port=9809")) + + Expect(deploymentExistsInSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-snapshot-controller")).To(BeFalse()) }) }) @@ -218,6 +220,8 @@ var _ = Describe("CompatCSICompatibilityHandler", func() { livenessContainer, found := findContainerInPod(&deployment.Spec.Template.Spec, "stackit-csi-liveness-probe") Expect(found).To(BeTrue(), "stackit-csi-liveness-probe container not found") Expect(livenessContainer.Args).To(ContainElement("--health-port=9809")) + + Expect(deploymentExistsInSecret(ctx, fakeClient, namespace, "managedresource-"+csiCompatSeedChartName, "stackit-compatibility-csi-snapshot-controller")).To(BeFalse()) }) }) }) @@ -437,6 +441,29 @@ func getDeploymentFromSecret(ctx context.Context, fakeClient client.Client, name return nil // will never be reached, but makes the linter very happy } +func deploymentExistsInSecret(ctx context.Context, fakeClient client.Client, namespace string, secretNamePrefix string, deploymentName string) bool { + GinkgoHelper() + secretList := &corev1.SecretList{} + Expect(fakeClient.List(ctx, secretList, client.InNamespace(namespace))).To(Succeed()) + for _, s := range secretList.Items { + if !strings.HasPrefix(s.Name, secretNamePrefix) { + continue + } + for _, data := range s.Data { + for doc := range bytes.SplitSeq(data, []byte("\n---")) { + if bytes.Contains(doc, []byte("kind: Deployment")) { + deployment := &appsv1.Deployment{} + Expect(yaml.Unmarshal(doc, deployment)).To(Succeed()) + if deployment.Name == deploymentName { + return true + } + } + } + } + } + return false +} + func findContainerInPod(podSpec *corev1.PodSpec, name string) (*corev1.Container, bool) { for i := range podSpec.Containers { if podSpec.Containers[i].Name == name { From af3c62509198c334598af409aad947e2cfc574c1 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 29 Jun 2026 16:37:48 +0200 Subject: [PATCH 48/57] create an integration test that checks if the compatibility related ManagedResource CRs are deployed # Conflicts: # go.mod # go.sum --- go.mod | 25 + go.sum | 304 +++++---- .../controlplane/controlplane_suite_test.go | 23 + .../controlplane/controlplane_test.go | 276 ++++++++ ...scaling.k8s.io_verticalpodautoscalers.yaml | 630 ++++++++++++++++++ ...tensions.gardener.cloud_controlplanes.yaml | 260 ++++++++ ...urces.gardener.cloud_managedresources.yaml | 254 +++++++ 7 files changed, 1658 insertions(+), 114 deletions(-) create mode 100644 test/integration/controlplane/controlplane_suite_test.go create mode 100644 test/integration/controlplane/controlplane_test.go create mode 100644 test/integration/testdata/upstream-crds/10-crd-autoscaling.k8s.io_verticalpodautoscalers.yaml create mode 100644 test/integration/testdata/upstream-crds/10-crd-extensions.gardener.cloud_controlplanes.yaml create mode 100644 test/integration/testdata/upstream-crds/10-crd-resources.gardener.cloud_managedresources.yaml diff --git a/go.mod b/go.mod index e7cf1a54..390a0bed 100644 --- a/go.mod +++ b/go.mod @@ -54,6 +54,7 @@ require ( github.com/BurntSushi/toml v1.6.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/PaesslerAG/gval v1.2.4 // indirect github.com/PaesslerAG/jsonpath v0.1.2-0.20240726212847-3a740cf7976f // indirect github.com/VictoriaMetrics/VictoriaLogs v1.36.2-0.20251008164716-21c0fb3de84d // indirect @@ -82,7 +83,9 @@ require ( github.com/blang/semver/v4 v4.0.0 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/brunoga/deep v1.3.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/coreos/go-semver v0.3.1 // indirect github.com/cyphar/filepath-securejoin v0.6.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/elastic/crd-ref-docs v0.3.0 // indirect @@ -90,11 +93,13 @@ require ( github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.19.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fluent/fluent-operator/v3 v3.7.0 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/gardener/cert-management v0.23.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/errors v0.22.7 // indirect github.com/go-openapi/jsonpointer v0.22.5 // indirect @@ -112,6 +117,7 @@ require ( github.com/go-openapi/swag/typeutils v0.26.0 // indirect github.com/go-openapi/swag/yamlutils v0.25.5 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/go-test/deep v1.1.1 // indirect github.com/gobuffalo/flect v1.0.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-yaml v1.19.2 // indirect @@ -124,6 +130,8 @@ require ( github.com/google/gnostic-models v0.7.1 // indirect github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect + github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect @@ -132,6 +140,7 @@ require ( github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect github.com/klauspost/compress v1.18.6 // indirect github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect github.com/labstack/echo/v4 v4.15.1 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.14 // indirect @@ -158,6 +167,7 @@ require ( github.com/prometheus/common v0.68.1 // indirect github.com/prometheus/procfs v0.20.1 // indirect github.com/prometheus/sigv4 v0.3.0 // indirect + github.com/robfig/cron v1.2.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.4 // indirect @@ -173,8 +183,19 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/zitadel/oidc/v3 v3.45.4 // indirect github.com/zitadel/schema v1.3.2 // indirect + go.etcd.io/etcd/api/v3 v3.6.5 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.6.5 // indirect + go.etcd.io/etcd/client/v3 v3.6.5 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect go.opentelemetry.io/otel v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect + go.opentelemetry.io/otel/sdk v1.44.0 // indirect go.opentelemetry.io/otel/trace v1.44.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.28.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect @@ -193,9 +214,11 @@ require ( gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect + google.golang.org/grpc v1.81.1 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect helm.sh/helm/v4 v4.1.4 // indirect @@ -206,10 +229,12 @@ require ( k8s.io/component-helpers v0.35.5 // indirect k8s.io/gengo/v2 v2.0.0-20251215205346-5ee0d033ba5b // indirect k8s.io/klog/v2 v2.140.0 // indirect + k8s.io/kms v0.35.5 // indirect k8s.io/kube-aggregator v0.35.5 // indirect k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect k8s.io/metrics v0.35.5 // indirect k8s.io/pod-security-admission v0.35.5 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect sigs.k8s.io/controller-tools v0.20.1 // indirect sigs.k8s.io/gateway-api v1.5.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect diff --git a/go.sum b/go.sum index a0fc47a0..4bc2b3e0 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= github.com/PaesslerAG/gval v1.2.4 h1:rhX7MpjJlcxYwL2eTTYIOBUyEKZ+A96T9vQySWkVUiU= github.com/PaesslerAG/gval v1.2.4/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac= @@ -67,34 +69,34 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= -github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= -github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= -github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= -github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= -github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= -github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= -github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= +github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= +github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= +github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= +github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= +github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= +github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= +github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -112,6 +114,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= +github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA= github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= @@ -126,6 +130,8 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elastic/crd-ref-docs v0.3.0 h1:9bGSUkBR56Z7TuDGQAu3KGbBkagwwZ6RkZmS+qvDuDM= github.com/elastic/crd-ref-docs v0.3.0/go.mod h1:8td3UC8CaO5M+G115O3FRKLmplmX+p0EqLMLGM6uNdk= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= @@ -156,18 +162,18 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gardener/cert-management v0.23.0 h1:kD88XcPn6C4zLc8EYtrHyb+/45Iyaozhb+HEM44MKz0= -github.com/gardener/cert-management v0.23.0/go.mod h1:Mehn8XY+iAkm8XOBbNGHmbMft8fP9ZEJFWzWSonPkfc= -github.com/gardener/etcd-druid/api v0.36.4 h1:o/17ciPrbh/w+igKMUuglW7N9XLjoMh7AvRKTzsBEVs= -github.com/gardener/etcd-druid/api v0.36.4/go.mod h1:RwZzKp8K415AS0zg8VoODjBxYepCAUYyLgXnZc1bmbo= -github.com/gardener/gardener v1.145.1 h1:o+gASDy1iuu68wN5VhgOEjD7Sn+BPJV9hFwPe3Tc6gE= -github.com/gardener/gardener v1.145.1/go.mod h1:cDTZIPHaoj9ywmBTvronK5SpPBc/zI942hsp2//WVNo= -github.com/gardener/gardener-extension-networking-calico v1.59.0 h1:L5w61z7ueiV8y2m4oHLFEQbv3sw7JVqouk3o0UPjdu8= -github.com/gardener/gardener-extension-networking-calico v1.59.0/go.mod h1:CTdu8c+5C8y7yoNR6QAvDBVRO4l8i8r7jvzIKv9ZFRc= -github.com/gardener/gardener-extension-os-coreos v1.30.0 h1:v0eqpNL+VkAH5bN/xQ141ystGTcl/+FF18Quqd2XcNY= -github.com/gardener/gardener-extension-os-coreos v1.30.0/go.mod h1:d5K2QNpBjBQZZo1T6QM2x30GlmRDg1qH3YUZQV6yLh0= -github.com/gardener/gardener/pkg/apis v1.145.1 h1:BYPdNqpFw5v5HGt6/3R7MBLvaLPaqmbsNWlJDjRQImM= -github.com/gardener/gardener/pkg/apis v1.145.1/go.mod h1:LsjZw5/3awWSMDnKg4bgftM1kW9Dkeor/ged5DNHVPI= +github.com/gardener/cert-management v0.22.0 h1:82uZiLzYs+W/Qp/z1XnUSN+nmNDCMFUcdgn5sX5xP7M= +github.com/gardener/cert-management v0.22.0/go.mod h1:hJvxHEKHwdSywZUxDqO7vUZN4D7udfhgtpdfDOoaKNs= +github.com/gardener/etcd-druid/api v0.36.3 h1:mBhDE/TJmcn2BY8Fscd5SfBSeWvtK4ZBPEW+v6XsQIQ= +github.com/gardener/etcd-druid/api v0.36.3/go.mod h1:RwZzKp8K415AS0zg8VoODjBxYepCAUYyLgXnZc1bmbo= +github.com/gardener/gardener v1.143.1 h1:+jCDk/j629Jcmgroltv8Egd8dfgvMwRvDs5lwA6oKbY= +github.com/gardener/gardener v1.143.1/go.mod h1:jwMrEnyQtztkCNsVu70/h/YWMHJXkAZa+iFrZoC1bFo= +github.com/gardener/gardener-extension-networking-calico v1.58.0 h1:jCHWWRPB5temGZYYl1mls1Aw69Xmvj44meJ2gEWE4Rw= +github.com/gardener/gardener-extension-networking-calico v1.58.0/go.mod h1:9nbH2Ftc2Rk/HQ2nYMiiHtMTENLAj0OaadGVts3zCu8= +github.com/gardener/gardener-extension-os-coreos v1.29.0 h1:u7O8EdiOLePi87K0pc2gwyoq+BoTAgYopRp/6q62PrM= +github.com/gardener/gardener-extension-os-coreos v1.29.0/go.mod h1:a3q58HtgudEqVq9RKTzO0EQUcBj6LrwoAmaZx3dqZEY= +github.com/gardener/gardener/pkg/apis v1.143.1 h1:zNhbd4ak2k3+gQU9RV7oyjjgPGfHfYMDK+r/TQTDJeY= +github.com/gardener/gardener/pkg/apis v1.143.1/go.mod h1:zGVoiiKNeCwH7f6+4pQI9lOGVqkJueaONqyfyoiLDpk= github.com/gardener/machine-controller-manager v0.61.3 h1:w0JuHCKLmcK7B8E7mx3TvE3e0hSYwikchsMSiMhocqw= github.com/gardener/machine-controller-manager v0.61.3/go.mod h1:8eE1qLztrWIbOM71mHSQGaC6Q+pl5lvOyN08qP39D7o= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -182,6 +188,7 @@ github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -209,34 +216,36 @@ github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochM github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= -github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= -github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= -github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= +github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= +github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= +github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= +github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= -github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= -github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= +github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= +github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= -github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= -github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= -github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= -github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= +github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= +github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= +github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= +github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 h1:3hZD1fwydvCx/cc1R2uYNQirHqf2s6lqpKV3FcNTURA= -github.com/go-openapi/testify/enable/yaml/v2 v2.5.0/go.mod h1:TvDZKBH7ZbMaF3EqH2AwTvNQCmzyZq8K1agRjf1B+Nk= -github.com/go-openapi/testify/v2 v2.5.0 h1:UOCr63aAsMIDydZbZGqo5Ev01D4eydItRbekDuZMJLw= -github.com/go-openapi/testify/v2 v2.5.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= +github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4= github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -303,15 +312,21 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud/v2 v2.13.0 h1:yEyJG+kABd8x2ttTqLsomihU6Kg2YheJSZhvP/QSx+8= -github.com/gophercloud/gophercloud/v2 v2.13.0/go.mod h1:KZRLVs6gcoy/pEFdkZqFjdYqnS0emMHv66UqdM5lMjU= -github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13 h1:Dnid+JYEmkqPWw/vJHRUzZsjO3mdoyCRo2l5UYCqh8k= -github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13/go.mod h1:zpDKeT3ElgCs1UA+7B8+XlDu3R+jkK/CBC1di5qOeow= +github.com/gophercloud/gophercloud/v2 v2.12.0 h1:Gxmc/Bog1UDKkxTcQW7MSPTDviJXpLeEgVeN5KrxoCo= +github.com/gophercloud/gophercloud/v2 v2.12.0/go.mod h1:H7TTOxbLy8RIaHSNhI2GCrWIzw4Xpw8Xn2mBhCUT5kA= +github.com/gophercloud/utils/v2 v2.0.0-20260424064311-2eeed4ceb3e9 h1:WEPhYFzYmpfWHq+YPaP3+8pYf4wKQuJMkgPiiI4g7CY= +github.com/gophercloud/utils/v2 v2.0.0-20260424064311-2eeed4ceb3e9/go.mod h1:bIEH+wgvnxfegUewFuGi0u/L+ji5uiEuVVQMNKQASEY= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1 h1:qnpSQwGEnkcRpTqNOIR6bJbR0gAorgP9CSALpRcKoAA= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/EyXF4lPgLoUtcSWquBM0Rs= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -326,6 +341,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= @@ -366,8 +383,8 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= -github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -404,16 +421,14 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= -github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= +github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= -github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= -github.com/open-telemetry/opentelemetry-operator/apis v0.153.0 h1:ALN6Bo+OU2M/KOT4n/8egYiLNA7M1dC4bOgs2UqC40Q= -github.com/open-telemetry/opentelemetry-operator/apis v0.153.0/go.mod h1:rK5glhBXD9XrMQYfewsF940NPO3LdXdJU2FJJGdBCZ4= -github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= -github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= +github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= +github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= +github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perses/common v0.30.2 h1:RAiVxUpX76lTCb4X7pfcXSvYdXQmZwKi4oDKAEO//u0= github.com/perses/common v0.30.2/go.mod h1:DFtur1QPah2/ChXbKKhw7djYdwNgz27s5fPKpiK0Xao= github.com/perses/perses v0.53.1 h1:9VY/6p9QWrZwPSV7qiwTMSOsgcB37Lb1AXKT0ORXc6I= @@ -430,17 +445,21 @@ github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.91.0 h github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.91.0/go.mod h1:Gfzi4500QCMnptFIQc8YdDi8YZ4QA0vs22LROWZ3+YU= github.com/prometheus/alertmanager v0.29.0 h1:/ET4NmAGx2Dv9kStrXIBqBgHyiSgIk4OetY+hoZRfgc= github.com/prometheus/alertmanager v0.29.0/go.mod h1:SjI2vhrfdWg10UaRUxTz27rgdJVG3HXrhI5WFjCdBgs= -github.com/prometheus/client_golang v1.23.3-0.20260602051030-3537b20ac86b h1:QNV54DNcRqdeECNdEXiOqTmI75w2rlZtOq5rt8RKhVo= -github.com/prometheus/client_golang v1.23.3-0.20260602051030-3537b20ac86b/go.mod h1:kPaff19KETV3GKIZJehgPmlA2Di3jNeWdgKA9RpObuU= +github.com/prometheus/client_golang v1.23.3-0.20260518105423-c9d5bc4c50a9 h1:rneH9T2i7VvR/2yKvhQ8bkmFEdJ6yuP/2UC8Wx71vIM= +github.com/prometheus/client_golang v1.23.3-0.20260518105423-c9d5bc4c50a9/go.mod h1:QRA+KSZEtitKV4dtGtD9gCh8T2Gy4gifR1dqcT1MMss= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pStaY= -github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= +github.com/prometheus/common v0.67.6-0.20260224092343-e4c38a0aea47 h1:DXz2mpU9UOivlNi73hwwVm7KSYGImaPDdYsniI6uOdE= +github.com/prometheus/common v0.67.6-0.20260224092343-e4c38a0aea47/go.mod h1:6wuJRcHcgBM1w2vIgTuCW7XSzlfCat/lS3C39U02oDA= +github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos= +github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/prometheus/sigv4 v0.3.0 h1:QIG7nTbu0JTnNidGI1Uwl5AGVIChWUACxn2B/BQ1kms= github.com/prometheus/sigv4 v0.3.0/go.mod h1:fKtFYDus2M43CWKMNtGvFNHGXnAJJEGZbiYCmVp/F8I= +github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ= +github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= @@ -452,6 +471,8 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= @@ -499,6 +520,8 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= +github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= @@ -515,6 +538,8 @@ github.com/valyala/quicktemplate v1.8.0 h1:zU0tjbIqTRgKQzFY1L42zq0qR3eh4WoQQdIdq github.com/valyala/quicktemplate v1.8.0/go.mod h1:qIqW8/igXt8fdrUln5kOSb+KWMaJ4Y8QUsfd1k6L2jM= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510 h1:S2dVYn90KE98chqDkyE9Z4N61UnQd+KOfgp5Iu53llk= +github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -523,25 +548,69 @@ github.com/zitadel/oidc/v3 v3.45.4 h1:GKyWaPRVQ8sCu9XgJ3NgNGtG52FzwVJpzXjIUG2+Yr github.com/zitadel/oidc/v3 v3.45.4/go.mod h1:XALmFXS9/kSom9B6uWin1yJ2WTI/E4Ti5aXJdewAVEs= github.com/zitadel/schema v1.3.2 h1:gfJvt7dOMfTmxzhscZ9KkapKo3Nei3B6cAxjav+lyjI= github.com/zitadel/schema v1.3.2/go.mod h1:IZmdfF9Wu62Zu6tJJTH3UsArevs3Y4smfJIj3L8fzxw= +go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= +go.etcd.io/etcd/api/v3 v3.6.5 h1:pMMc42276sgR1j1raO/Qv3QI9Af/AuyQUW6CBAWuntA= +go.etcd.io/etcd/api/v3 v3.6.5/go.mod h1:ob0/oWA/UQQlT1BmaEkWQzI0sJ1M0Et0mMpaABxguOQ= +go.etcd.io/etcd/client/pkg/v3 v3.6.5 h1:Duz9fAzIZFhYWgRjp/FgNq2gO1jId9Yae/rLn3RrBP8= +go.etcd.io/etcd/client/pkg/v3 v3.6.5/go.mod h1:8Wx3eGRPiy0qOFMZT/hfvdos+DjEaPxdIDiCDUv/FQk= +go.etcd.io/etcd/client/v3 v3.6.5 h1:yRwZNFBx/35VKHTcLDeO7XVLbCBFbPi+XV4OC3QJf2U= +go.etcd.io/etcd/client/v3 v3.6.5/go.mod h1:ZqwG/7TAFZ0BJ0jXRPoJjKQJtbFo/9NIY8uoFFKcCyo= +go.etcd.io/etcd/pkg/v3 v3.6.5 h1:byxWB4AqIKI4SBmquZUG1WGtvMfMaorXFoCcFbVeoxM= +go.etcd.io/etcd/pkg/v3 v3.6.5/go.mod h1:uqrXrzmMIJDEy5j00bCqhVLzR5jEJIwDp5wTlLwPGOU= +go.etcd.io/etcd/server/v3 v3.6.5 h1:4RbUb1Bd4y1WkBHmuF+cZII83JNQMuNXzyjwigQ06y0= +go.etcd.io/etcd/server/v3 v3.6.5/go.mod h1:PLuhyVXz8WWRhzXDsl3A3zv/+aK9e4A9lpQkqawIaH0= +go.etcd.io/raft/v3 v3.6.0 h1:5NtvbDVYpnfZWcIHgGRk9DyzkBIXOi8j+DDp1IcnUWQ= +go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= +go.opentelemetry.io/contrib/otelconf v0.23.0 h1:s3C7KdMYiutf4rC8hKFA0WOIDG+gIru8ajjQKS59ir8= +go.opentelemetry.io/contrib/otelconf v0.23.0/go.mod h1:0kN2tcccZS82e7IZlo045gkcL8/8dup1k25sf9ypGxM= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= +go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE= +go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= +go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= +go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= +go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= +go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= +go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -567,15 +636,15 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20260527015227-08cc5374adb3 h1:VHEvKbpgPXcPXn40t9cDTGK3JZwMikIEyF/CTrFfu7k= -golang.org/x/exp v0.0.0-20260527015227-08cc5374adb3/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -612,8 +681,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= -golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= +golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -628,8 +697,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= -golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -646,18 +715,19 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc= -golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= -golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= -golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= +golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= +golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= +golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -688,8 +758,8 @@ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= -golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -700,6 +770,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -720,18 +792,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= -google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d h1:/aDRtSZJjyLQzm75d+a1wOJaqyKBMvIAfeQmoa3ORiI= +google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:etfGUgejTiadZAUaEP14NP97xi1RGeawqkjDARA/UOs= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= -google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -754,6 +826,8 @@ gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWM gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -771,8 +845,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -istio.io/api v1.29.4 h1:6cVx5lLSjMwLehIjzlAhLeNgBiWaZnOht7iUen4MhLY= -istio.io/api v1.29.4/go.mod h1:+brQWcBHoROuyA6fv8rbgg8Kfn0RCGuqoY0duCMuSLA= +istio.io/api v1.29.3 h1:olbjGA/wLDB62U3SsBn+lFHHnugnDTLoCRE0pBWXZ8M= +istio.io/api v1.29.3/go.mod h1:+brQWcBHoROuyA6fv8rbgg8Kfn0RCGuqoY0duCMuSLA= istio.io/client-go v1.29.2 h1:aljV/qXcEGgRYisPTJ7L0Nx8GXWbM8rZgr1eEAC3bCw= istio.io/client-go v1.29.2/go.mod h1:RazwM5KR3FzhWXrvjCq1o85ReFNrHcG3cF2lcs5anzg= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= @@ -807,6 +881,8 @@ k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc= k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0= +k8s.io/kms v0.35.5 h1:KTbr4tWIl7+OeYcY30Vwzv0G0Pz8EeSWcAnx5P7gBPY= +k8s.io/kms v0.35.5/go.mod h1:c/uQe/eKrWdBkvizLFW+ThLA6tTzR0RkkwJJyzDRT1g= k8s.io/kube-aggregator v0.35.5 h1:oLflHAqh8tEoEcXtrzGhr4hctwhcRr5B1sM+T96N1rs= k8s.io/kube-aggregator v0.35.5/go.mod h1:L3GflyN8a8CDjej2UxgeGwRiXYuI+aTZ0GE7qssdN2w= k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= diff --git a/test/integration/controlplane/controlplane_suite_test.go b/test/integration/controlplane/controlplane_suite_test.go new file mode 100644 index 00000000..bae167c5 --- /dev/null +++ b/test/integration/controlplane/controlplane_suite_test.go @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package controlplane + +import ( + "flag" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func init() { + // Accepted for compatibility with the shared make test-integration invocation. + flag.String("region", "eu01", "Region") +} + +func TestControlPlane(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "ControlPlane Controller Suite") +} diff --git a/test/integration/controlplane/controlplane_test.go b/test/integration/controlplane/controlplane_test.go new file mode 100644 index 00000000..298dcac7 --- /dev/null +++ b/test/integration/controlplane/controlplane_test.go @@ -0,0 +1,276 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Gardener contributors +// +// SPDX-License-Identifier: Apache-2.0 + +package controlplane + +import ( + "bytes" + "context" + "encoding/json" + "path/filepath" + "time" + + gardenerv1beta1 "github.com/gardener/gardener/pkg/apis/core/v1beta1" + extensionsv1alpha1 "github.com/gardener/gardener/pkg/apis/extensions/v1alpha1" + resourcesv1alpha1 "github.com/gardener/gardener/pkg/apis/resources/v1alpha1" + "github.com/gardener/gardener/pkg/client/kubernetes" + "github.com/gardener/gardener/pkg/logger" + gardenerenvtest "github.com/gardener/gardener/test/envtest" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + runtimejson "k8s.io/apimachinery/pkg/runtime/serializer/json" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/uuid" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + + stackitv1alpha1 "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/apis/stackit/v1alpha1" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/controller/controlplane" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/openstack" + "github.com/stackitcloud/gardener-extension-provider-stackit/v2/pkg/stackit" +) + +var ( + ctx = context.Background() + + testEnv *gardenerenvtest.GardenerTestEnvironment + mgrCancel context.CancelFunc + k8sclient client.Client + encoder runtime.Encoder + + testID = string(uuid.NewUUID()) +) + +var _ = BeforeSuite(func() { + logf.SetLogger(logger.MustNewZapLogger(logger.DebugLevel, logger.FormatJSON, zap.WriteTo(GinkgoWriter))) + + repoRoot := filepath.Join("..", "..", "..") + + DeferCleanup(func() { + By("stopping manager") + mgrCancel() + + By("stopping test environment") + Expect(testEnv.Stop()).To(Succeed()) + }) + + By("starting test environment") + testEnv = &gardenerenvtest.GardenerTestEnvironment{ + Environment: &envtest.Environment{ + CRDInstallOptions: envtest.CRDInstallOptions{ + Paths: []string{ + filepath.Join(repoRoot, "test", "integration", "testdata", "upstream-crds", "10-crd-extensions.gardener.cloud_clusters.yaml"), + filepath.Join(repoRoot, "test", "integration", "testdata", "upstream-crds", "10-crd-extensions.gardener.cloud_controlplanes.yaml"), + filepath.Join(repoRoot, "test", "integration", "testdata", "upstream-crds", "10-crd-resources.gardener.cloud_managedresources.yaml"), + filepath.Join(repoRoot, "test", "integration", "testdata", "upstream-crds", "10-crd-autoscaling.k8s.io_verticalpodautoscalers.yaml"), + }, + }, + }, + GardenerAPIServer: &gardenerenvtest.GardenerAPIServer{}, + } + + restConfig, err := testEnv.Start() + Expect(err).ToNot(HaveOccurred()) + Expect(restConfig).ToNot(BeNil()) + + scheme := runtime.NewScheme() + Expect(kubernetes.AddSeedSchemeToScheme(scheme)).To(Succeed()) + Expect(stackitv1alpha1.AddToScheme(scheme)).To(Succeed()) + Expect(gardenerv1beta1.AddToScheme(scheme)).To(Succeed()) + + By("setup manager") + mgr, err := manager.New(restConfig, manager.Options{ + Scheme: scheme, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + Cache: cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &extensionsv1alpha1.ControlPlane{}: { + Label: labels.SelectorFromSet(labels.Set{"test-id": testID}), + }, + }, + }, + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(controlplane.AddToManagerWithOptions(ctx, mgr, controlplane.AddOptions{ + Controller: controller.Options{ + MaxConcurrentReconciles: 1, + }, + })).To(Succeed()) + + var mgrCtx context.Context + mgrCtx, mgrCancel = context.WithCancel(ctx) + + By("start manager") + go func() { + defer GinkgoRecover() + Expect(mgr.Start(mgrCtx)).To(Succeed()) + }() + + k8sclient = mgr.GetClient() + + gv := schema.GroupVersions{ + stackitv1alpha1.SchemeGroupVersion, + gardenerv1beta1.SchemeGroupVersion, + } + encoder = serializer.NewCodecFactory(scheme).EncoderForVersion(&runtimejson.Serializer{}, gv) +}) + +var _ = Describe("ControlPlane CSI compatibility mode", func() { + var namespace string + + BeforeEach(func() { + namespace = "shoot--test--" + testID[:8] + + By("create namespace") + ns := &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: namespace}} + Expect(k8sclient.Create(ctx, ns)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, ns))).To(Succeed()) }) + + By("create cluster") + shoot := &gardenerv1beta1.Shoot{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "core.gardener.cloud/v1beta1", + Kind: "Shoot", + }, + ObjectMeta: metav1.ObjectMeta{Name: "test"}, + Spec: gardenerv1beta1.ShootSpec{ + Kubernetes: gardenerv1beta1.Kubernetes{Version: "1.33.5"}, + }, + Status: gardenerv1beta1.ShootStatus{TechnicalID: namespace}, + } + shootBytes := new(bytes.Buffer) + Expect(encoder.Encode(shoot, shootBytes)).To(Succeed()) + + cluster := &extensionsv1alpha1.Cluster{ + ObjectMeta: metav1.ObjectMeta{Name: namespace}, + Spec: extensionsv1alpha1.ClusterSpec{ + CloudProfile: runtime.RawExtension{Raw: []byte(`{}`)}, + Seed: runtime.RawExtension{Raw: []byte(`{}`)}, + Shoot: runtime.RawExtension{Raw: shootBytes.Bytes()}, + }, + } + Expect(k8sclient.Create(ctx, cluster)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, cluster))).To(Succeed()) }) + + By("create cloudprovider secret") + cloudproviderSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: "cloudprovider", Namespace: namespace}, + Data: map[string][]byte{ + stackit.ProjectID: []byte("test-project-id"), + stackit.SaKeyJSON: []byte(`{"key":"value"}`), + }, + } + Expect(k8sclient.Create(ctx, cloudproviderSecret)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, cloudproviderSecret))).To(Succeed()) }) + + By("create cloud-provider-config secret") + cpConfigSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: openstack.CloudProviderConfigName, Namespace: namespace}, + Data: map[string][]byte{"config": []byte("placeholder")}, + } + Expect(k8sclient.Create(ctx, cpConfigSecret)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, cpConfigSecret))).To(Succeed()) }) + + By("create cloud-provider-disk-config-csi secret") + diskConfigSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{Name: openstack.CloudProviderCSIDiskConfigName, Namespace: namespace}, + Data: map[string][]byte{"config": []byte("placeholder")}, + } + Expect(k8sclient.Create(ctx, diskConfigSecret)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, diskConfigSecret))).To(Succeed()) }) + }) + + It("should create both ManagedResources when compatibilityMode=compat", func() { + cpConfig := &stackitv1alpha1.ControlPlaneConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: stackitv1alpha1.SchemeGroupVersion.String(), + Kind: "ControlPlaneConfig", + }, + CloudControllerManager: &stackitv1alpha1.CloudControllerManagerConfig{ + Name: string(stackitv1alpha1.STACKIT), + }, + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.STACKIT), + CompatibilityMode: string(stackitv1alpha1.COMPAT), + }, + }, + } + cpConfigBytes, err := json.Marshal(cpConfig) + Expect(err).NotTo(HaveOccurred()) + + infraStatus := &stackitv1alpha1.InfrastructureStatus{ + TypeMeta: metav1.TypeMeta{ + APIVersion: stackitv1alpha1.SchemeGroupVersion.String(), + Kind: "InfrastructureStatus", + }, + Networks: stackitv1alpha1.NetworkStatus{ + ID: "test-network-id", + Name: "test-network-name", + Router: stackitv1alpha1.RouterStatus{ + ID: "test-router-id", + }, + }, + Node: stackitv1alpha1.NodeStatus{KeyName: "test-key"}, + } + infraStatusBytes, err := json.Marshal(infraStatus) + Expect(err).NotTo(HaveOccurred()) + + By("create ControlPlane CR") + cp := &extensionsv1alpha1.ControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "controlplane", + Namespace: namespace, + Labels: map[string]string{"test-id": testID}, + }, + Spec: extensionsv1alpha1.ControlPlaneSpec{ + DefaultSpec: extensionsv1alpha1.DefaultSpec{ + Type: stackit.Type, + ProviderConfig: &runtime.RawExtension{Raw: cpConfigBytes}, + }, + Region: "eu01", + SecretRef: corev1.SecretReference{ + Name: "cloudprovider", + Namespace: namespace, + }, + InfrastructureProviderStatus: &runtime.RawExtension{Raw: infraStatusBytes}, + }, + } + Expect(k8sclient.Create(ctx, cp)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, cp))).To(Succeed()) }) + + By("wait for seed ManagedResource") + seedMR := &resourcesv1alpha1.ManagedResource{} + Eventually(func() error { + return k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-chart", + Namespace: namespace, + }, seedMR) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Succeed()) + + By("wait for shoot ManagedResource") + shootMR := &resourcesv1alpha1.ManagedResource{} + Eventually(func() error { + return k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-shoot-chart", + Namespace: namespace, + }, shootMR) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Succeed()) + }) +}) diff --git a/test/integration/testdata/upstream-crds/10-crd-autoscaling.k8s.io_verticalpodautoscalers.yaml b/test/integration/testdata/upstream-crds/10-crd-autoscaling.k8s.io_verticalpodautoscalers.yaml new file mode 100644 index 00000000..bf6f0b3d --- /dev/null +++ b/test/integration/testdata/upstream-crds/10-crd-autoscaling.k8s.io_verticalpodautoscalers.yaml @@ -0,0 +1,630 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + api-approved.kubernetes.io: https://github.com/kubernetes/kubernetes/pull/63797 + controller-gen.kubebuilder.io/version: v0.20.1 + name: verticalpodautoscalers.autoscaling.k8s.io +spec: + group: autoscaling.k8s.io + names: + kind: VerticalPodAutoscaler + listKind: VerticalPodAutoscalerList + plural: verticalpodautoscalers + shortNames: + - vpa + singular: verticalpodautoscaler + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .spec.updatePolicy.updateMode + name: Mode + type: string + - jsonPath: .status.recommendation.containerRecommendations[0].target.cpu + name: CPU + type: string + - jsonPath: .status.recommendation.containerRecommendations[0].target.memory + name: Mem + type: string + - jsonPath: .status.conditions[?(@.type=='RecommendationProvided')].status + name: Provided + type: string + - jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1 + schema: + openAPIV3Schema: + description: |- + VerticalPodAutoscaler is the configuration for a vertical pod + autoscaler, which automatically manages pod resources based on historical and + real time resource utilization. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the behavior of the autoscaler. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. + properties: + recommenders: + description: |- + Recommender responsible for generating recommendation for this object. + List should be empty (then the default recommender will generate the + recommendation) or contain exactly one recommender. + items: + description: |- + VerticalPodAutoscalerRecommenderSelector points to a specific Vertical Pod Autoscaler recommender. + In the future it might pass parameters to the recommender. + properties: + name: + description: Name of the recommender responsible for generating + recommendation for this object. + type: string + required: + - name + type: object + type: array + resourcePolicy: + description: |- + Controls how the autoscaler computes recommended resources. + The resource policy may be used to set constraints on the recommendations + for individual containers. + If any individual containers need to be excluded from getting the VPA recommendations, then + it must be disabled explicitly by setting mode to "Off" under containerPolicies. + If not specified, the autoscaler computes recommended resources for all containers in the pod, + without additional constraints. + properties: + containerPolicies: + description: Per-container resource policies. + items: + description: |- + ContainerResourcePolicy controls how autoscaler computes the recommended + resources for a specific container. + properties: + containerName: + description: |- + Name of the container or DefaultContainerResourcePolicy, in which + case the policy is used by the containers that don't have their own + policy specified. + type: string + controlledResources: + description: |- + Specifies the type of recommendations that will be computed + (and possibly applied) by VPA. + If not specified, the default of [ResourceCPU, ResourceMemory] will be used. + items: + description: ResourceName is the name identifying various + resources in a ResourceList. + type: string + type: array + controlledValues: + description: |- + Specifies which resource values should be controlled. + The default is "RequestsAndLimits". + enum: + - RequestsAndLimits + - RequestsOnly + type: string + maxAllowed: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Specifies the maximum amount of resources that will be recommended + for the container. The default is no maximum. + type: object + minAllowed: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Specifies the minimal amount of resources that will be recommended + for the container. The default is no minimum. + type: object + mode: + description: Whether autoscaler is enabled for the container. + The default is "Auto". + enum: + - Auto + - "Off" + type: string + oomBumpUpRatio: + anyOf: + - type: integer + - type: string + description: oomBumpUpRatio is the ratio to increase memory + when OOM is detected. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + oomMinBumpUp: + anyOf: + - type: integer + - type: string + description: oomMinBumpUp is the minimum increase in memory + when OOM is detected. + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + type: array + type: object + targetRef: + description: |- + TargetRef points to the controller managing the set of pods for the + autoscaler to control - e.g. Deployment, StatefulSet. VerticalPodAutoscaler + can be targeted at controller implementing scale subresource (the pod set is + retrieved from the controller's ScaleStatus) or some well known controllers + (e.g. for DaemonSet the pod set is read from the controller's spec). + If VerticalPodAutoscaler cannot use specified target it will report + ConfigUnsupported condition. + Note that VerticalPodAutoscaler does not require full implementation + of scale subresource - it will not use it to modify the replica count. + The only thing retrieved is a label selector matching pods grouped by + the target resource. + properties: + apiVersion: + description: apiVersion is the API version of the referent + type: string + kind: + description: 'kind is the kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'name is the name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + updatePolicy: + description: |- + Describes the rules on how changes are applied to the pods. + If not specified, all fields in the `PodUpdatePolicy` are set to their + default values. + properties: + evictionRequirements: + description: |- + EvictionRequirements is a list of EvictionRequirements that need to + evaluate to true in order for a Pod to be evicted. If more than one + EvictionRequirement is specified, all of them need to be fulfilled to allow eviction. + items: + description: |- + EvictionRequirement defines a single condition which needs to be true in + order to evict a Pod + properties: + changeRequirement: + description: EvictionChangeRequirement refers to the relationship + between the new target recommendation for a Pod and its + current requests, what kind of change is necessary for + the Pod to be evicted + enum: + - TargetHigherThanRequests + - TargetLowerThanRequests + type: string + resources: + description: |- + Resources is a list of one or more resources that the condition applies + to. If more than one resource is given, the EvictionRequirement is fulfilled + if at least one resource meets `changeRequirement`. + items: + description: ResourceName is the name identifying various + resources in a ResourceList. + type: string + type: array + required: + - changeRequirement + - resources + type: object + type: array + minReplicas: + description: |- + Minimal number of replicas which need to be alive for Updater to attempt + pod eviction (pending other checks like PDB). Only positive values are + allowed. Overrides global '--min-replicas' flag. + format: int32 + type: integer + updateMode: + description: |- + Controls when autoscaler applies changes to the pod resources. + The default is 'Recreate'. + enum: + - "Off" + - Initial + - Recreate + - InPlaceOrRecreate + - Auto + type: string + type: object + required: + - targetRef + type: object + status: + description: Current information about the autoscaler. + properties: + conditions: + description: |- + Conditions is the set of conditions required for this autoscaler to scale its target, + and indicates whether or not those conditions are met. + items: + description: |- + VerticalPodAutoscalerCondition describes the state of + a VerticalPodAutoscaler at a certain point. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from + one status to another + format: date-time + type: string + message: + description: |- + message is a human-readable explanation containing details about + the transition + type: string + reason: + description: reason is the reason for the condition's last transition. + type: string + status: + description: status is the status of the condition (True, False, + Unknown) + type: string + type: + description: type describes the current condition + type: string + required: + - status + - type + type: object + type: array + recommendation: + description: |- + The most recently computed amount of resources recommended by the + autoscaler for the controlled pods. + properties: + containerRecommendations: + description: Resources recommended by the autoscaler for each + container. + items: + description: |- + RecommendedContainerResources is the recommendation of resources computed by + autoscaler for a specific container. Respects the container resource policy + if present in the spec. In particular the recommendation is not produced for + containers with `ContainerScalingMode` set to 'Off'. + properties: + containerName: + description: Name of the container. + type: string + lowerBound: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Minimum recommended amount of resources. Observes ContainerResourcePolicy. + This amount is not guaranteed to be sufficient for the application to operate in a stable way, however + running with less resources is likely to have significant impact on performance/availability. + type: object + target: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Recommended amount of resources. Observes ContainerResourcePolicy. + type: object + uncappedTarget: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + The most recent recommended resources target computed by the autoscaler + for the controlled pods, based only on actual resource usage, not taking + into account the ContainerResourcePolicy. + May differ from the Recommendation if the actual resource usage causes + the target to violate the ContainerResourcePolicy (lower than MinAllowed + or higher that MaxAllowed). + Used only as status indication, will not affect actual resource assignment. + type: object + upperBound: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Maximum recommended amount of resources. Observes ContainerResourcePolicy. + Any resources allocated beyond this value are likely wasted. This value may be larger than the maximum + amount of application is actually capable of consuming. + type: object + required: + - target + type: object + type: array + type: object + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} + - deprecated: true + deprecationWarning: autoscaling.k8s.io/v1beta2 API is deprecated + name: v1beta2 + schema: + openAPIV3Schema: + description: |- + VerticalPodAutoscaler is the configuration for a vertical pod + autoscaler, which automatically manages pod resources based on historical and + real time resource utilization. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the behavior of the autoscaler. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status. + properties: + resourcePolicy: + description: |- + Controls how the autoscaler computes recommended resources. + The resource policy may be used to set constraints on the recommendations + for individual containers. If not specified, the autoscaler computes recommended + resources for all containers in the pod, without additional constraints. + properties: + containerPolicies: + description: Per-container resource policies. + items: + description: |- + ContainerResourcePolicy controls how autoscaler computes the recommended + resources for a specific container. + properties: + containerName: + description: |- + Name of the container or DefaultContainerResourcePolicy, in which + case the policy is used by the containers that don't have their own + policy specified. + type: string + maxAllowed: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Specifies the maximum amount of resources that will be recommended + for the container. The default is no maximum. + type: object + minAllowed: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Specifies the minimal amount of resources that will be recommended + for the container. The default is no minimum. + type: object + mode: + description: Whether autoscaler is enabled for the container. + The default is "Auto". + enum: + - Auto + - "Off" + type: string + type: object + type: array + type: object + targetRef: + description: |- + TargetRef points to the controller managing the set of pods for the + autoscaler to control - e.g. Deployment, StatefulSet. VerticalPodAutoscaler + can be targeted at controller implementing scale subresource (the pod set is + retrieved from the controller's ScaleStatus) or some well known controllers + (e.g. for DaemonSet the pod set is read from the controller's spec). + If VerticalPodAutoscaler cannot use specified target it will report + ConfigUnsupported condition. + Note that VerticalPodAutoscaler does not require full implementation + of scale subresource - it will not use it to modify the replica count. + The only thing retrieved is a label selector matching pods grouped by + the target resource. + properties: + apiVersion: + description: apiVersion is the API version of the referent + type: string + kind: + description: 'kind is the kind of the referent; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'name is the name of the referent; More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + updatePolicy: + description: |- + Describes the rules on how changes are applied to the pods. + If not specified, all fields in the `PodUpdatePolicy` are set to their + default values. + properties: + updateMode: + description: |- + Controls when autoscaler applies changes to the pod resources. + The default is 'Auto'. + enum: + - "Off" + - Initial + - Recreate + - Auto + type: string + type: object + required: + - targetRef + type: object + status: + description: Current information about the autoscaler. + properties: + conditions: + description: |- + Conditions is the set of conditions required for this autoscaler to scale its target, + and indicates whether or not those conditions are met. + items: + description: |- + VerticalPodAutoscalerCondition describes the state of + a VerticalPodAutoscaler at a certain point. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from + one status to another + format: date-time + type: string + message: + description: |- + message is a human-readable explanation containing details about + the transition + type: string + reason: + description: reason is the reason for the condition's last transition. + type: string + status: + description: status is the status of the condition (True, False, + Unknown) + type: string + type: + description: type describes the current condition + type: string + required: + - status + - type + type: object + type: array + recommendation: + description: |- + The most recently computed amount of resources recommended by the + autoscaler for the controlled pods. + properties: + containerRecommendations: + description: Resources recommended by the autoscaler for each + container. + items: + description: |- + RecommendedContainerResources is the recommendation of resources computed by + autoscaler for a specific container. Respects the container resource policy + if present in the spec. In particular the recommendation is not produced for + containers with `ContainerScalingMode` set to 'Off'. + properties: + containerName: + description: Name of the container. + type: string + lowerBound: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Minimum recommended amount of resources. Observes ContainerResourcePolicy. + This amount is not guaranteed to be sufficient for the application to operate in a stable way, however + running with less resources is likely to have significant impact on performance/availability. + type: object + target: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: Recommended amount of resources. Observes ContainerResourcePolicy. + type: object + uncappedTarget: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + The most recent recommended resources target computed by the autoscaler + for the controlled pods, based only on actual resource usage, not taking + into account the ContainerResourcePolicy. + May differ from the Recommendation if the actual resource usage causes + the target to violate the ContainerResourcePolicy (lower than MinAllowed + or higher that MaxAllowed). + Used only as status indication, will not affect actual resource assignment. + type: object + upperBound: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Maximum recommended amount of resources. Observes ContainerResourcePolicy. + Any resources allocated beyond this value are likely wasted. This value may be larger than the maximum + amount of application is actually capable of consuming. + type: object + required: + - target + type: object + type: array + type: object + type: object + required: + - spec + type: object + served: false + storage: false + subresources: + status: {} diff --git a/test/integration/testdata/upstream-crds/10-crd-extensions.gardener.cloud_controlplanes.yaml b/test/integration/testdata/upstream-crds/10-crd-extensions.gardener.cloud_controlplanes.yaml new file mode 100644 index 00000000..bbb7d68b --- /dev/null +++ b/test/integration/testdata/upstream-crds/10-crd-extensions.gardener.cloud_controlplanes.yaml @@ -0,0 +1,260 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: controlplanes.extensions.gardener.cloud +spec: + group: extensions.gardener.cloud + names: + kind: ControlPlane + listKind: ControlPlaneList + plural: controlplanes + shortNames: + - cp + singular: controlplane + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The control plane type. + jsonPath: .spec.type + name: Type + type: string + - description: Status of control plane resource. + jsonPath: .status.lastOperation.state + name: Status + type: string + - description: creation timestamp + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ControlPlane is a specification for a ControlPlane resource. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: |- + Specification of the ControlPlane. + If the object's deletion timestamp is set, this field is immutable. + properties: + class: + description: Class holds the extension class used to control the responsibility + for multiple provider extensions. + type: string + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + infrastructureProviderStatus: + description: |- + InfrastructureProviderStatus contains the provider status that has + been generated by the controller responsible for the `Infrastructure` resource. + type: object + x-kubernetes-preserve-unknown-fields: true + providerConfig: + description: ProviderConfig is the provider specific configuration. + type: object + x-kubernetes-preserve-unknown-fields: true + region: + description: Region is the region of this control plane. This field + is immutable. + type: string + secretRef: + description: SecretRef is a reference to a secret that contains the + cloud provider specific credentials. + properties: + name: + description: name is unique within a namespace to reference a + secret resource. + type: string + namespace: + description: namespace defines the space within which the secret + name must be unique. + type: string + type: object + x-kubernetes-map-type: atomic + type: + description: Type contains the instance of the resource's kind. + type: string + required: + - region + - secretRef + - type + type: object + status: + description: ControlPlaneStatus is the status of a ControlPlane resource. + properties: + conditions: + description: Conditions represents the latest available observations + of a Seed's current state. + items: + description: Condition holds the information about the state of + a resource. + properties: + codes: + description: Well-defined error codes in case the condition + reports a problem. + items: + description: ErrorCode is a string alias. + type: string + type: array + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: Last time the condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - lastUpdateTime + - message + - reason + - status + - type + type: object + type: array + lastError: + description: LastError holds information about the last occurred error + during an operation. + properties: + codes: + description: Well-defined error codes of the last error(s). + items: + description: ErrorCode is a string alias. + type: string + type: array + description: + description: A human readable message indicating details about + the last error. + type: string + lastUpdateTime: + description: Last time the error was reported + format: date-time + type: string + taskID: + description: ID of the task which caused this last error + type: string + required: + - description + type: object + lastOperation: + description: LastOperation holds information about the last operation + on the resource. + properties: + description: + description: A human readable message indicating details about + the last operation. + type: string + lastUpdateTime: + description: Last time the operation state transitioned from one + to another. + format: date-time + type: string + progress: + description: The progress in percentage (0-100) of the last operation. + format: int32 + type: integer + state: + description: Status of the last operation, one of Aborted, Processing, + Succeeded, Error, Failed. + type: string + type: + description: Type of the last operation, one of Create, Reconcile, + Delete, Migrate, Restore. + type: string + required: + - description + - lastUpdateTime + - progress + - state + - type + type: object + observedGeneration: + description: ObservedGeneration is the most recent generation observed + for this resource. + format: int64 + type: integer + providerStatus: + description: ProviderStatus contains provider-specific status. + type: object + x-kubernetes-preserve-unknown-fields: true + resources: + description: Resources holds a list of named resource references that + can be referred to in the state by their names. + items: + description: NamedResourceReference is a named reference to a resource. + properties: + name: + description: Name of the resource reference. + type: string + resourceRef: + description: ResourceRef is a reference to a resource. + properties: + apiVersion: + description: apiVersion is the API version of the referent + type: string + kind: + description: 'kind is the kind of the referent; More info: + https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + name: + description: 'name is the name of the referent; More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + required: + - name + - resourceRef + type: object + type: array + state: + description: State can be filled by the operating controller with + what ever data it needs. + type: object + x-kubernetes-preserve-unknown-fields: true + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/test/integration/testdata/upstream-crds/10-crd-resources.gardener.cloud_managedresources.yaml b/test/integration/testdata/upstream-crds/10-crd-resources.gardener.cloud_managedresources.yaml new file mode 100644 index 00000000..dfdb95b7 --- /dev/null +++ b/test/integration/testdata/upstream-crds/10-crd-resources.gardener.cloud_managedresources.yaml @@ -0,0 +1,254 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: managedresources.resources.gardener.cloud +spec: + group: resources.gardener.cloud + names: + kind: ManagedResource + listKind: ManagedResourceList + plural: managedresources + shortNames: + - mr + singular: managedresource + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: The class identifies which resource manager is responsible for + this ManagedResource. + jsonPath: .spec.class + name: Class + type: string + - description: ' Indicates whether all resources have been applied.' + jsonPath: .status.conditions[?(@.type=="ResourcesApplied")].status + name: Applied + type: string + - description: Indicates whether all resources are healthy. + jsonPath: .status.conditions[?(@.type=="ResourcesHealthy")].status + name: Healthy + type: string + - description: Indicates whether some resources are still progressing to be rolled + out. + jsonPath: .status.conditions[?(@.type=="ResourcesProgressing")].status + name: Progressing + type: string + - description: creation timestamp + jsonPath: .metadata.creationTimestamp + name: Age + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: ManagedResource describes a list of managed resources. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: Spec contains the specification of this managed resource. + properties: + class: + description: Class holds the resource class used to control the responsibility + for multiple resource manager instances + type: string + deletePersistentVolumeClaims: + description: |- + DeletePersistentVolumeClaims specifies if PersistentVolumeClaims created by StatefulSets, which are managed by this + resource, should also be deleted when the corresponding StatefulSet is deleted (defaults to false). + type: boolean + equivalences: + description: Equivalences specifies possible group/kind equivalences + for objects. + items: + items: + description: |- + GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying + concepts during lookup stages without having partially valid types + properties: + group: + type: string + kind: + type: string + required: + - group + - kind + type: object + type: array + type: array + forceOverwriteAnnotations: + description: ForceOverwriteAnnotations specifies that all existing + annotations should be overwritten. Defaults to false. + type: boolean + forceOverwriteLabels: + description: ForceOverwriteLabels specifies that all existing labels + should be overwritten. Defaults to false. + type: boolean + injectLabels: + additionalProperties: + type: string + description: InjectLabels injects the provided labels into every resource + that is part of the referenced secrets. + type: object + keepObjects: + description: |- + KeepObjects specifies whether the objects should be kept although the managed resource has already been deleted. + Defaults to false. + type: boolean + secretRefs: + description: SecretRefs is a list of secret references. + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + required: + - secretRefs + type: object + status: + description: Status contains the status of this managed resource. + properties: + conditions: + items: + description: Condition holds the information about the state of + a resource. + properties: + codes: + description: Well-defined error codes in case the condition + reports a problem. + items: + description: ErrorCode is a string alias. + type: string + type: array + lastTransitionTime: + description: Last time the condition transitioned from one status + to another. + format: date-time + type: string + lastUpdateTime: + description: Last time the condition was updated. + format: date-time + type: string + message: + description: A human readable message indicating details about + the transition. + type: string + reason: + description: The reason for the condition's last transition. + type: string + status: + description: Status of the condition, one of True, False, Unknown. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - lastUpdateTime + - message + - reason + - status + - type + type: object + type: array + observedGeneration: + description: ObservedGeneration is the most recent generation observed + for this resource. + format: int64 + type: integer + resources: + description: Resources is a list of objects that have been created. + items: + description: ObjectReference is a reference to another object. + properties: + annotations: + additionalProperties: + type: string + description: Annotations is a map of annotations that were used + during last update of the resource. + type: object + apiVersion: + description: API version of the referent. + type: string + fieldPath: + description: |- + If referring to a piece of an object instead of an entire object, this string + should contain a valid JSON/Go field access statement, such as desiredState.manifest.containers[2]. + For example, if the object reference is to a container within a pod, this would take on a value like: + "spec.containers{name}" (where "name" refers to the name of the container that triggered + the event) or if no container name is specified "spec.containers[2]" (container with + index 2 in this pod). This syntax is chosen only to have some well-defined way of + referencing a part of an object. + type: string + kind: + description: |- + Kind of the referent. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + labels: + additionalProperties: + type: string + description: Labels is a map of labels that were used during + last update of the resource. + type: object + name: + description: |- + Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + namespace: + description: |- + Namespace of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ + type: string + resourceVersion: + description: |- + Specific resourceVersion to which this reference is made, if any. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency + type: string + uid: + description: |- + UID of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#uids + type: string + type: object + x-kubernetes-map-type: atomic + type: array + secretsDataChecksum: + description: SecretsDataChecksum is the checksum of referenced secrets + data. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} From 60a7f79ae9df96fa77f071b9cc5bab6ee1777297 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Thu, 2 Jul 2026 15:37:57 +0200 Subject: [PATCH 49/57] add test to cover the transition from compat to default --- .../controlplane/controlplane_test.go | 120 ++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/test/integration/controlplane/controlplane_test.go b/test/integration/controlplane/controlplane_test.go index 298dcac7..1be7b533 100644 --- a/test/integration/controlplane/controlplane_test.go +++ b/test/integration/controlplane/controlplane_test.go @@ -20,6 +20,7 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" @@ -273,4 +274,123 @@ var _ = Describe("ControlPlane CSI compatibility mode", func() { }, shootMR) }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Succeed()) }) + + It("should delete both ManagedResources when transitioning from compat to default", func() { + cpConfigCompat := &stackitv1alpha1.ControlPlaneConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: stackitv1alpha1.SchemeGroupVersion.String(), + Kind: "ControlPlaneConfig", + }, + CloudControllerManager: &stackitv1alpha1.CloudControllerManagerConfig{ + Name: string(stackitv1alpha1.STACKIT), + }, + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.STACKIT), + CompatibilityMode: string(stackitv1alpha1.COMPAT), + }, + }, + } + cpConfigCompatBytes, err := json.Marshal(cpConfigCompat) + Expect(err).NotTo(HaveOccurred()) + + infraStatus := &stackitv1alpha1.InfrastructureStatus{ + TypeMeta: metav1.TypeMeta{ + APIVersion: stackitv1alpha1.SchemeGroupVersion.String(), + Kind: "InfrastructureStatus", + }, + Networks: stackitv1alpha1.NetworkStatus{ + ID: "test-network-id", + Name: "test-network-name", + Router: stackitv1alpha1.RouterStatus{ + ID: "test-router-id", + }, + }, + Node: stackitv1alpha1.NodeStatus{KeyName: "test-key"}, + } + infraStatusBytes, err := json.Marshal(infraStatus) + Expect(err).NotTo(HaveOccurred()) + + By("create ControlPlane CR with compat mode") + cp := &extensionsv1alpha1.ControlPlane{ + ObjectMeta: metav1.ObjectMeta{ + Name: "controlplane", + Namespace: namespace, + Labels: map[string]string{"test-id": testID}, + }, + Spec: extensionsv1alpha1.ControlPlaneSpec{ + DefaultSpec: extensionsv1alpha1.DefaultSpec{ + Type: stackit.Type, + ProviderConfig: &runtime.RawExtension{Raw: cpConfigCompatBytes}, + }, + Region: "eu01", + SecretRef: corev1.SecretReference{ + Name: "cloudprovider", + Namespace: namespace, + }, + InfrastructureProviderStatus: &runtime.RawExtension{Raw: infraStatusBytes}, + }, + } + Expect(k8sclient.Create(ctx, cp)).To(Succeed()) + DeferCleanup(func() { Expect(client.IgnoreNotFound(k8sclient.Delete(ctx, cp))).To(Succeed()) }) + + By("wait for seed ManagedResource to be created") + Eventually(func() error { + return k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-chart", + Namespace: namespace, + }, &resourcesv1alpha1.ManagedResource{}) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Succeed()) + + By("wait for shoot ManagedResource to be created") + Eventually(func() error { + return k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-shoot-chart", + Namespace: namespace, + }, &resourcesv1alpha1.ManagedResource{}) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(Succeed()) + + By("update ControlPlane to default compatibility mode") + cpConfigDefault := &stackitv1alpha1.ControlPlaneConfig{ + TypeMeta: metav1.TypeMeta{ + APIVersion: stackitv1alpha1.SchemeGroupVersion.String(), + Kind: "ControlPlaneConfig", + }, + CloudControllerManager: &stackitv1alpha1.CloudControllerManagerConfig{ + Name: string(stackitv1alpha1.STACKIT), + }, + Storage: &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{ + Name: string(stackitv1alpha1.STACKIT), + CompatibilityMode: string(stackitv1alpha1.DEFAULT), + }, + }, + } + cpConfigDefaultBytes, err := json.Marshal(cpConfigDefault) + Expect(err).NotTo(HaveOccurred()) + + By("trigger reconciliation") + Expect(k8sclient.Get(ctx, types.NamespacedName{Name: cp.Name, Namespace: cp.Namespace}, cp)).To(Succeed()) + cp.Spec.ProviderConfig = &runtime.RawExtension{Raw: cpConfigDefaultBytes} + metav1.SetMetaDataAnnotation(&cp.ObjectMeta, "gardener.cloud/operation", "reconcile") + Expect(k8sclient.Update(ctx, cp)).To(Succeed()) + + By("wait for seed ManagedResource to be deleted") + Eventually(func() bool { + err := k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-chart", + Namespace: namespace, + }, &resourcesv1alpha1.ManagedResource{}) + return apierrors.IsNotFound(err) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(BeTrue()) + + By("wait for shoot ManagedResource to be deleted") + Eventually(func() bool { + err := k8sclient.Get(ctx, types.NamespacedName{ + Name: "stackit-compatibility-shoot-chart", + Namespace: namespace, + }, &resourcesv1alpha1.ManagedResource{}) + return apierrors.IsNotFound(err) + }).WithTimeout(2 * time.Minute).WithPolling(2 * time.Second).Should(BeTrue()) + }) }) From 6ce05fdeaaf83ca23d862b9f450c5c3088286c18 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 6 Jul 2026 16:23:42 +0200 Subject: [PATCH 50/57] go mod tidy --- go.sum | 259 ++++++++++++++++++++++++++------------------------------- 1 file changed, 116 insertions(+), 143 deletions(-) diff --git a/go.sum b/go.sum index 4bc2b3e0..3eebd2d6 100644 --- a/go.sum +++ b/go.sum @@ -69,34 +69,34 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -github.com/aws/aws-sdk-go-v2 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls= -github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4= -github.com/aws/aws-sdk-go-v2/config v1.32.10 h1:9DMthfO6XWZYLfzZglAgW5Fyou2nRI5CuV44sTedKBI= -github.com/aws/aws-sdk-go-v2/config v1.32.10/go.mod h1:2rUIOnA2JaiqYmSKYmRJlcMWy6qTj1vuRFscppSBMcw= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10 h1:EEhmEUFCE1Yhl7vDhNOI5OCL/iKMdkkYFTRpZXNw7m8= -github.com/aws/aws-sdk-go-v2/credentials v1.19.10/go.mod h1:RnnlFCAlxQCkN2Q379B67USkBMu1PipEEiibzYN5UTE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= -github.com/aws/smithy-go v1.24.1 h1:VbyeNfmYkWoxMVpGUAbQumkODcYmfMRfZ8yQiH30SK0= -github.com/aws/smithy-go v1.24.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/aws/aws-sdk-go-v2 v1.41.7 h1:DWpAJt66FmnnaRIOT/8ASTucrvuDPZASqhhLey6tLY8= +github.com/aws/aws-sdk-go-v2 v1.41.7/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc= +github.com/aws/aws-sdk-go-v2/config v1.32.17 h1:FpL4/758/diKwqbytU0prpuiu60fgXKUWCpDJtApclU= +github.com/aws/aws-sdk-go-v2/config v1.32.17/go.mod h1:OXqUMzgXytfoF9JaKkhrOYsyh72t9G+MJH8mMRaexOE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16 h1:r3RJBuU7X9ibt8RHbMjWE6y60QbKBiII6wSrXnapxSU= +github.com/aws/aws-sdk-go-v2/credentials v1.19.16/go.mod h1:6cx7zqDENJDbBIIWX6P8s0h6hqHC8Avbjh9Dseo27ug= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23 h1:UuSfcORqNSz/ey3VPRS8TcVH2Ikf0/sC+Hdj400QI6U= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.23/go.mod h1:+G/OSGiOFnSOkYloKj/9M35s74LgVAdJBSD5lsFfqKg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23 h1:GpT/TrnBYuE5gan2cZbTtvP+JlHsutdmlV2YfEyNde0= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.23/go.mod h1:xYWD6BS9ywC5bS3sz9Xh04whO/hzK2plt2Zkyrp4JuA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23 h1:bpd8vxhlQi2r1hiueOw02f/duEPTMK59Q4QMAoTTtTo= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.23/go.mod h1:15DfR2nw+CRHIk0tqNyifu3G1YdAOy68RftkhMDDwYk= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24 h1:OQqn11BtaYv1WLUowvcA30MpzIu8Ti4pcLPIIyoKZrA= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.24/go.mod h1:X5ZJyfwVrWA96GzPmUCWFQaEARPR7gCrpq2E92PJwAE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23 h1:pbrxO/kuIwgEsOPLkaHu0O+m4fNgLU8B3vxQ+72jTPw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.23/go.mod h1:/CMNUqoj46HpS3MNRDEDIwcgEnrtZlKRaHNaHxIFpNA= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11 h1:TdJ+HdzOBhU8+iVAOGUTU63VXopcumCOF1paFulHWZc= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.11/go.mod h1:R82ZRExE/nheo0N+T8zHPcLRTcH8MGsnR3BiVGX0TwI= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17 h1:7byT8HUWrgoRp6sXjxtZwgOKfhss5fW6SkLBtqzgRoE= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.17/go.mod h1:xNWknVi4Ezm1vg1QsB/5EWpAJURq22uqd38U8qKvOJc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21 h1:+1Kl1zx6bWi4X7cKi3VYh29h8BvsCoHQEQ6ST9X8w7w= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.21/go.mod h1:4vIRDq+CJB2xFAXZ+YgGUTiEft7oAQlhIs71xcSeuVg= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1 h1:F/M5Y9I3nwr2IEpshZgh1GeHpOItExNM9L1euNuh/fk= +github.com/aws/aws-sdk-go-v2/service/sts v1.42.1/go.mod h1:mTNxImtovCOEEuD65mKW7DCsL+2gjEH+RPEAexAzAio= +github.com/aws/smithy-go v1.25.1 h1:J8ERsGSU7d+aCmdQur5Txg6bVoYelvQJgtZehD12GkI= +github.com/aws/smithy-go v1.25.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -162,18 +162,18 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gardener/cert-management v0.22.0 h1:82uZiLzYs+W/Qp/z1XnUSN+nmNDCMFUcdgn5sX5xP7M= -github.com/gardener/cert-management v0.22.0/go.mod h1:hJvxHEKHwdSywZUxDqO7vUZN4D7udfhgtpdfDOoaKNs= -github.com/gardener/etcd-druid/api v0.36.3 h1:mBhDE/TJmcn2BY8Fscd5SfBSeWvtK4ZBPEW+v6XsQIQ= -github.com/gardener/etcd-druid/api v0.36.3/go.mod h1:RwZzKp8K415AS0zg8VoODjBxYepCAUYyLgXnZc1bmbo= -github.com/gardener/gardener v1.143.1 h1:+jCDk/j629Jcmgroltv8Egd8dfgvMwRvDs5lwA6oKbY= -github.com/gardener/gardener v1.143.1/go.mod h1:jwMrEnyQtztkCNsVu70/h/YWMHJXkAZa+iFrZoC1bFo= -github.com/gardener/gardener-extension-networking-calico v1.58.0 h1:jCHWWRPB5temGZYYl1mls1Aw69Xmvj44meJ2gEWE4Rw= -github.com/gardener/gardener-extension-networking-calico v1.58.0/go.mod h1:9nbH2Ftc2Rk/HQ2nYMiiHtMTENLAj0OaadGVts3zCu8= -github.com/gardener/gardener-extension-os-coreos v1.29.0 h1:u7O8EdiOLePi87K0pc2gwyoq+BoTAgYopRp/6q62PrM= -github.com/gardener/gardener-extension-os-coreos v1.29.0/go.mod h1:a3q58HtgudEqVq9RKTzO0EQUcBj6LrwoAmaZx3dqZEY= -github.com/gardener/gardener/pkg/apis v1.143.1 h1:zNhbd4ak2k3+gQU9RV7oyjjgPGfHfYMDK+r/TQTDJeY= -github.com/gardener/gardener/pkg/apis v1.143.1/go.mod h1:zGVoiiKNeCwH7f6+4pQI9lOGVqkJueaONqyfyoiLDpk= +github.com/gardener/cert-management v0.23.0 h1:kD88XcPn6C4zLc8EYtrHyb+/45Iyaozhb+HEM44MKz0= +github.com/gardener/cert-management v0.23.0/go.mod h1:Mehn8XY+iAkm8XOBbNGHmbMft8fP9ZEJFWzWSonPkfc= +github.com/gardener/etcd-druid/api v0.36.4 h1:o/17ciPrbh/w+igKMUuglW7N9XLjoMh7AvRKTzsBEVs= +github.com/gardener/etcd-druid/api v0.36.4/go.mod h1:RwZzKp8K415AS0zg8VoODjBxYepCAUYyLgXnZc1bmbo= +github.com/gardener/gardener v1.145.1 h1:o+gASDy1iuu68wN5VhgOEjD7Sn+BPJV9hFwPe3Tc6gE= +github.com/gardener/gardener v1.145.1/go.mod h1:cDTZIPHaoj9ywmBTvronK5SpPBc/zI942hsp2//WVNo= +github.com/gardener/gardener-extension-networking-calico v1.59.0 h1:L5w61z7ueiV8y2m4oHLFEQbv3sw7JVqouk3o0UPjdu8= +github.com/gardener/gardener-extension-networking-calico v1.59.0/go.mod h1:CTdu8c+5C8y7yoNR6QAvDBVRO4l8i8r7jvzIKv9ZFRc= +github.com/gardener/gardener-extension-os-coreos v1.30.0 h1:v0eqpNL+VkAH5bN/xQ141ystGTcl/+FF18Quqd2XcNY= +github.com/gardener/gardener-extension-os-coreos v1.30.0/go.mod h1:d5K2QNpBjBQZZo1T6QM2x30GlmRDg1qH3YUZQV6yLh0= +github.com/gardener/gardener/pkg/apis v1.145.1 h1:BYPdNqpFw5v5HGt6/3R7MBLvaLPaqmbsNWlJDjRQImM= +github.com/gardener/gardener/pkg/apis v1.145.1/go.mod h1:LsjZw5/3awWSMDnKg4bgftM1kW9Dkeor/ged5DNHVPI= github.com/gardener/machine-controller-manager v0.61.3 h1:w0JuHCKLmcK7B8E7mx3TvE3e0hSYwikchsMSiMhocqw= github.com/gardener/machine-controller-manager v0.61.3/go.mod h1:8eE1qLztrWIbOM71mHSQGaC6Q+pl5lvOyN08qP39D7o= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -216,32 +216,32 @@ github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochM github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= -github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= -github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk= -github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc= +github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I= +github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE= +github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU= +github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc= github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= -github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= -github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA= +github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y= github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw= github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY= github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= -github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= -github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= -github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= -github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg= +github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE= +github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4= +github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE= github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= -github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= -github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= -github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.0 h1:3hZD1fwydvCx/cc1R2uYNQirHqf2s6lqpKV3FcNTURA= +github.com/go-openapi/testify/enable/yaml/v2 v2.5.0/go.mod h1:TvDZKBH7ZbMaF3EqH2AwTvNQCmzyZq8K1agRjf1B+Nk= +github.com/go-openapi/testify/v2 v2.5.0 h1:UOCr63aAsMIDydZbZGqo5Ev01D4eydItRbekDuZMJLw= +github.com/go-openapi/testify/v2 v2.5.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= @@ -312,10 +312,10 @@ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/gophercloud/gophercloud/v2 v2.12.0 h1:Gxmc/Bog1UDKkxTcQW7MSPTDviJXpLeEgVeN5KrxoCo= -github.com/gophercloud/gophercloud/v2 v2.12.0/go.mod h1:H7TTOxbLy8RIaHSNhI2GCrWIzw4Xpw8Xn2mBhCUT5kA= -github.com/gophercloud/utils/v2 v2.0.0-20260424064311-2eeed4ceb3e9 h1:WEPhYFzYmpfWHq+YPaP3+8pYf4wKQuJMkgPiiI4g7CY= -github.com/gophercloud/utils/v2 v2.0.0-20260424064311-2eeed4ceb3e9/go.mod h1:bIEH+wgvnxfegUewFuGi0u/L+ji5uiEuVVQMNKQASEY= +github.com/gophercloud/gophercloud/v2 v2.13.0 h1:yEyJG+kABd8x2ttTqLsomihU6Kg2YheJSZhvP/QSx+8= +github.com/gophercloud/gophercloud/v2 v2.13.0/go.mod h1:KZRLVs6gcoy/pEFdkZqFjdYqnS0emMHv66UqdM5lMjU= +github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13 h1:Dnid+JYEmkqPWw/vJHRUzZsjO3mdoyCRo2l5UYCqh8k= +github.com/gophercloud/utils/v2 v2.0.0-20260626221802-4ae35253ac13/go.mod h1:zpDKeT3ElgCs1UA+7B8+XlDu3R+jkK/CBC1di5qOeow= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -325,8 +325,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0 h1:FbSCl+KggFl+Ocym490i/E github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -383,8 +383,8 @@ github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4= +github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -421,14 +421,16 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.29.0 h1:rfh+ZFjgJhYWRoIqVf3Uwx/W20yLrcrE2h2GmYVRaag= -github.com/onsi/ginkgo/v2 v2.29.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= +github.com/onsi/ginkgo/v2 v2.32.0 h1:Hw7s2pVrQo/8Yz5N77qdnpHaoc+c6cC9WIV1Jce+J6E= +github.com/onsi/ginkgo/v2 v2.32.0/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.41.0 h1:OwKp4pXNgVxf6sCplzYo794OFNuoL2q2SBMU5NSWOjA= -github.com/onsi/gomega v1.41.0/go.mod h1:M/Uqpu/8qTjtzCLUA2zJHX9Iilrau25x1PdoSRbWh5A= -github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= -github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I= +github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg= +github.com/open-telemetry/opentelemetry-operator/apis v0.153.0 h1:ALN6Bo+OU2M/KOT4n/8egYiLNA7M1dC4bOgs2UqC40Q= +github.com/open-telemetry/opentelemetry-operator/apis v0.153.0/go.mod h1:rK5glhBXD9XrMQYfewsF940NPO3LdXdJU2FJJGdBCZ4= +github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY= +github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/perses/common v0.30.2 h1:RAiVxUpX76lTCb4X7pfcXSvYdXQmZwKi4oDKAEO//u0= github.com/perses/common v0.30.2/go.mod h1:DFtur1QPah2/ChXbKKhw7djYdwNgz27s5fPKpiK0Xao= github.com/perses/perses v0.53.1 h1:9VY/6p9QWrZwPSV7qiwTMSOsgcB37Lb1AXKT0ORXc6I= @@ -445,15 +447,13 @@ github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.91.0 h github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.91.0/go.mod h1:Gfzi4500QCMnptFIQc8YdDi8YZ4QA0vs22LROWZ3+YU= github.com/prometheus/alertmanager v0.29.0 h1:/ET4NmAGx2Dv9kStrXIBqBgHyiSgIk4OetY+hoZRfgc= github.com/prometheus/alertmanager v0.29.0/go.mod h1:SjI2vhrfdWg10UaRUxTz27rgdJVG3HXrhI5WFjCdBgs= -github.com/prometheus/client_golang v1.23.3-0.20260518105423-c9d5bc4c50a9 h1:rneH9T2i7VvR/2yKvhQ8bkmFEdJ6yuP/2UC8Wx71vIM= -github.com/prometheus/client_golang v1.23.3-0.20260518105423-c9d5bc4c50a9/go.mod h1:QRA+KSZEtitKV4dtGtD9gCh8T2Gy4gifR1dqcT1MMss= +github.com/prometheus/client_golang v1.23.3-0.20260602051030-3537b20ac86b h1:QNV54DNcRqdeECNdEXiOqTmI75w2rlZtOq5rt8RKhVo= +github.com/prometheus/client_golang v1.23.3-0.20260602051030-3537b20ac86b/go.mod h1:kPaff19KETV3GKIZJehgPmlA2Di3jNeWdgKA9RpObuU= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.67.6-0.20260224092343-e4c38a0aea47 h1:DXz2mpU9UOivlNi73hwwVm7KSYGImaPDdYsniI6uOdE= -github.com/prometheus/common v0.67.6-0.20260224092343-e4c38a0aea47/go.mod h1:6wuJRcHcgBM1w2vIgTuCW7XSzlfCat/lS3C39U02oDA= -github.com/prometheus/otlptranslator v1.0.0 h1:s0LJW/iN9dkIH+EnhiD3BlkkP5QVIUVEoIwkU+A6qos= -github.com/prometheus/otlptranslator v1.0.0/go.mod h1:vRYWnXvI6aWGpsdY/mOT/cbeVRBlPWtBNDb7kGR3uKM= +github.com/prometheus/common v0.68.1 h1:omjRRl4QP4komogpXuhfeOiisQg7xdy8VM1UY+pStaY= +github.com/prometheus/common v0.68.1/go.mod h1:ZzL3f6u94qUxh9p+tJTrF+FvBS1XXbbRAZCQkytAL0Y= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/prometheus/sigv4 v0.3.0 h1:QIG7nTbu0JTnNidGI1Uwl5AGVIChWUACxn2B/BQ1kms= @@ -571,46 +571,20 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo= -go.opentelemetry.io/contrib/otelconf v0.23.0 h1:s3C7KdMYiutf4rC8hKFA0WOIDG+gIru8ajjQKS59ir8= -go.opentelemetry.io/contrib/otelconf v0.23.0/go.mod h1:0kN2tcccZS82e7IZlo045gkcL8/8dup1k25sf9ypGxM= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE= -go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= -go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= -go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= -go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= -go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= +go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo= +go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= +go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= +go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= +go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= +go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= +go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= +go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= +go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -636,15 +610,15 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= -golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= -golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= +golang.org/x/exp v0.0.0-20260527015227-08cc5374adb3 h1:VHEvKbpgPXcPXn40t9cDTGK3JZwMikIEyF/CTrFfu7k= +golang.org/x/exp v0.0.0-20260527015227-08cc5374adb3/go.mod h1:d2fgXJLVs4dYDHUk5lwMIfzRzSrWCfGZb0ZqeLa/Vcw= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -681,8 +655,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -697,8 +671,8 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -715,19 +689,18 @@ golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6 h1:HjU6IWBiAgRIdAJ9/y1rwCn+UELEmwV+VsTLzj/W4sE= -golang.org/x/telemetry v0.0.0-20260508192327-42602be52be6/go.mod h1:Eqhaxk/wZsWEH8CRxLwj6xzEJbz7k1EFGqx7nyCoabE= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57 h1:nwGZBCt+FnXUrGsj5vjzAsEmkcaFvd82BbOjECiFYZc= +golang.org/x/telemetry v0.0.0-20260625142307-59b4966ccb57/go.mod h1:3AWMyWHS+caVoiEXpiq6+tzKA40J4vQT3MYr80ZtQpc= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -758,8 +731,8 @@ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= @@ -792,18 +765,18 @@ google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98 google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d h1:/aDRtSZJjyLQzm75d+a1wOJaqyKBMvIAfeQmoa3ORiI= -google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:etfGUgejTiadZAUaEP14NP97xi1RGeawqkjDARA/UOs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8= +google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= -google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ= +google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -845,8 +818,8 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -istio.io/api v1.29.3 h1:olbjGA/wLDB62U3SsBn+lFHHnugnDTLoCRE0pBWXZ8M= -istio.io/api v1.29.3/go.mod h1:+brQWcBHoROuyA6fv8rbgg8Kfn0RCGuqoY0duCMuSLA= +istio.io/api v1.29.4 h1:6cVx5lLSjMwLehIjzlAhLeNgBiWaZnOht7iUen4MhLY= +istio.io/api v1.29.4/go.mod h1:+brQWcBHoROuyA6fv8rbgg8Kfn0RCGuqoY0duCMuSLA= istio.io/client-go v1.29.2 h1:aljV/qXcEGgRYisPTJ7L0Nx8GXWbM8rZgr1eEAC3bCw= istio.io/client-go v1.29.2/go.mod h1:RazwM5KR3FzhWXrvjCq1o85ReFNrHcG3cF2lcs5anzg= k8s.io/api v0.19.0/go.mod h1:I1K45XlvTrDjmj5LoM5LuP/KYrhWbjUKT/SoPG0qTjw= From 4d8bffc91fdb48d9b1ca1e30e21f2ac26f9b784d Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 6 Jul 2026 16:45:55 +0200 Subject: [PATCH 51/57] add a description for the two CSI config fields --- pkg/apis/stackit/v1alpha1/types_controlplane.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/apis/stackit/v1alpha1/types_controlplane.go b/pkg/apis/stackit/v1alpha1/types_controlplane.go index 5b28637d..426d0df5 100644 --- a/pkg/apis/stackit/v1alpha1/types_controlplane.go +++ b/pkg/apis/stackit/v1alpha1/types_controlplane.go @@ -58,7 +58,15 @@ type Storage struct { } type CSI struct { - Name string `json:"name"` + // Name contains the name of the used CSI driver: + // - "openstack" uses the legacy cinder driver + // - "stackit" uses the new STACKIT CSI driver + Name string `json:"name"` + // CompatibilityMode can be used to enable a compatibility layer for clusters that still uses cinder volumes: + // - "default" uses only the new STACKIT CSI driver, no compatibility layer is active (default value, can be omitted) + // - "compat" enables the cinder compatibility layer in addition to the STACKIT CSI driver to allow access to cinder volumes + // - "compatblock" enables the cinder compatibility layer like "compat", but does not allow to create new cinder volumes + // +optional CompatibilityMode string `json:"compatibilityMode,omitempty"` } From 7eef97a64597d8d3ec258040faf58cf448c13e68 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 6 Jul 2026 16:46:25 +0200 Subject: [PATCH 52/57] use the flag instead of a string comparison --- .../stackit-blockstorage-csi-driver/templates/daemonset.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index 273f2941..5a31672b 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -17,7 +17,7 @@ spec: metadata: annotations: checksum/configmap-cloud-provider-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - {{- if eq .Values.driverName "cinder.csi.openstack.org" }} + {{- if eq .Values.csi.enableCompatibilityMode }} node.gardener.cloud/wait-for-csi-node-openstack: {{ .Values.driverName }} {{- else }} node.gardener.cloud/wait-for-csi-node-stackit: {{ .Values.driverName }} From ef0520e337485b8ab8b3b2fefc2fee2143b2ff1b Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 6 Jul 2026 16:52:51 +0200 Subject: [PATCH 53/57] update api documentation --- hack/api-reference/api.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hack/api-reference/api.md b/hack/api-reference/api.md index e1c00e93..3a8dcc99 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -249,7 +249,7 @@ string -

+

Name contains the name of the used CSI driver:
- "openstack" uses the legacy cinder driver
- "stackit" uses the new STACKIT CSI driver

@@ -260,7 +260,8 @@ string -

+(Optional) +

CompatibilityMode can be used to enable a compatibility layer for clusters that still uses cinder volumes:
- "default" uses only the new STACKIT CSI driver, no compatibility layer is active (default value, can be omitted)
- "compat" enables the cinder compatibility layer in addition to the STACKIT CSI driver to allow access to cinder volumes
- "compatblock" enables the cinder compatibility layer like "compat", but does not allow to create new cinder volumes

From eacb4a15f6d317713a22f4847ca8618f47f8d7c7 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Mon, 6 Jul 2026 16:56:16 +0200 Subject: [PATCH 54/57] fix wrong check --- .../stackit-blockstorage-csi-driver/templates/daemonset.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml index 5a31672b..931eb3ea 100644 --- a/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml +++ b/charts/internal/shoot-system-components/charts/stackit-blockstorage-csi-driver/templates/daemonset.yaml @@ -17,7 +17,7 @@ spec: metadata: annotations: checksum/configmap-cloud-provider-config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} - {{- if eq .Values.csi.enableCompatibilityMode }} + {{- if .Values.csi.enableCompatibilityMode }} node.gardener.cloud/wait-for-csi-node-openstack: {{ .Values.driverName }} {{- else }} node.gardener.cloud/wait-for-csi-node-stackit: {{ .Values.driverName }} From ecafaa82efa675423a3811db620a63b3f9630140 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Tue, 7 Jul 2026 13:42:20 +0200 Subject: [PATCH 55/57] replace switch/case with if and early bail out --- .../controlplane/csi_compatibility.go | 49 ++++++++++--------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index db59f27a..73c4cf16 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -43,23 +43,25 @@ type CompatCSICompatibilityHandler struct { func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context.Context, namespace string, version string, cpConfig *stackitv1alpha1.ControlPlaneConfig, controlPlaneValues map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) - switch compatibilityMode { - case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: - blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK - chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues, namespace, version, blockLegacyCreation) - if err != nil { - return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) - } - err = ch.deploySeedCSICompatibilityMode(ctx, namespace, chart) - if err != nil { - return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) - } - default: + blockLegacyCreation := compatibilityMode == stackitv1alpha1.COMPATBLOCK + + if compatibilityMode != stackitv1alpha1.COMPAT && compatibilityMode != stackitv1alpha1.COMPATBLOCK { err := ch.deleteSeedCSICompatibilityMode(ctx, namespace) if err != nil { return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) } + return nil + } + + chart, err := ch.renderSeedCSICompatibilityMode(controlPlaneValues, namespace, version, blockLegacyCreation) + if err != nil { + return fmt.Errorf("failed to render seed CSI compatibility mode: %w", err) + } + err = ch.deploySeedCSICompatibilityMode(ctx, namespace, chart) + if err != nil { + return fmt.Errorf("failed to deploy seed CSI compatibility mode: %w", err) } + return nil } @@ -104,22 +106,23 @@ func (ch *CompatCSICompatibilityHandler) deleteSeedCSICompatibilityMode(ctx cont func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context.Context, namespace string, version string, cpConfig *stackitv1alpha1.ControlPlaneConfig, values map[string]any) error { compatibilityMode := getCSICompatibilityMode(cpConfig) - switch compatibilityMode { - case stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK: - chart, err := ch.renderShootCSICompatibilityMode(values, version) - if err != nil { - return fmt.Errorf("render shoot CSI compatibility mode: %w", err) - } - err = ch.deployShootCSICompatibilityMode(ctx, namespace, chart) - if err != nil { - return fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) - } - default: + if compatibilityMode != stackitv1alpha1.COMPAT && compatibilityMode != stackitv1alpha1.COMPATBLOCK { err := ch.deleteShootCSICompatibilityMode(ctx, namespace) if err != nil { return fmt.Errorf("delete shoot CSI compatibility mode: %w", err) } + return nil } + + chart, err := ch.renderShootCSICompatibilityMode(values, version) + if err != nil { + return fmt.Errorf("render shoot CSI compatibility mode: %w", err) + } + err = ch.deployShootCSICompatibilityMode(ctx, namespace, chart) + if err != nil { + return fmt.Errorf("deploy shoot CSI compatibility mode: %w", err) + } + return nil } From 2a4c621abddf0d3edf6494741f8e900576775857 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 8 Jul 2026 11:55:13 +0200 Subject: [PATCH 56/57] add validation of the compatibilityMode field --- pkg/apis/stackit/validation/controlplane.go | 18 +++++++++++++++--- .../stackit/validation/controlplane_test.go | 12 ++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/pkg/apis/stackit/validation/controlplane.go b/pkg/apis/stackit/validation/controlplane.go index 86d88aac..3237f9c0 100644 --- a/pkg/apis/stackit/validation/controlplane.go +++ b/pkg/apis/stackit/validation/controlplane.go @@ -14,7 +14,10 @@ import ( ) var ( - validControllers = []stackitv1alpha1.ControllerName{stackitv1alpha1.STACKIT, stackitv1alpha1.OPENSTACK} + validControllers = []stackitv1alpha1.ControllerName{stackitv1alpha1.STACKIT, stackitv1alpha1.OPENSTACK} + validCSICompatibilityModes = []stackitv1alpha1.CSICompatibilityMode{ + stackitv1alpha1.DEFAULT, stackitv1alpha1.COMPAT, stackitv1alpha1.COMPATBLOCK, + } ) // ValidateControlPlaneConfig validates a ControlPlaneConfig object. @@ -58,11 +61,20 @@ func validateCloudController(cloudcontroller *stackitv1alpha1.CloudControllerMan func validateStorage(storage *stackitv1alpha1.Storage, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList - if storage == nil { + if storage == nil || storage.CSI == nil { return allErrs } - if storage.CSI != nil && !slices.Contains(validControllers, stackitv1alpha1.ControllerName(storage.CSI.Name)) { + if !slices.Contains(validControllers, stackitv1alpha1.ControllerName(storage.CSI.Name)) { allErrs = append(allErrs, field.Invalid(fldPath.Child("csi", "name"), storage.CSI.Name, "not supported csi driver")) } + // CompatibilityMode is optional; empty is accepted (defaulting sets it to "default"). + if storage.CSI.CompatibilityMode != "" && + !slices.Contains(validCSICompatibilityModes, stackitv1alpha1.CSICompatibilityMode(storage.CSI.CompatibilityMode)) { + allErrs = append(allErrs, field.Invalid( + fldPath.Child("csi", "compatibilityMode"), + storage.CSI.CompatibilityMode, + "not supported CSI compatibility mode", + )) + } return allErrs } diff --git a/pkg/apis/stackit/validation/controlplane_test.go b/pkg/apis/stackit/validation/controlplane_test.go index 7eccc96b..8ded76b1 100644 --- a/pkg/apis/stackit/validation/controlplane_test.go +++ b/pkg/apis/stackit/validation/controlplane_test.go @@ -48,6 +48,18 @@ var _ = Describe("ControlPlaneConfig validation", func() { })), )) }) + + It("should fail with an unsupported CSI compatibility mode", func() { + controlPlane.Storage = &stackitv1alpha1.Storage{ + CSI: &stackitv1alpha1.CSI{Name: "stackit", CompatibilityMode: "bogus"}, + } + Expect(ValidateControlPlaneConfig(controlPlane, infraConfig, "", nilPath)).To(ConsistOf( + PointTo(MatchFields(IgnoreExtras, Fields{ + "Type": Equal(field.ErrorTypeInvalid), + "Field": Equal("storage.csi.compatibilityMode"), + })), + )) + }) }) Describe("#ValidateControlPlaneConfigUpdate", func() { From d222b6b162c9ebe1fc0fdeef913ed7f534c0b3d9 Mon Sep 17 00:00:00 2001 From: Florian Thienel Date: Wed, 8 Jul 2026 12:03:23 +0200 Subject: [PATCH 57/57] use specific names for the seed and shoot CSI charts --- pkg/controller/controlplane/csi_compatibility.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/controller/controlplane/csi_compatibility.go b/pkg/controller/controlplane/csi_compatibility.go index 73c4cf16..623e2fa0 100644 --- a/pkg/controller/controlplane/csi_compatibility.go +++ b/pkg/controller/controlplane/csi_compatibility.go @@ -66,7 +66,7 @@ func (ch *CompatCSICompatibilityHandler) HandleSeedCSICompatibility(ctx context. } func (ch *CompatCSICompatibilityHandler) renderSeedCSICompatibilityMode(values map[string]any, namespace string, version string, blockLegacyCreation bool) (*chartrenderer.RenderedChart, error) { - chartValues := composeCompatibilityChartValues(values) + chartValues := composeCompatibilityChartValues(values, openstack.CSISTACKITControllerName) // Override chart values chartValues["prefix"] = csiCompatibilityPrefix @@ -127,7 +127,7 @@ func (ch *CompatCSICompatibilityHandler) HandleShootCSICompatibility(ctx context } func (ch *CompatCSICompatibilityHandler) renderShootCSICompatibilityMode(values map[string]any, version string) (*chartrenderer.RenderedChart, error) { - chartValues := composeCompatibilityChartValues(values) + chartValues := composeCompatibilityChartValues(values, openstack.CSISTACKITNodeName) // Override chart values chartValues["prefix"] = csiCompatibilityPrefix @@ -164,12 +164,12 @@ func (ch *CompatCSICompatibilityHandler) deleteShootCSICompatibilityMode(ctx con } // composeCompatibilityChartValues returns a copy of the given values map merged with the csiStackitValues on topLevel. -// Basically removes the openstack.CSISTACKITControllerName key -func composeCompatibilityChartValues(values map[string]any) map[string]any { +// Basically moves the contents of the csiSTACKITChartName key into the configuration's root level. +func composeCompatibilityChartValues(values map[string]any, csiSTACKITChartName string) map[string]any { if values == nil { return map[string]any{} } - csiStackitValues, ok := values[openstack.CSISTACKITControllerName].(map[string]any) + csiStackitValues, ok := values[csiSTACKITChartName].(map[string]any) if !ok { csiStackitValues = nil }