diff --git a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml index b62635034..6dd0cd393 100644 --- a/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml +++ b/charts/cluster-api-runtime-extensions-nutanix/templates/csi/nutanix/manifests/helm-addon-installation.yaml @@ -8,5 +8,7 @@ metadata: name: '{{ .Values.hooks.csi.nutanix.helmAddonStrategy.defaultValueTemplateConfigMap.name }}' data: values.yaml: |- + # The Secret containing the credentials will be created by the handler. createSecret: false + secretName: nutanix-csi-credentials {{- end -}} diff --git a/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go b/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go index 90072b3a2..c08ef0c18 100644 --- a/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go +++ b/pkg/handlers/generic/lifecycle/csi/aws-ebs/handler.go @@ -21,6 +21,11 @@ import ( "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/pkg/handlers/options" ) +var defaultStorageClassParams = map[string]string{ + "csi.storage.k8s.io/fstype": "ext4", + "type": "gp3", +} + type AWSEBSConfig struct { *options.GlobalOptions defaultAWSEBSConfigMapName string @@ -81,14 +86,14 @@ func (a *AWSEBS) createStorageClasses(ctx context.Context, defaultStorageConfig *v1alpha1.DefaultStorage, ) error { allStorageClasses := make([]runtime.Object, 0, len(configs)) - for _, c := range configs { - setAsDefault := c.Name == defaultStorageConfig.StorageClassConfigName && + for _, config := range configs { + setAsDefault := config.Name == defaultStorageConfig.StorageClassConfigName && v1alpha1.CSIProviderAWSEBS == defaultStorageConfig.ProviderName allStorageClasses = append(allStorageClasses, lifecycleutils.CreateStorageClass( - c, - a.config.GlobalOptions.DefaultsNamespace(), + config, v1alpha1.AWSEBSProvisioner, setAsDefault, + defaultStorageClassParams, )) } cm, err := lifecycleutils.CreateConfigMapForCRS( diff --git a/pkg/handlers/generic/lifecycle/csi/handler.go b/pkg/handlers/generic/lifecycle/csi/handler.go index fd07767c6..178869808 100644 --- a/pkg/handlers/generic/lifecycle/csi/handler.go +++ b/pkg/handlers/generic/lifecycle/csi/handler.go @@ -118,7 +118,7 @@ func (c *CSIHandler) AfterControlPlaneInitialized( ) continue } - log.Info(fmt.Sprintf("Creating csi provider %s", provider.Name)) + log.Info(fmt.Sprintf("Creating CSI provider %s", provider.Name)) err = handler.Apply( ctx, provider, @@ -129,11 +129,17 @@ func (c *CSIHandler) AfterControlPlaneInitialized( log.Error( err, fmt.Sprintf( - "failed to create %s csi driver object.", + "failed to delpoy %s CSI driver", provider.Name, ), ) resp.SetStatus(runtimehooksv1.ResponseStatusFailure) + resp.SetMessage( + fmt.Sprintf( + "failed to deploy CSI driver: %v", + err, + ), + ) } } } diff --git a/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go b/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go index 5682f1a6d..ff1119ac8 100644 --- a/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go +++ b/pkg/handlers/generic/lifecycle/csi/nutanix-csi/handler.go @@ -8,7 +8,6 @@ import ( "fmt" "github.com/spf13/pflag" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" @@ -24,16 +23,32 @@ import ( ) const ( - defaultHelmRepositoryURL = "https://nutanix.github.io/helm/" - defaultStorageHelmChartVersion = "v2.6.6" - defaultStorageHelmChartName = "nutanix-csi-storage" - defaultStorageHelmReleaseNameTemplate = "nutanix-csi-storage-%s" - - defaultSnapshotHelmChartVersion = "v6.3.2" - defaultSnapshotHelmChartName = "nutanix-csi-snapshot" - defaultSnapshotHelmReleaseNameTemplate = "nutanix-csi-snapshot-%s" + defaultHelmRepositoryURL = "https://nutanix.github.io/helm/" + defaultStorageHelmChartVersion = "v2.6.6" + defaultStorageHelmChartName = "nutanix-csi-storage" + defaultStorageHelmReleaseName = "nutanix-csi-storage" + defaultStorageHelmReleaseNamespace = "ntnx-system" + + defaultSnapshotHelmChartVersion = "v6.3.2" + defaultSnapshotHelmChartName = "nutanix-csi-snapshot" + defaultSnapshotHelmReleaseName = "nutanix-csi-snapshot" + defaultSnapshotHelmReleaseNamespace = "ntnx-system" + + //nolint:gosec // Does not contain hard coded credentials. + defaultCredentialsSecretName = "nutanix-csi-credentials" ) +var defaultStorageClassParameters = map[string]string{ + "storageType": "NutanixVolumes", + "csi.storage.k8s.io/fstype": "xfs", + "csi.storage.k8s.io/provisioner-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/provisioner-secret-namespace": defaultStorageHelmReleaseNamespace, + "csi.storage.k8s.io/node-publish-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/node-publish-secret-namespace": defaultStorageHelmReleaseNamespace, + "csi.storage.k8s.io/controller-expand-secret-name": defaultCredentialsSecretName, + "csi.storage.k8s.io/controller-expand-secret-namespace": defaultStorageHelmReleaseNamespace, +} + type NutanixCSIConfig struct { *options.GlobalOptions defaultValuesTemplateConfigMapName string @@ -80,42 +95,38 @@ func (n *NutanixCSI) Apply( default: return fmt.Errorf("stategy %s not implemented", strategy) } + if provider.Credentials != nil { - sec := &corev1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: provider.Credentials.Name, - Namespace: req.Cluster.Namespace, - }, + key := ctrlclient.ObjectKey{ + Name: defaultCredentialsSecretName, + Namespace: defaultStorageHelmReleaseNamespace, } - err := n.client.Get( + err := lifecycleutils.CopySecretToRemoteCluster( ctx, - ctrlclient.ObjectKeyFromObject(sec), - sec, - ) - if err != nil { - return err - } - err = lifecycleutils.EnsureCRSForClusterFromObjects( - ctx, - fmt.Sprintf("nutanix-csi-credentials-crs-%s", req.Cluster.Name), n.client, + provider.Credentials.Name, + key, &req.Cluster, - sec, ) if err != nil { - return err + return fmt.Errorf( + "error creating credentials Secret for the Nutanix CSI driver: %w", + err, + ) } } - return n.createStorageClasses( + + err := n.createStorageClasses( ctx, provider.StorageClassConfig, &req.Cluster, defaultStorageConfig, ) + if err != nil { + return fmt.Errorf("error creating StorageClasses for the Nutanix CSI driver: %w", err) + } + + return nil } func (n *NutanixCSI) handleHelmAddonApply( @@ -149,8 +160,8 @@ func (n *NutanixCSI) handleHelmAddonApply( ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{clusterv1.ClusterNameLabel: req.Cluster.Name}, }, - ReleaseNamespace: req.Cluster.Namespace, - ReleaseName: fmt.Sprintf(defaultStorageHelmReleaseNameTemplate, req.Cluster.Name), + ReleaseNamespace: defaultStorageHelmReleaseNamespace, + ReleaseName: defaultStorageHelmReleaseName, Version: defaultStorageHelmChartVersion, ValuesTemplate: values, }, @@ -174,7 +185,7 @@ func (n *NutanixCSI) handleHelmAddonApply( }, ObjectMeta: metav1.ObjectMeta{ Namespace: req.Cluster.Namespace, - Name: "nutanix-csi-snapshot" + req.Cluster.Name, + Name: "nutanix-csi-snapshot-" + req.Cluster.Name, }, Spec: caaphv1.HelmChartProxySpec{ RepoURL: defaultHelmRepositoryURL, @@ -182,8 +193,8 @@ func (n *NutanixCSI) handleHelmAddonApply( ClusterSelector: metav1.LabelSelector{ MatchLabels: map[string]string{clusterv1.ClusterNameLabel: req.Cluster.Name}, }, - ReleaseNamespace: req.Cluster.Namespace, - ReleaseName: fmt.Sprintf(defaultSnapshotHelmReleaseNameTemplate, req.Cluster.Name), + ReleaseNamespace: defaultSnapshotHelmReleaseNamespace, + ReleaseName: defaultSnapshotHelmReleaseName, Version: defaultSnapshotHelmChartVersion, }, } @@ -205,20 +216,21 @@ func (n *NutanixCSI) handleHelmAddonApply( return nil } -func (n *NutanixCSI) createStorageClasses(ctx context.Context, +func (n *NutanixCSI) createStorageClasses( + ctx context.Context, configs []v1alpha1.StorageClassConfig, cluster *clusterv1.Cluster, defaultStorageConfig *v1alpha1.DefaultStorage, ) error { allStorageClasses := make([]runtime.Object, 0, len(configs)) - for _, c := range configs { - setAsDefault := c.Name == defaultStorageConfig.StorageClassConfigName && + for _, config := range configs { + setAsDefault := config.Name == defaultStorageConfig.StorageClassConfigName && v1alpha1.CSIProviderNutanix == defaultStorageConfig.ProviderName allStorageClasses = append(allStorageClasses, lifecycleutils.CreateStorageClass( - c, - n.config.GlobalOptions.DefaultsNamespace(), + config, v1alpha1.NutanixProvisioner, setAsDefault, + defaultStorageClassParameters, )) } cm, err := lifecycleutils.CreateConfigMapForCRS( diff --git a/pkg/handlers/generic/lifecycle/utils/scs.go b/pkg/handlers/generic/lifecycle/utils/scs.go new file mode 100644 index 000000000..6bcb66bd3 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/scs.go @@ -0,0 +1,52 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" +) + +const ( + kindStorageClass = "StorageClass" +) + +func CreateStorageClass( + storageConfig v1alpha1.StorageClassConfig, + provisionerName v1alpha1.StorageProvisioner, + isDefault bool, + defaultParameters map[string]string, +) *storagev1.StorageClass { + parameters := make(map[string]string) + // set the defaults first so that user provided parameters can override them + for k, v := range defaultParameters { + parameters[k] = v + } + // set user provided parameters, overriding any defaults with the same key + for k, v := range storageConfig.Parameters { + parameters[k] = v + } + + sc := storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: storageConfig.Name, + }, + Provisioner: string(provisionerName), + Parameters: parameters, + VolumeBindingMode: ptr.To(storageConfig.VolumeBindingMode), + ReclaimPolicy: ptr.To(storageConfig.ReclaimPolicy), + AllowVolumeExpansion: ptr.To(storageConfig.AllowExpansion), + } + if isDefault { + sc.ObjectMeta.Annotations = defaultStorageClassMap + } + return &sc +} diff --git a/pkg/handlers/generic/lifecycle/utils/scs_test.go b/pkg/handlers/generic/lifecycle/utils/scs_test.go new file mode 100644 index 000000000..a75a5fdd8 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/scs_test.go @@ -0,0 +1,144 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" +) + +var ( + defaultParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "ext4", + "type": "gp3", + } + userProviderParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "xfs", + "flashMode": "ENABLED", + "storageContainer": "storage-container-name", + "chapAuth": "ENABLED", + "storageType": "NutanixVolumes", + "whitelistIPMode": "ENABLED", + "whitelistIPAddr": "1.1.1.1", + } + + combinedParameters = map[string]string{ + "csi.storage.k8s.io/fstype": "xfs", + "type": "gp3", + "flashMode": "ENABLED", + "storageContainer": "storage-container-name", + "chapAuth": "ENABLED", + "storageType": "NutanixVolumes", + "whitelistIPMode": "ENABLED", + "whitelistIPAddr": "1.1.1.1", + } +) + +func TestCreateStorageClass(t *testing.T) { + tests := []struct { + name string + storageConfig v1alpha1.StorageClassConfig + provisioner v1alpha1.StorageProvisioner + setAsDefault bool + defaultParameters map[string]string + expectedStorageClass *storagev1.StorageClass + }{ + { + name: "with only default parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "aws-ebs", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: nil, + AllowExpansion: true, + }, + provisioner: v1alpha1.AWSEBSProvisioner, + defaultParameters: defaultParameters, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "aws-ebs", + }, + Parameters: defaultParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.AWSEBSProvisioner), + AllowVolumeExpansion: ptr.To(true), + }, + }, + { + name: "with only user provided parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "nutanix-volumes", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: userProviderParameters, + }, + provisioner: v1alpha1.NutanixProvisioner, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "nutanix-volumes", + }, + Parameters: userProviderParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.NutanixProvisioner), + AllowVolumeExpansion: ptr.To(false), + }, + }, + { + name: "with both default and user provided parameters", + storageConfig: v1alpha1.StorageClassConfig{ + Name: "aws-ebs", + ReclaimPolicy: v1alpha1.VolumeReclaimDelete, + VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, + Parameters: userProviderParameters, + AllowExpansion: true, + }, + provisioner: v1alpha1.AWSEBSProvisioner, + defaultParameters: defaultParameters, + expectedStorageClass: &storagev1.StorageClass{ + TypeMeta: metav1.TypeMeta{ + Kind: kindStorageClass, + APIVersion: storagev1.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "aws-ebs", + }, + Parameters: combinedParameters, + ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), + VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), + Provisioner: string(v1alpha1.AWSEBSProvisioner), + AllowVolumeExpansion: ptr.To(true), + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sc := CreateStorageClass( + tt.storageConfig, + tt.provisioner, + tt.setAsDefault, + tt.defaultParameters, + ) + if diff := cmp.Diff(sc, tt.expectedStorageClass); diff != "" { + t.Errorf("CreateStorageClass() mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/pkg/handlers/generic/lifecycle/utils/secrets.go b/pkg/handlers/generic/lifecycle/utils/secrets.go new file mode 100644 index 000000000..7c3660913 --- /dev/null +++ b/pkg/handlers/generic/lifecycle/utils/secrets.go @@ -0,0 +1,73 @@ +// Copyright 2023 D2iQ, Inc. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +package utils + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" + "sigs.k8s.io/cluster-api/controllers/remote" + ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" +) + +// CopySecretToRemoteCluster will get the Secret from srcSecretName +// and create it on the remote cluster, copying Data and StringData to dstSecretKey Secret. +func CopySecretToRemoteCluster( + ctx context.Context, + cl ctrlclient.Client, + srcSecretName string, + dstSecretKey ctrlclient.ObjectKey, + cluster *clusterv1.Cluster, +) error { + sourceSecret := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: srcSecretName, + Namespace: cluster.Namespace, + }, + } + err := cl.Get(ctx, ctrlclient.ObjectKeyFromObject(sourceSecret), sourceSecret) + if err != nil { + return err + } + + credentialsOnRemote := &corev1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: dstSecretKey.Name, + Namespace: dstSecretKey.Namespace, + }, + Data: sourceSecret.Data, + StringData: sourceSecret.StringData, + } + + clusterKey := ctrlclient.ObjectKeyFromObject(cluster) + remoteClient, err := remote.NewClusterClient(ctx, "", cl, clusterKey) + if err != nil { + return fmt.Errorf("error creating client for remote cluster: %w", err) + } + + err = EnsureNamespace(ctx, remoteClient, dstSecretKey.Namespace) + if err != nil { + return fmt.Errorf("error creating namespace on the remote cluster: %w", err) + } + + err = client.ServerSideApply(ctx, remoteClient, credentialsOnRemote) + if err != nil { + return fmt.Errorf("error creating Secret on the remote cluster: %w", err) + } + + return nil +} diff --git a/pkg/handlers/generic/lifecycle/utils/utils.go b/pkg/handlers/generic/lifecycle/utils/utils.go index aa6d42b30..8a78b4d15 100644 --- a/pkg/handlers/generic/lifecycle/utils/utils.go +++ b/pkg/handlers/generic/lifecycle/utils/utils.go @@ -6,26 +6,21 @@ package utils import ( "context" "fmt" - "maps" corev1 "k8s.io/api/core/v1" - storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/ptr" clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" crsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1" utilyaml "sigs.k8s.io/cluster-api/util/yaml" ctrlclient "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/common/pkg/k8s/client" ) const ( - kindStorageClass = "StorageClass" defaultCRSConfigMapKey = "custom-resources.yaml" ) @@ -34,10 +29,6 @@ var ( defaultStorageClassMap = map[string]string{ defaultStorageClassKey: "true", } - defaultAWSStorageClassParams = map[string]string{ - "csi.storage.k8s.io/fstype": "ext4", - "type": "gp3", - } ) func EnsureCRSForClusterFromObjects( @@ -153,40 +144,6 @@ func RetrieveValuesTemplateConfigMap( return configMap, nil } -func CreateStorageClass( - storageConfig v1alpha1.StorageClassConfig, - defaultsNamespace string, - provisionerName v1alpha1.StorageProvisioner, - isDefault bool, -) *storagev1.StorageClass { - var params map[string]string - if provisionerName == v1alpha1.AWSEBSProvisioner { - params = defaultAWSStorageClassParams - } - if storageConfig.Parameters != nil { - params = maps.Clone(storageConfig.Parameters) - } - sc := storagev1.StorageClass{ - TypeMeta: metav1.TypeMeta{ - Kind: kindStorageClass, - APIVersion: storagev1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: storageConfig.Name, - Namespace: defaultsNamespace, - }, - Provisioner: string(provisionerName), - Parameters: params, - VolumeBindingMode: ptr.To(storageConfig.VolumeBindingMode), - ReclaimPolicy: ptr.To(storageConfig.ReclaimPolicy), - AllowVolumeExpansion: ptr.To(storageConfig.AllowExpansion), - } - if isDefault { - sc.ObjectMeta.Annotations = defaultStorageClassMap - } - return &sc -} - func CreateConfigMapForCRS(configMapName, configMapNamespace string, objs ...runtime.Object, ) (*corev1.ConfigMap, error) { diff --git a/pkg/handlers/generic/lifecycle/utils/utils_test.go b/pkg/handlers/generic/lifecycle/utils/utils_test.go index 420ebe562..26ec622e6 100644 --- a/pkg/handlers/generic/lifecycle/utils/utils_test.go +++ b/pkg/handlers/generic/lifecycle/utils/utils_test.go @@ -6,134 +6,12 @@ package utils import ( "testing" - "github.com/google/go-cmp/cmp" corev1 "k8s.io/api/core/v1" storagev1 "k8s.io/api/storage/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/utils/ptr" - - "github.com/d2iq-labs/cluster-api-runtime-extensions-nutanix/api/v1alpha1" ) -func TestCreateStorageClass(t *testing.T) { - tests := []struct { - name string - defaultsNamespace string - storageConfig v1alpha1.StorageClassConfig - expectedStorageClass *storagev1.StorageClass - provisioner v1alpha1.StorageProvisioner - isDefault bool - }{ - { - name: "defaulting with AWS", - storageConfig: v1alpha1.StorageClassConfig{ - Name: "aws-ebs", - ReclaimPolicy: v1alpha1.VolumeReclaimDelete, - VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, - Parameters: nil, - AllowExpansion: true, - }, - expectedStorageClass: &storagev1.StorageClass{ - TypeMeta: metav1.TypeMeta{ - Kind: kindStorageClass, - APIVersion: storagev1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "aws-ebs", - Namespace: "default", - }, - Parameters: defaultAWSStorageClassParams, - ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), - VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), - Provisioner: string(v1alpha1.AWSEBSProvisioner), - AllowVolumeExpansion: ptr.To(true), - }, - provisioner: v1alpha1.AWSEBSProvisioner, - defaultsNamespace: "default", - }, - { - name: "nutanix for nutanix files", - storageConfig: v1alpha1.StorageClassConfig{ - Name: "nutanix-volumes", - ReclaimPolicy: v1alpha1.VolumeReclaimDelete, - VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, - Parameters: map[string]string{ - "csi.storage.k8s.io/fstype": "ext4", - "flashMode": "ENABLED", - "storageContainer": "storage-container-name", - "chapAuth": "ENABLED", - "storageType": "NutanixVolumes", - "whitelistIPMode": "ENABLED", - "whitelistIPAddr": "1.1.1.1", - }, - }, - expectedStorageClass: &storagev1.StorageClass{ - TypeMeta: metav1.TypeMeta{ - Kind: kindStorageClass, - APIVersion: storagev1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nutanix-volumes", - Namespace: "default", - }, - Parameters: map[string]string{ - "csi.storage.k8s.io/fstype": "ext4", - "flashMode": "ENABLED", - "storageContainer": "storage-container-name", - "chapAuth": "ENABLED", - "storageType": "NutanixVolumes", - "whitelistIPMode": "ENABLED", - "whitelistIPAddr": "1.1.1.1", - }, - ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), - VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), - Provisioner: string(v1alpha1.NutanixProvisioner), - AllowVolumeExpansion: ptr.To(false), - }, - provisioner: v1alpha1.NutanixProvisioner, - defaultsNamespace: "default", - }, - { - name: "nutanix defaults", - storageConfig: v1alpha1.StorageClassConfig{ - Name: "nutanix-volumes", - ReclaimPolicy: v1alpha1.VolumeReclaimDelete, - VolumeBindingMode: v1alpha1.VolumeBindingWaitForFirstConsumer, - }, - expectedStorageClass: &storagev1.StorageClass{ - TypeMeta: metav1.TypeMeta{ - Kind: kindStorageClass, - APIVersion: storagev1.SchemeGroupVersion.String(), - }, - ObjectMeta: metav1.ObjectMeta{ - Name: "nutanix-volumes", - Namespace: "default", - }, - ReclaimPolicy: ptr.To(corev1.PersistentVolumeReclaimDelete), - VolumeBindingMode: ptr.To(storagev1.VolumeBindingWaitForFirstConsumer), - Provisioner: string(v1alpha1.NutanixProvisioner), - AllowVolumeExpansion: ptr.To(false), - }, - provisioner: v1alpha1.NutanixProvisioner, - defaultsNamespace: "default", - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - sc := CreateStorageClass( - tt.storageConfig, - tt.defaultsNamespace, - tt.provisioner, - false, - ) - if diff := cmp.Diff(sc, tt.expectedStorageClass); diff != "" { - t.Errorf("CreateStorageClass() mismatch (-want +got):\n%s", diff) - } - }) - } -} - func TestCreateConfigMapForCRS(t *testing.T) { tests := []struct { name string