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/hack/api-reference/api.md b/hack/api-reference/api.md index 8bbbc206..6169a6c6 100644 --- a/hack/api-reference/api.md +++ b/hack/api-reference/api.md @@ -215,6 +215,17 @@ string

+ + +compatibilityMode
+ +string + + + +

+ + diff --git a/pkg/apis/stackit/v1alpha1/constants.go b/pkg/apis/stackit/v1alpha1/constants.go index 86bbdc81..42400ae6 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" ) @@ -15,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/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 + } } 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) diff --git a/pkg/controller/controlplane/valuesprovider.go b/pkg/controller/controlplane/valuesprovider.go index 180ac234..af0a341b 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 @@ -736,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: @@ -750,19 +764,17 @@ 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 { + return nil, fmt.Errorf("failed to deploy CSI CSI compatibility mode: %w", err) + } + } default: 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) @@ -1076,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) } @@ -1176,6 +1193,114 @@ 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, + namespace, + chartValues, + ) + if err != nil { + return err + } + + 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), + ) +} + +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-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 + + 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. @@ -1216,6 +1341,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) }