From eb8494878a123c86e2f75f89a10722289a118741 Mon Sep 17 00:00:00 2001 From: "Jose A. Rivera" Date: Fri, 23 Feb 2024 09:21:30 -0600 Subject: [PATCH 1/4] provider: slight refactor to GetStorageClassClaimConfig() Since a StorageClass resource *must* always be returned as one of the external resources in the StorageClassClaimConfig, we might as well start defining it outside the loop over the StorageClassRequest's CephResources. This enables the upcoming enhancement to add RADOS namespace information. Signed-off-by: Jose A. Rivera --- services/provider/server/server.go | 62 ++++++++++++++++-------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/services/provider/server/server.go b/services/provider/server/server.go index 4c9b4ebbd8..b3137895de 100644 --- a/services/provider/server/server.go +++ b/services/provider/server/server.go @@ -549,6 +549,10 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req } } var extR []*pb.ExternalResource + + var storageClassName string + storageClassData := map[string]string{} + for _, cephRes := range storageClassRequest.Status.CephResources { switch cephRes.Kind { case "CephClient": @@ -588,24 +592,20 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req if err != nil { return nil, status.Error(codes.Internal, err.Error()) } - rbdStorageClass := map[string]string{ - "clusterID": s.namespace, - "pool": cephRes.Name, - "imageFeatures": "layering,deep-flatten,exclusive-lock,object-map,fast-diff", - "csi.storage.k8s.io/fstype": "ext4", - "imageFormat": "2", - "csi.storage.k8s.io/provisioner-secret-name": provisionerCephClientSecret, - "csi.storage.k8s.io/node-stage-secret-name": nodeCephClientSecret, - "csi.storage.k8s.io/controller-expand-secret-name": provisionerCephClientSecret, - } + + storageClassName = "ceph-rbd" + storageClassData["clusterID"] = s.namespace + storageClassData["pool"] = cephRes.Name + storageClassData["imageFeatures"] = "layering,deep-flatten,exclusive-lock,object-map,fast-diff" + storageClassData["csi.storage.k8s.io/fstype"] = "ext4" + storageClassData["imageFormat"] = "2" + storageClassData["csi.storage.k8s.io/provisioner-secret-name"] = provisionerCephClientSecret + storageClassData["csi.storage.k8s.io/node-stage-secret-name"] = nodeCephClientSecret + storageClassData["csi.storage.k8s.io/controller-expand-secret-name"] = provisionerCephClientSecret if storageClassRequest.Spec.EncryptionMethod != "" { - rbdStorageClass["encrypted"] = "true" - rbdStorageClass["encryptionKMSID"] = storageClassRequest.Spec.EncryptionMethod + storageClassData["encrypted"] = "true" + storageClassData["encryptionKMSID"] = storageClassRequest.Spec.EncryptionMethod } - extR = append(extR, &pb.ExternalResource{ - Name: "ceph-rbd", - Kind: "StorageClass", - Data: mustMarshal(rbdStorageClass)}) extR = append(extR, &pb.ExternalResource{ Name: "ceph-rbd", @@ -632,18 +632,14 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req return nil, status.Error(codes.Internal, err.Error()) } - extR = append(extR, &pb.ExternalResource{ - Name: "cephfs", - Kind: "StorageClass", - Data: mustMarshal(map[string]string{ - "clusterID": getSubVolumeGroupClusterID(subVolumeGroup), - "subvolumegroupname": subVolumeGroup.Name, - "fsName": subVolumeGroup.Spec.FilesystemName, - "csi.storage.k8s.io/provisioner-secret-name": provisionerCephClientSecret, - "csi.storage.k8s.io/node-stage-secret-name": nodeCephClientSecret, - "csi.storage.k8s.io/controller-expand-secret-name": provisionerCephClientSecret, - "pool": subVolumeGroup.GetLabels()[v1alpha1.CephFileSystemDataPoolLabel], - })}) + storageClassName = "cephfs" + storageClassData["clusterID"] = getSubVolumeGroupClusterID(subVolumeGroup) + storageClassData["subvolumegroupname"] = subVolumeGroup.Name + storageClassData["fsName"] = subVolumeGroup.Spec.FilesystemName + storageClassData["pool"] = subVolumeGroup.GetLabels()[v1alpha1.CephFileSystemDataPoolLabel] + storageClassData["csi.storage.k8s.io/provisioner-secret-name"] = provisionerCephClientSecret + storageClassData["csi.storage.k8s.io/node-stage-secret-name"] = nodeCephClientSecret + storageClassData["csi.storage.k8s.io/controller-expand-secret-name"] = provisionerCephClientSecret extR = append(extR, &pb.ExternalResource{ Name: cephRes.Name, @@ -661,6 +657,16 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req })}) } } + + if storageClassName == "" { + return nil, status.Error(codes.Internal, "No StorageClass was defined") + } + extR = append(extR, &pb.ExternalResource{ + Name: storageClassName, + Kind: "StorageClass", + Data: mustMarshal(storageClassData), + }) + klog.Infof("successfully returned the storage class claim %q for %q", req.StorageClassClaimName, req.StorageConsumerUUID) return &pb.StorageClassClaimConfigResponse{ExternalResource: extR}, nil From 220c8bff36f0488c1429820a554a533a6b61bc2b Mon Sep 17 00:00:00 2001 From: "Jose A. Rivera" Date: Fri, 23 Feb 2024 09:25:46 -0600 Subject: [PATCH 2/4] provider: check for RADOS namespace in GetStorageClassClaimConfig() Signed-off-by: Jose A. Rivera --- services/provider/server/server.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/services/provider/server/server.go b/services/provider/server/server.go index b3137895de..dbb0e9cac0 100644 --- a/services/provider/server/server.go +++ b/services/provider/server/server.go @@ -583,6 +583,24 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req }) case "CephBlockPool": + storageClassName = "ceph-rbd" + storageClassData["clusterID"] = s.namespace + + // Check if the StorageClassRequest has an associated RADOS Namespace + for _, rnsRes := range storageClassRequest.Status.CephResources { + if rnsRes.Kind == "CephBlockPoolRadosNamespace" { + rns := &rookCephv1.CephBlockPoolRadosNamespace{} + err = s.client.Get(ctx, types.NamespacedName{Name: rnsRes.Name, Namespace: s.namespace}, rns) + if err != nil { + return nil, status.Errorf(codes.Internal, "failed to get %s CephBlockPoolRadosNamespace. %v", rnsRes.Name, err) + } + storageClassData["clusterID"] = rns.Status.Info["clusterID"] + storageClassData["radosnamespace"] = rns.Name + cephRes.CephClients = rnsRes.CephClients + break + } + } + nodeCephClientSecret, _, err := s.getCephClientInformation(ctx, cephRes.CephClients["node"]) if err != nil { return nil, status.Error(codes.Internal, err.Error()) @@ -593,8 +611,6 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req return nil, status.Error(codes.Internal, err.Error()) } - storageClassName = "ceph-rbd" - storageClassData["clusterID"] = s.namespace storageClassData["pool"] = cephRes.Name storageClassData["imageFeatures"] = "layering,deep-flatten,exclusive-lock,object-map,fast-diff" storageClassData["csi.storage.k8s.io/fstype"] = "ext4" @@ -611,7 +627,7 @@ func (s *OCSProviderServer) GetStorageClassClaimConfig(ctx context.Context, req Name: "ceph-rbd", Kind: "VolumeSnapshotClass", Data: mustMarshal(map[string]string{ - "clusterID": s.namespace, + "clusterID": storageClassData["clusterID"], "csi.storage.k8s.io/snapshotter-secret-name": provisionerCephClientSecret, })}) From b522666ff35ea03e6292ff225500b6125021795c Mon Sep 17 00:00:00 2001 From: "Jose A. Rivera" Date: Mon, 26 Feb 2024 10:03:42 -0600 Subject: [PATCH 3/4] controllers: define OwnersIndexFieldFunc() Signed-off-by: Jose A. Rivera --- .../storageclassrequest_controller.go | 18 ++--- .../storageclassrequest_controller_test.go | 69 +++++-------------- controllers/util/k8sutil.go | 12 ++++ 3 files changed, 36 insertions(+), 63 deletions(-) diff --git a/controllers/storageclassrequest/storageclassrequest_controller.go b/controllers/storageclassrequest/storageclassrequest_controller.go index c3e3ecfcf0..0f12ad14f2 100644 --- a/controllers/storageclassrequest/storageclassrequest_controller.go +++ b/controllers/storageclassrequest/storageclassrequest_controller.go @@ -26,6 +26,7 @@ import ( v1 "github.com/red-hat-storage/ocs-operator/api/v4/v1" "github.com/red-hat-storage/ocs-operator/api/v4/v1alpha1" controllers "github.com/red-hat-storage/ocs-operator/v4/controllers/storageconsumer" + "github.com/red-hat-storage/ocs-operator/v4/controllers/util" rookCephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" storagev1 "k8s.io/api/storage/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -45,8 +46,6 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" ) -const ownerUIDIndexName = "ownerUID" - // StorageClassRequestReconciler reconciles a StorageClassRequest object // nolint:revive type StorageClassRequestReconciler struct { @@ -143,17 +142,10 @@ func (r *StorageClassRequestReconciler) SetupWithManager(mgr ctrl.Manager) error if err := mgr.GetCache().IndexField( context.TODO(), &rookCephv1.CephFilesystemSubVolumeGroup{}, - ownerUIDIndexName, - func(obj client.Object) []string { - refs := obj.GetOwnerReferences() - owners := []string{} - for i := range refs { - owners = append(owners, string(refs[i].UID)) - } - return owners - }, + util.OwnerUIDIndexName, + util.OwnersIndexFieldFunc, ); err != nil { - return fmt.Errorf("unable to set up FieldIndexer for owner reference UIDs: %v", err) + return fmt.Errorf("unable to set up FieldIndexer on CephFilesystemSubVolumeGroups for owner reference UIDs: %v", err) } enqueueStorageConsumerRequest := handler.EnqueueRequestsFromMapFunc( @@ -266,7 +258,7 @@ func (r *StorageClassRequestReconciler) initPhase(storageProfile *v1.StorageProf r.ctx, cephFilesystemSubVolumeGroupList, client.InNamespace(r.OperatorNamespace), - client.MatchingFields{ownerUIDIndexName: string(r.StorageClassRequest.UID)}) + client.MatchingFields{util.OwnerUIDIndexName: string(r.StorageClassRequest.UID)}) if err != nil { return err } diff --git a/controllers/storageclassrequest/storageclassrequest_controller_test.go b/controllers/storageclassrequest/storageclassrequest_controller_test.go index 503ba3a9ed..42d1c3f139 100644 --- a/controllers/storageclassrequest/storageclassrequest_controller_test.go +++ b/controllers/storageclassrequest/storageclassrequest_controller_test.go @@ -22,12 +22,12 @@ import ( v1 "github.com/red-hat-storage/ocs-operator/api/v4/v1" "github.com/red-hat-storage/ocs-operator/api/v4/v1alpha1" controllers "github.com/red-hat-storage/ocs-operator/v4/controllers/storageconsumer" + "github.com/red-hat-storage/ocs-operator/v4/controllers/util" rookCephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/cache/informertest" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" @@ -176,6 +176,12 @@ func createFakeReconciler(t *testing.T) StorageClassRequestReconciler { return fakeReconciler } +func newFakeClientBuilder(scheme *runtime.Scheme) *fake.ClientBuilder { + return fake.NewClientBuilder(). + WithScheme(scheme). + WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, util.OwnerUIDIndexName, util.OwnersIndexFieldFunc) +} + func TestProfileReconcile(t *testing.T) { var err error var caseCounter int @@ -246,18 +252,9 @@ func TestProfileReconcile(t *testing.T) { c.createObjects = append(c.createObjects, fakeStorageProfile) r.Cache = &informertest.FakeInformers{Scheme: r.Scheme} - fakeClient := fake.NewClientBuilder(). - WithScheme(r.Scheme). + fakeClient := newFakeClientBuilder(r.Scheme). WithRuntimeObjects(c.createObjects...). - WithStatusSubresource(fakeStorageClassRequest). - WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, ownerUIDIndexName, func(obj client.Object) []string { - refs := obj.GetOwnerReferences() - owners := []string{} - for i := range refs { - owners = append(owners, string(refs[i].UID)) - } - return owners - }) + WithStatusSubresource(fakeStorageClassRequest) r.Client = fakeClient.Build() req := reconcile.Request{} @@ -363,7 +360,7 @@ func TestStorageProfileCephBlockPool(t *testing.T) { c.createObjects = append(c.createObjects, c.storageProfile) c.createObjects = append(c.createObjects, fakeStorageConsumer) - fakeClient := fake.NewClientBuilder().WithScheme(r.Scheme).WithRuntimeObjects(c.createObjects...) + fakeClient := newFakeClientBuilder(r.Scheme).WithRuntimeObjects(c.createObjects...) r.Client = fakeClient.Build() _, err = r.reconcilePhases() @@ -469,18 +466,8 @@ func TestStorageProfileCephFsSubVolGroup(t *testing.T) { c.createObjects = append(c.createObjects, c.cephFs) c.createObjects = append(c.createObjects, c.storageProfile) c.createObjects = append(c.createObjects, fakeStorageConsumer) - fakeClient := fake.NewClientBuilder(). - WithScheme(r.Scheme). - WithRuntimeObjects(c.createObjects...). - WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, ownerUIDIndexName, func(obj client.Object) []string { - refs := obj.GetOwnerReferences() - owners := []string{} - for i := range refs { - owners = append(owners, string(refs[i].UID)) - } - return owners - }) - + fakeClient := newFakeClientBuilder(r.Scheme). + WithRuntimeObjects(c.createObjects...) r.Client = fakeClient.Build() _, err = r.reconcilePhases() @@ -620,7 +607,7 @@ func TestCephBlockPool(t *testing.T) { c.createObjects = append(c.createObjects, fakeStorageProfile) c.createObjects = append(c.createObjects, fakeStorageConsumer) - fakeClient := fake.NewClientBuilder().WithScheme(r.Scheme).WithRuntimeObjects(c.createObjects...) + fakeClient := newFakeClientBuilder(r.Scheme).WithRuntimeObjects(c.createObjects...) r.Client = fakeClient.Build() _, err = r.reconcilePhases() @@ -653,7 +640,7 @@ func TestCephBlockPool(t *testing.T) { r := createFakeReconciler(t) r.StorageClassRequest.Spec.Type = "blockpool" r.StorageClassRequest.Spec.StorageProfile = badStorageProfile.Name - fakeClient := fake.NewClientBuilder().WithScheme(r.Scheme) + fakeClient := newFakeClientBuilder(r.Scheme) r.Client = fakeClient.WithRuntimeObjects(badStorageProfile, fakeStorageConsumer).Build() _, err = r.reconcilePhases() @@ -711,17 +698,8 @@ func TestCephFsSubVolGroup(t *testing.T) { c.createObjects = append(c.createObjects, fakeCephFs) c.createObjects = append(c.createObjects, fakeStorageProfile) c.createObjects = append(c.createObjects, fakeStorageConsumer) - fakeClient := fake.NewClientBuilder(). - WithScheme(r.Scheme). - WithRuntimeObjects(c.createObjects...). - WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, ownerUIDIndexName, func(obj client.Object) []string { - refs := obj.GetOwnerReferences() - owners := []string{} - for i := range refs { - owners = append(owners, string(refs[i].UID)) - } - return owners - }) + fakeClient := newFakeClientBuilder(r.Scheme). + WithRuntimeObjects(c.createObjects...) r.Client = fakeClient.Build() _, err = r.reconcilePhases() @@ -747,17 +725,8 @@ func TestCephFsSubVolGroup(t *testing.T) { r := createFakeReconciler(t) r.StorageClassRequest.Spec.Type = "sharedfilesystem" r.StorageClassRequest.Spec.StorageProfile = fakeStorageProfile.Name - fakeClient := fake.NewClientBuilder(). - WithScheme(r.Scheme). - WithRuntimeObjects(fakeStorageProfile, fakeStorageConsumer). - WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, ownerUIDIndexName, func(obj client.Object) []string { - refs := obj.GetOwnerReferences() - owners := []string{} - for i := range refs { - owners = append(owners, string(refs[i].UID)) - } - return owners - }) + fakeClient := newFakeClientBuilder(r.Scheme). + WithRuntimeObjects(fakeStorageProfile, fakeStorageConsumer) r.Client = fakeClient.Build() _, err = r.reconcilePhases() @@ -773,7 +742,7 @@ func TestCephFsSubVolGroup(t *testing.T) { r = createFakeReconciler(t) r.StorageClassRequest.Spec.Type = "sharedfilesystem" r.StorageClassRequest.Spec.StorageProfile = badStorageProfile.Name - fakeClient = fake.NewClientBuilder().WithScheme(r.Scheme) + fakeClient = newFakeClientBuilder(r.Scheme) r.Client = fakeClient.WithRuntimeObjects(badStorageProfile, fakeStorageConsumer, fakeCephFs).Build() _, err = r.reconcilePhases() diff --git a/controllers/util/k8sutil.go b/controllers/util/k8sutil.go index 682dcbb590..4da988358a 100644 --- a/controllers/util/k8sutil.go +++ b/controllers/util/k8sutil.go @@ -36,6 +36,9 @@ const ( TopologyDomainLabelsKey = "CSI_TOPOLOGY_DOMAIN_LABELS" EnableNFSKey = "ROOK_CSI_ENABLE_NFS" CsiRemoveHolderPodsKey = "CSI_REMOVE_HOLDER_PODS" + + // This is the name for the OwnerUID FieldIndex + OwnerUIDIndexName = "ownerUID" ) // GetWatchNamespace returns the namespace the operator should be watching for changes @@ -117,3 +120,12 @@ func GetCountOfRunningPods(podList *corev1.PodList) int { } return count } + +func OwnersIndexFieldFunc(obj client.Object) []string { + refs := obj.GetOwnerReferences() + owners := []string{} + for i := range refs { + owners = append(owners, string(refs[i].UID)) + } + return owners +} From f8c4eedc4254414dd255b794057241304fc2dcd3 Mon Sep 17 00:00:00 2001 From: "Jose A. Rivera" Date: Fri, 23 Feb 2024 09:27:04 -0600 Subject: [PATCH 4/4] storageclassrequest: fulfill requests with RADOS namespaces This set of changes achieves two things: * Reconciles a given CephBlockPool to serve a single storage profile, regardless of consumer. As part of this, also changes the CephBlockPool name generation to remove the UUID portion. * Reconciles a CephBlockPoolRadosNamespace to provide isolation of data for consumers on shared block pools. Signed-off-by: Jose A. Rivera --- .../storageclassrequest_controller.go | 169 ++++----- .../storageclassrequest_controller_test.go | 332 +++++++----------- .../storageconsumer_controller.go | 1 + 3 files changed, 201 insertions(+), 301 deletions(-) diff --git a/controllers/storageclassrequest/storageclassrequest_controller.go b/controllers/storageclassrequest/storageclassrequest_controller.go index 0f12ad14f2..43934b439f 100644 --- a/controllers/storageclassrequest/storageclassrequest_controller.go +++ b/controllers/storageclassrequest/storageclassrequest_controller.go @@ -21,7 +21,6 @@ import ( "strings" "github.com/go-logr/logr" - "github.com/google/uuid" snapapi "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1" v1 "github.com/red-hat-storage/ocs-operator/api/v4/v1" "github.com/red-hat-storage/ocs-operator/api/v4/v1alpha1" @@ -59,7 +58,7 @@ type StorageClassRequestReconciler struct { storageConsumer *v1alpha1.StorageConsumer storageCluster *v1.StorageCluster StorageClassRequest *v1alpha1.StorageClassRequest - cephBlockPool *rookCephv1.CephBlockPool + cephRadosNamespace *rookCephv1.CephBlockPoolRadosNamespace cephFilesystemSubVolumeGroup *rookCephv1.CephFilesystemSubVolumeGroup cephClientProvisioner *rookCephv1.CephClient cephClientNode *rookCephv1.CephClient @@ -139,6 +138,15 @@ func (r *StorageClassRequestReconciler) Reconcile(ctx context.Context, request r func (r *StorageClassRequestReconciler) SetupWithManager(mgr ctrl.Manager) error { + if err := mgr.GetCache().IndexField( + context.TODO(), + &rookCephv1.CephBlockPoolRadosNamespace{}, + util.OwnerUIDIndexName, + util.OwnersIndexFieldFunc, + ); err != nil { + return fmt.Errorf("unable to set up FieldIndexer on CephBlockPoolRadosNamespaces for owner reference UIDs: %v", err) + } + if err := mgr.GetCache().IndexField( context.TODO(), &rookCephv1.CephFilesystemSubVolumeGroup{}, @@ -163,11 +171,12 @@ func (r *StorageClassRequestReconciler) SetupWithManager(mgr ctrl.Manager) error return []reconcile.Request{} }) enqueueForOwner := handler.EnqueueRequestForOwner(mgr.GetScheme(), mgr.GetRESTMapper(), &v1alpha1.StorageClassRequest{}) + return ctrl.NewControllerManagedBy(mgr). For(&v1alpha1.StorageClassRequest{}, builder.WithPredicates( predicate.GenerationChangedPredicate{}, )). - Watches(&rookCephv1.CephBlockPool{}, enqueueForOwner). + Owns(&rookCephv1.CephBlockPoolRadosNamespace{}). Watches(&rookCephv1.CephFilesystemSubVolumeGroup{}, enqueueForOwner). Watches(&rookCephv1.CephClient{}, enqueueForOwner). Watches(&storagev1.StorageClass{}, enqueueStorageConsumerRequest). @@ -199,63 +208,56 @@ func (r *StorageClassRequestReconciler) initPhase(storageProfile *v1.StorageProf } profileName := r.StorageClassRequest.Spec.StorageProfile - if profileName == "" { - profileName = r.storageCluster.Spec.DefaultStorageProfile - } - - // Fetch StorageProfile by name in the StorageCluster's namespace - storageProfile.Name = profileName - storageProfile.Namespace = r.storageCluster.Namespace - - if err := r.get(storageProfile); err != nil { - return fmt.Errorf("no storage profile CR found for storage profile %s", profileName) - } // check request status already contains the name of the resource. if not, add it. if r.StorageClassRequest.Spec.Type == "blockpool" { - r.cephBlockPool = &rookCephv1.CephBlockPool{} - r.cephBlockPool.Namespace = r.OperatorNamespace - for _, res := range r.StorageClassRequest.Status.CephResources { - if res.Kind == "CephBlockPool" { - r.cephBlockPool.Name = res.Name - break - } + // initialize in-memory structs + r.cephRadosNamespace = &rookCephv1.CephBlockPoolRadosNamespace{} + r.cephRadosNamespace.Namespace = r.OperatorNamespace + + // check if a CephBlockPoolRadosNamespace resource exists for the desired storageconsumer and storageprofile. + cephRadosNamespaceList := &rookCephv1.CephBlockPoolRadosNamespaceList{} + err := r.list( + cephRadosNamespaceList, + client.InNamespace(r.OperatorNamespace), + client.MatchingFields{util.OwnerUIDIndexName: string(r.StorageClassRequest.UID)}) + if err != nil { + return err } - // check if a cephblockpool resource exists for the desired storageconsumer and storageprofile. - if r.cephBlockPool.Name == "" { - cephBlockPoolList := &rookCephv1.CephBlockPoolList{} - listOptions := &client.MatchingLabels{ - controllers.StorageConsumerNameLabel: r.storageConsumer.Name, - controllers.StorageProfileSpecLabel: storageProfile.GetSpecHash(), - } - if err := r.list(cephBlockPoolList, client.InNamespace(r.OperatorNamespace), listOptions); err != nil { - return err - } + // if we found no CephBlockPoolRadosNamespaces, generate a new name + // if we found only one CephBlockPoolRadosNamespace with our query, we're good + // if we found more than one CephBlockPoolRadosNamespace, we can't determine which one to select, so error out + rnsItemsLen := len(cephRadosNamespaceList.Items) + if rnsItemsLen == 0 { + md5Sum := md5.Sum([]byte(r.StorageClassRequest.Name)) + rnsName := fmt.Sprintf("cephradosnamespace-%s", hex.EncodeToString(md5Sum[:16])) + r.log.V(1).Info("no valid CephBlockPoolRadosNamespace found, creating new one", "CephBlockPoolRadosNamespace", rnsName) + r.cephRadosNamespace.Name = rnsName + } else if rnsItemsLen == 1 { + r.cephRadosNamespace = &cephRadosNamespaceList.Items[0] + r.log.V(1).Info("valid CephBlockPoolRadosNamespace found", "CephBlockPoolRadosNamespace", r.cephRadosNamespace.Name) + } else { + return fmt.Errorf("invalid number of CephBlockPoolRadosNamespaces for storage consumer %q: found %d, expecting 0 or 1", r.storageConsumer.Name, rnsItemsLen) + } + } else if r.StorageClassRequest.Spec.Type == "sharedfilesystem" { + if profileName == "" { + profileName = r.storageCluster.Spec.DefaultStorageProfile + } - // if we found no CephBlockPools, generate a new name - // if we found only one CephBlockPool with our query, we're good - // if we found more than one CephBlockPool, we can't determine which one to select, so error out - cbpItemsLen := len(cephBlockPoolList.Items) - if cbpItemsLen == 0 { - cbpNewName := fmt.Sprintf("cephblockpool-%s-%s", r.storageConsumer.Name, generateUUID()) - r.log.V(1).Info("no valid CephBlockPool found, creating new one", "CephBlockPool", cbpNewName) - r.cephBlockPool.Name = cbpNewName - } else if cbpItemsLen == 1 { - r.cephBlockPool.Name = cephBlockPoolList.Items[0].GetName() - r.log.V(1).Info("valid CephBlockPool found", "CephBlockPool", r.cephBlockPool.Name) - } else { - return fmt.Errorf("invalid number of CephBlockPools for storage consumer %q and storage profile %q: found %d, expecting 0 or 1", r.storageConsumer.Name, storageProfile.Name, cbpItemsLen) - } + // Fetch StorageProfile by name in the StorageCluster's namespace + storageProfile.Name = profileName + storageProfile.Namespace = r.storageCluster.Namespace + + if err := r.get(storageProfile); err != nil { + return fmt.Errorf("no storage profile CR found for storage profile %s", profileName) } - } else if r.StorageClassRequest.Spec.Type == "sharedfilesystem" { r.cephFilesystemSubVolumeGroup = &rookCephv1.CephFilesystemSubVolumeGroup{} r.cephFilesystemSubVolumeGroup.Namespace = r.OperatorNamespace cephFilesystemSubVolumeGroupList := &rookCephv1.CephFilesystemSubVolumeGroupList{} - err := r.Client.List( - r.ctx, + err := r.list( cephFilesystemSubVolumeGroupList, client.InNamespace(r.OperatorNamespace), client.MatchingFields{util.OwnerUIDIndexName: string(r.StorageClassRequest.UID)}) @@ -317,7 +319,7 @@ func (r *StorageClassRequestReconciler) reconcilePhases() (reconcile.Result, err return reconcile.Result{}, err } - if err := r.reconcileCephBlockPool(&storageProfile); err != nil { + if err := r.reconcileRadosNamespace(); err != nil { return reconcile.Result{}, err } @@ -352,43 +354,20 @@ func (r *StorageClassRequestReconciler) reconcilePhases() (reconcile.Result, err return reconcile.Result{}, nil } -func (r *StorageClassRequestReconciler) reconcileCephBlockPool(storageProfile *v1.StorageProfile) error { - - failureDomain := r.storageCluster.Status.FailureDomain - - _, err := ctrl.CreateOrUpdate(r.ctx, r.Client, r.cephBlockPool, func() error { - if err := r.own(r.cephBlockPool); err != nil { +func (r *StorageClassRequestReconciler) reconcileRadosNamespace() error { + _, err := ctrl.CreateOrUpdate(r.ctx, r.Client, r.cephRadosNamespace, func() error { + if err := r.own(r.cephRadosNamespace); err != nil { return err } - deviceClass := storageProfile.Spec.DeviceClass - deviceSetList := r.storageCluster.Spec.StorageDeviceSets - var deviceSet *v1.StorageDeviceSet - for i := range deviceSetList { - ds := &deviceSetList[i] - // get the required deviceSetName of the profile - if deviceClass == ds.DeviceClass { - deviceSet = ds - break - } - } - if deviceSet == nil { - return fmt.Errorf("could not find device set with device class %q in storagecluster", deviceClass) - } + addLabel(r.cephRadosNamespace, controllers.StorageConsumerNameLabel, r.storageConsumer.Name) - addLabel(r.cephBlockPool, controllers.StorageConsumerNameLabel, r.storageConsumer.Name) - addLabel(r.cephBlockPool, controllers.StorageProfileSpecLabel, storageProfile.GetSpecHash()) - - r.cephBlockPool.Spec = rookCephv1.NamedBlockPoolSpec{ - PoolSpec: rookCephv1.PoolSpec{ - FailureDomain: failureDomain, - DeviceClass: deviceClass, - Replicated: rookCephv1.ReplicatedSpec{ - Size: 3, - ReplicasPerFailureDomain: 1, - }, - Parameters: storageProfile.Spec.BlockPoolConfiguration.Parameters, - }, + blockPoolName := r.StorageClassRequest.Spec.StorageProfile + if blockPoolName == "" { + blockPoolName = fmt.Sprintf("%s-cephblockpool", r.storageCluster.Name) + } + r.cephRadosNamespace.Spec = rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: blockPoolName, } return nil }) @@ -396,9 +375,9 @@ func (r *StorageClassRequestReconciler) reconcileCephBlockPool(storageProfile *v if err != nil { r.log.Error( err, - "Failed to update CephBlockPool.", - "CephBlockPool", - klog.KRef(r.cephBlockPool.Namespace, r.cephBlockPool.Name), + "Failed to update CephBlockPoolRadosNamespace.", + "CephBlockPoolRadosNamespace", + klog.KRef(r.cephRadosNamespace.Namespace, r.cephRadosNamespace.Name), ) return err } @@ -408,11 +387,11 @@ func (r *StorageClassRequestReconciler) reconcileCephBlockPool(storageProfile *v "node": r.cephClientNode.Name, } phase := "" - if r.cephBlockPool.Status != nil { - phase = string(r.cephBlockPool.Status.Phase) + if r.cephRadosNamespace.Status != nil { + phase = string(r.cephRadosNamespace.Status.Phase) } - r.setCephResourceStatus(r.cephBlockPool.Name, "CephBlockPool", phase, cephClients) + r.setCephResourceStatus(r.cephRadosNamespace.Name, "CephBlockPoolRadosNamespace", phase, cephClients) return nil } @@ -491,9 +470,9 @@ func (r *StorageClassRequestReconciler) reconcileCephClientRBDProvisioner() erro addStorageRelatedAnnotations(r.cephClientProvisioner, r.getNamespacedName(), "rbd", "provisioner") r.cephClientProvisioner.Spec = rookCephv1.ClientSpec{ Caps: map[string]string{ - "mon": "profile rbd", + "mon": "profile rbd, allow command 'osd blocklist'", "mgr": "allow rw", - "osd": fmt.Sprintf("profile rbd pool=%s", r.cephBlockPool.Name), + "osd": fmt.Sprintf("profile rbd pool=%s namespace=%s", r.cephRadosNamespace.Spec.BlockPoolName, r.cephRadosNamespace.Name), }, } return nil @@ -529,7 +508,7 @@ func (r *StorageClassRequestReconciler) reconcileCephClientRBDNode() error { Caps: map[string]string{ "mon": "profile rbd", "mgr": "allow rw", - "osd": fmt.Sprintf("profile rbd pool=%s", r.cephBlockPool.Name), + "osd": fmt.Sprintf("profile rbd pool=%s namespace=%s", r.cephRadosNamespace.Spec.BlockPoolName, r.cephRadosNamespace.Name), }, } @@ -566,7 +545,7 @@ func (r *StorageClassRequestReconciler) reconcileCephClientCephFSProvisioner() e addStorageRelatedAnnotations(r.cephClientProvisioner, r.getNamespacedName(), "cephfs", "provisioner") r.cephClientProvisioner.Spec = rookCephv1.ClientSpec{ Caps: map[string]string{ - "mon": "allow r", + "mon": "allow r, allow command 'osd blocklist'", "mgr": "allow rw", "mds": fmt.Sprintf("allow rw path=/volumes/%s", r.cephFilesystemSubVolumeGroup.Name), "osd": "allow rw tag cephfs metadata=*", @@ -690,9 +669,3 @@ func addLabel(obj metav1.Object, key string, value string) { } labels[key] = value } - -// generateUUID generates a random UUID string and return first 8 characters. -func generateUUID() string { - newUUID := uuid.New().String() - return newUUID[:8] -} diff --git a/controllers/storageclassrequest/storageclassrequest_controller_test.go b/controllers/storageclassrequest/storageclassrequest_controller_test.go index 42d1c3f139..69db83300c 100644 --- a/controllers/storageclassrequest/storageclassrequest_controller_test.go +++ b/controllers/storageclassrequest/storageclassrequest_controller_test.go @@ -15,6 +15,8 @@ package storageclassrequest import ( "context" + "crypto/md5" + "encoding/hex" "fmt" "strings" "testing" @@ -55,25 +57,6 @@ var fakeStorageProfile = &v1.StorageProfile{ }, } -var validStorageProfile = &v1.StorageProfile{ - TypeMeta: metav1.TypeMeta{Kind: storageProfileKind}, - ObjectMeta: metav1.ObjectMeta{ - Name: "valid", - Namespace: namespaceName, - }, - Spec: v1.StorageProfileSpec{ - DeviceClass: deviceClass, - BlockPoolConfiguration: v1.BlockPoolConfigurationSpec{ - Parameters: map[string]string{ - pgAutoscaleMode: "on", - pgNum: "128", - pgpNum: "128", - }, - }, - }, - Status: v1.StorageProfileStatus{Phase: ""}, -} - // A rejected StorageProfile is one that is invalid due to having a blank device class field and is set to // Rejected in its phase. var rejectedStorageProfile = &v1.StorageProfile{ @@ -179,6 +162,7 @@ func createFakeReconciler(t *testing.T) StorageClassRequestReconciler { func newFakeClientBuilder(scheme *runtime.Scheme) *fake.ClientBuilder { return fake.NewClientBuilder(). WithScheme(scheme). + WithIndex(&rookCephv1.CephBlockPoolRadosNamespace{}, util.OwnerUIDIndexName, util.OwnersIndexFieldFunc). WithIndex(&rookCephv1.CephFilesystemSubVolumeGroup{}, util.OwnerUIDIndexName, util.OwnersIndexFieldFunc) } @@ -193,30 +177,15 @@ func TestProfileReconcile(t *testing.T) { failureExpected bool createObjects []runtime.Object }{ - { - label: "Reconcile blockpool StorageClassRequest", - scrType: "blockpool", - profileName: fakeStorageProfile.Name, - }, { label: "Reconcile sharedfilesystem StorageClassRequest", scrType: "sharedfilesystem", profileName: fakeStorageProfile.Name, }, - { - label: "Reconcile blockpool StorageClassRequest with default StorageProfile", - scrType: "blockpool", - }, { label: "Reconcile sharedfilesystem StorageClassRequest with default StorageProfile", scrType: "sharedfilesystem", }, - { - label: "Reconcile blockpool StorageClassRequest with invalid StorageProfile", - scrType: "blockpool", - profileName: "nope", - failureExpected: true, - }, { label: "Reconcile sharedfilesystem StorageClassRequest with invalid StorageProfile", scrType: "sharedfilesystem", @@ -283,109 +252,6 @@ func TestProfileReconcile(t *testing.T) { assert.NoError(t, err, caseLabel) } -func TestStorageProfileCephBlockPool(t *testing.T) { - var err error - var caseCounter int - - var primaryTestCases = []struct { - label string - expectedPoolName string - failureExpected bool - createObjects []runtime.Object - storageProfile *v1.StorageProfile - }{ - { - label: "valid profile", - expectedPoolName: "test-valid-blockpool", - failureExpected: false, - storageProfile: validStorageProfile, - createObjects: []runtime.Object{ - &rookCephv1.CephBlockPool{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-valid-blockpool", - Namespace: namespaceName, - Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: validStorageProfile.GetSpecHash(), - }, - }, Spec: rookCephv1.NamedBlockPoolSpec{ - Name: "spec", - PoolSpec: rookCephv1.PoolSpec{ - FailureDomain: "zone", - DeviceClass: deviceClass, - Parameters: map[string]string{}, - }, - }, - }, - }, - }, - { - label: "rejected profile", - expectedPoolName: "test-rejected-blockpool", - failureExpected: true, - storageProfile: rejectedStorageProfile, - createObjects: []runtime.Object{ - &rookCephv1.CephBlockPool{ - ObjectMeta: metav1.ObjectMeta{ - Name: "test-rejected-blockpool", - Namespace: namespaceName, - Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: rejectedStorageProfile.GetSpecHash(), - }, - }, Spec: rookCephv1.NamedBlockPoolSpec{ - Name: "spec", - PoolSpec: rookCephv1.PoolSpec{ - FailureDomain: "zone", - DeviceClass: deviceClass, - Parameters: map[string]string{}, - }, - }, - }, - }, - }, - } - - for _, c := range primaryTestCases { - caseCounter++ - caseLabel := fmt.Sprintf("Case %d: %s", caseCounter, c.label) - fmt.Println(caseLabel) - - r := createFakeReconciler(t) - r.storageCluster.Spec.DefaultStorageProfile = c.storageProfile.Name - r.StorageClassRequest.Spec.Type = "blockpool" - - r.StorageClassRequest.Spec.StorageProfile = c.storageProfile.Name - - c.createObjects = append(c.createObjects, c.storageProfile) - c.createObjects = append(c.createObjects, fakeStorageConsumer) - - fakeClient := newFakeClientBuilder(r.Scheme).WithRuntimeObjects(c.createObjects...) - r.Client = fakeClient.Build() - - _, err = r.reconcilePhases() - if c.failureExpected { - assert.Error(t, err, caseLabel) - continue - } - assert.NoError(t, err, caseLabel) - - assert.Equal(t, c.expectedPoolName, r.cephBlockPool.Name, caseLabel) - - if strings.Contains(c.expectedPoolName, "valid") { - expectedStorageProfileParameters := validStorageProfile.Spec.BlockPoolConfiguration.Parameters - actualBlockPoolParameters := r.cephBlockPool.Spec.Parameters - assert.Equal(t, expectedStorageProfileParameters, actualBlockPoolParameters, caseLabel) - assert.NotEqual(t, v1.StorageProfilePhaseRejected, c.storageProfile.Status.Phase) - } else { - actualBlockPoolParameters := r.cephBlockPool.Spec.Parameters - assert.Equal(t, v1.StorageProfilePhaseRejected, c.storageProfile.Status.Phase) - assert.Nil(t, actualBlockPoolParameters, caseLabel) - } - } - -} - func TestStorageProfileCephFsSubVolGroup(t *testing.T) { var err error var caseCounter int @@ -492,105 +358,157 @@ func TestCephBlockPool(t *testing.T) { cephResources []*v1alpha1.CephResourcesSpec }{ { - label: "No CephBlockPool exists", + label: "No CephBlockPool exists", + failureExpected: true, }, { - label: "Valid CephBlockPool exists", - expectedPoolName: "test-blockpool", + label: "Valid CephBlockPool and RadosNamespace exist", + expectedPoolName: "medium", createObjects: []runtime.Object{ &rookCephv1.CephBlockPool{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool", + Name: "medium", Namespace: "test-ns", Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), + controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), }, }, }, &rookCephv1.CephBlockPool{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool2", + Name: "medium2", Namespace: "test-ns", Labels: map[string]string{ - controllers.StorageConsumerNameLabel: "wrongConsumer", - controllers.StorageProfileSpecLabel: "0123456789", + controllers.StorageProfileSpecLabel: "0123456789", }, }, }, - }, - }, - { - label: "Valid CephBlockPool only exists for different consumer/profile", - createObjects: []runtime.Object{ - &rookCephv1.CephBlockPool{ + &rookCephv1.CephBlockPoolRadosNamespace{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool", + Name: "cephradosnamespace-d41d8cd98f00b204e9800998ecf8427e", Namespace: "test-ns", - Labels: map[string]string{ - controllers.StorageConsumerNameLabel: "wrongConsumer", - controllers.StorageProfileSpecLabel: "0123456789", + OwnerReferences: []metav1.OwnerReference{ + { + UID: storageClassRequestUID, + }, }, }, + Spec: rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: "medium", + }, }, }, }, { - label: "More than one valid CephBlockPool exists", - failureExpected: true, + label: "Valid RadosNamespace only exists for different profile", + expectedPoolName: "medium", createObjects: []runtime.Object{ &rookCephv1.CephBlockPool{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool", + Name: "medium", Namespace: "test-ns", Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), + controllers.StorageProfileSpecLabel: "0123456789", }, }, }, &rookCephv1.CephBlockPool{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool2", + Name: "medium2", Namespace: "test-ns", Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), + controllers.StorageProfileSpecLabel: "0123456789", }, }, }, + &rookCephv1.CephBlockPoolRadosNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cephradosnamespace-medium-test-consumer", + Namespace: "test-ns", + OwnerReferences: []metav1.OwnerReference{ + { + UID: "0123456789", + }, + }, + }, + Spec: rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: "medium2", + }, + }, }, }, { - label: "Request status already has valid CephResource", - expectedPoolName: "test-blockpool", - cephResources: []*v1alpha1.CephResourcesSpec{ - { - Name: "test-blockpool", - Kind: "CephBlockPool", - }, - }, + label: "More than one valid RadosNamespace exists", + failureExpected: true, + expectedPoolName: "medium", createObjects: []runtime.Object{ &rookCephv1.CephBlockPool{ ObjectMeta: metav1.ObjectMeta{ - Name: "test-blockpool", + Name: "medium", Namespace: "test-ns", Labels: map[string]string{ - controllers.StorageConsumerNameLabel: fakeStorageConsumer.Name, - controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), + controllers.StorageProfileSpecLabel: fakeStorageProfile.GetSpecHash(), + }, + }, + }, + &rookCephv1.CephBlockPoolRadosNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "medium-rns", + Namespace: "test-ns", + OwnerReferences: []metav1.OwnerReference{ + { + UID: storageClassRequestUID, + }, + }, + }, + Spec: rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: "medium", + }, + }, + &rookCephv1.CephBlockPoolRadosNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "medium-rns2", + Namespace: "test-ns", + OwnerReferences: []metav1.OwnerReference{ + { + UID: storageClassRequestUID, + }, }, }, + Spec: rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: "medium", + }, }, }, }, { - label: "Request status has CephResource that doesn't exist", - expectedPoolName: "test-blockpool", + label: "Request status has existing RadosNamespace and inextant CephBlockPool", + expectedPoolName: "medium", cephResources: []*v1alpha1.CephResourcesSpec{ { - Name: "test-blockpool", + Name: "medium", Kind: "CephBlockPool", }, + { + Name: "medium-rns", + Kind: "CephBlockPoolRadosNamespace", + }, + }, + createObjects: []runtime.Object{ + &rookCephv1.CephBlockPoolRadosNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "medium-rns", + Namespace: "test-ns", + OwnerReferences: []metav1.OwnerReference{ + { + UID: storageClassRequestUID, + }, + }, + }, + Spec: rookCephv1.CephBlockPoolRadosNamespaceSpec{ + BlockPoolName: "medium", + }, + }, }, }, } @@ -611,40 +529,48 @@ func TestCephBlockPool(t *testing.T) { r.Client = fakeClient.Build() _, err = r.reconcilePhases() - if c.failureExpected { - assert.Error(t, err, caseLabel) - continue - } - assert.NoError(t, err, caseLabel) - if c.expectedPoolName == "" { - assert.NotEmpty(t, r.cephBlockPool, caseLabel) + if err == nil && c.expectedPoolName == "" { + assert.NotEmpty(t, r.cephRadosNamespace.Spec.BlockPoolName, caseLabel) createdBlockpool := &rookCephv1.CephBlockPool{} - createdBlockpool.Name = r.cephBlockPool.Name - createdBlockpool.Namespace = r.cephBlockPool.Namespace + createdBlockpool.Name = r.cephRadosNamespace.Spec.BlockPoolName + createdBlockpool.Namespace = r.cephRadosNamespace.Namespace err = r.get(createdBlockpool) - assert.NoError(t, err, caseLabel) - } else { - assert.Equal(t, c.expectedPoolName, r.cephBlockPool.Name, caseLabel) } - } - caseCounter++ - caseLabel := fmt.Sprintf("Case %d: StorageProfile has invalid DeviceClass", caseCounter) - fmt.Println(caseLabel) + if c.failureExpected { + assert.Error(t, err, caseLabel) + continue + } + assert.NoError(t, err, caseLabel) - badStorageProfile := fakeStorageProfile.DeepCopy() - badStorageProfile.Spec.DeviceClass = "nope" + assert.Equal(t, c.expectedPoolName, r.cephRadosNamespace.Spec.BlockPoolName, caseLabel) + + // The generated CephBlockPoolRadosNamespace name is expected + // to be deterministic, so hard-coding the name generation in + // the test to guard against unintentional changes. + md5Sum := md5.Sum([]byte(r.StorageClassRequest.Name)) + expectedRadosNamespaceName := fmt.Sprintf("cephradosnamespace-%s", hex.EncodeToString(md5Sum[:16])) + for _, cephRes := range c.cephResources { + if cephRes.Kind == "CephBlockPoolRadosNamespace" { + expectedRadosNamespaceName = cephRes.Name + break + } + } + expectedRadosNamespace := &rookCephv1.CephBlockPoolRadosNamespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: expectedRadosNamespaceName, + Namespace: r.cephRadosNamespace.Namespace, + }, + } - r := createFakeReconciler(t) - r.StorageClassRequest.Spec.Type = "blockpool" - r.StorageClassRequest.Spec.StorageProfile = badStorageProfile.Name - fakeClient := newFakeClientBuilder(r.Scheme) - r.Client = fakeClient.WithRuntimeObjects(badStorageProfile, fakeStorageConsumer).Build() + assert.NotEmpty(t, r.cephRadosNamespace, caseLabel) + assert.Equal(t, expectedRadosNamespaceName, r.cephRadosNamespace.Name, caseLabel) - _, err = r.reconcilePhases() - assert.Error(t, err, caseLabel) + err = r.get(expectedRadosNamespace) + assert.NoError(t, err, caseLabel) + } } func TestCephFsSubVolGroup(t *testing.T) { diff --git a/controllers/storageconsumer/storageconsumer_controller.go b/controllers/storageconsumer/storageconsumer_controller.go index 8a26b2788d..c8ef6703f9 100644 --- a/controllers/storageconsumer/storageconsumer_controller.go +++ b/controllers/storageconsumer/storageconsumer_controller.go @@ -44,6 +44,7 @@ const ( StorageConsumerAnnotation = "ocs.openshift.io.storageconsumer" StorageRequestAnnotation = "ocs.openshift.io.storagerequest" StorageCephUserTypeAnnotation = "ocs.openshift.io.cephusertype" + StorageProfileLabel = "ocs.openshift.io/storageprofile" StorageProfileSpecLabel = "ocs.openshift.io/storageprofile-spec" ConsumerUUIDLabel = "ocs.openshift.io/storageconsumer-uuid" StorageConsumerNameLabel = "ocs.openshift.io/storageconsumer-name"