From 21ff52f6badc92c64ad1a0f4d94189e6a8679876 Mon Sep 17 00:00:00 2001 From: parth-gr Date: Mon, 1 Apr 2024 18:22:03 +0530 Subject: [PATCH] object: add support for user s3 key for cephobjectstoreuser CephObjectStoreUser should optionally be able to reference a secret where S3 key is defined. This enables us to specify the accesskey and accesssecret rather than those values being randomly generated. Closes: https://github.com/rook/rook/issues/11563 Signed-off-by: parth-gr --- .../ceph-object-store-user-crd.md | 73 +++++++++ go.mod | 2 +- pkg/operator/ceph/object/secret/secret.go | 77 +++++++++ pkg/operator/ceph/object/user.go | 25 ++- pkg/operator/ceph/object/user/controller.go | 149 ++++++++++++++---- .../ceph/object/user/controller_test.go | 28 ++-- 6 files changed, 303 insertions(+), 51 deletions(-) create mode 100644 pkg/operator/ceph/object/secret/secret.go diff --git a/Documentation/CRDs/Object-Storage/ceph-object-store-user-crd.md b/Documentation/CRDs/Object-Storage/ceph-object-store-user-crd.md index 4dde0d8bf1acf..6f6ce14b3feb5 100644 --- a/Documentation/CRDs/Object-Storage/ceph-object-store-user-crd.md +++ b/Documentation/CRDs/Object-Storage/ceph-object-store-user-crd.md @@ -60,3 +60,76 @@ spec: * `user-policy` * `odic-provider` * `ratelimit` + +### CephObjectStoreUser Reference Secret + +A Kubernetes secret is created by Rook with the name, `rook-ceph-object-user--` in the same namespace of cephobjectUser, where: + +* `store-name`: The object store name in which the user will be created. This matches the name of the objectstore CRD. +* `user-name`: The metadata name of the cephObjectStoreUser + +This secret can be used to configure there aplications. +Secret contains, +`AccessKey`, `Credentials`, `Endpoint`, `Endpoint`. + +```console +$ kubectl get secrets -nrook-ceph rook-ceph-object-user-my-store-my-user -o yaml +apiVersion: v1 +data: + AccessKey: *** + Credentials: *** + Endpoint: *** + SecretKey: *** +kind: Secret +... +... +type: kubernetes.io/rook + +``` +User can use the above s3 keys to connect the rgw user to there application which require object storage. + +### User Specific CephObjectStoreUser Reference Secret + +If a specific user key and secret is desired instead of randomly generated credentials, a specific user key and secret can be specified for an object store user. + +Create or update the Kubernetes secret with name, `rook-ceph-object-user--` in the same namespace of cephobjectUser, where: + +* `store-name`: The object store name in which the user will be created. This matches the name of the objectstore CRD. +* `user-name`: The metadata name of the cephObjectStoreUser + +#### Secret details: + +i) The annotations as `rook.io/source-of-truth: secret` specified and type as `type: "kubernetes.io/rook"`. + +ii) (optional) The array of `SecretKeys` which contains all the truted access and secret keys. If any key is present in the ceph user and not present in the `SecretKeys`, it will be removed from the ceph user too. + +If `SecretKeys` is left empty, it will be auto updated by the latest AccessKey and SecretKey and will remove all the other keys from the ceph user. + +```console +`SecretKeys` +[{"access_key":"IE58RNT71Y2F1EQE80RA","secret_key":"cULyMz5dCpX18dPsJhpIKay7vcDNRNJWJPu8VqUA"}, {"access_key":"IE58RNT71Y2F1EQE80RC","secret_key":"cULyMz5dCpX18dPsJhpIKay7vcDNRNJWJPu8VqUA"}] + +!!! note + All the data values should be converted to base64 format and then updated. + +Example Secret: +```console +kubectl create -f +apiVersion: v1 +kind: Secret +metadata: + name: rook-ceph-object-user-my-store-my-user + namespace: rook-ceph + annotations: + rook.io/source-of-truth: secret +data: + AccessKey: *** + SecretKey: *** + Endpoint: *** + Credentials: *** +type: "kubernetes.io/rook" +``` + +!!! note + Be careful when updating Kubernetes secret values. Mistakes entering info can cause errors reconciling the CephObjectStore. + And ignore the endpoint when `rook.io/source-of-truth: secret` is set, and instead use the `CephObjectStore.Status.Endpoints` list to retrieve endpoints for applications. diff --git a/go.mod b/go.mod index d6e72f3f3acf0..a2d967c5a7a64 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( ) require ( - emperror.dev/errors v0.8.1 // indirect + emperror.dev/errors v0.8.1 github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/ansel1/merry v1.8.0 // indirect github.com/ansel1/merry/v2 v2.2.0 // indirect diff --git a/pkg/operator/ceph/object/secret/secret.go b/pkg/operator/ceph/object/secret/secret.go new file mode 100644 index 0000000000000..00305a5e6f62e --- /dev/null +++ b/pkg/operator/ceph/object/secret/secret.go @@ -0,0 +1,77 @@ +/* +Copyright 2016 The Rook Authors. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package secret + +import ( + "encoding/json" + "strings" + + "emperror.dev/errors" + "github.com/ceph/go-ceph/rgw/admin" +) + +const ( + //#nosec G101 -- This is the name of an annotation key, not a secret + updateObjectUserSecretAnnotation = "rook.io/source-of-truth" + sourceOfTruthSecret = "secret" +) + +func ValidateCredentials(accessKey, secretKey string, credentials []admin.UserKeySpec) ([]admin.UserKeySpec, error) { + // if len is 0 just update the credentials array with the accessKey and secretKey key + if len(credentials) == 0 { + credentials = []admin.UserKeySpec{ + {AccessKey: accessKey, + SecretKey: secretKey}} + return credentials, nil + } + // match if the credentials array contains the accessKey and secretKey key + for i := range credentials { + if credentials[i].AccessKey == accessKey && credentials[0].SecretKey == secretKey { + return credentials, nil + } + } + + return credentials, errors.Errorf("secret keys data is invalid, please update the secret with cvalid format") +} + +func UnmarshalKeys(credentials []byte) ([]admin.UserKeySpec, error) { + var userKeys []admin.UserKeySpec + err := json.Unmarshal(credentials, &userKeys) + if err != nil { + return userKeys, errors.Wrapf(err, "unable to unmarshal credentials from the object secret") + } + return userKeys, nil +} + +func ForceUpdateObjectUserSecret(annotations map[string]string) bool { + if value, found := annotations[updateObjectUserSecretAnnotation]; found { + if strings.EqualFold(value, sourceOfTruthSecret) { + return true + } + } + return false +} + +func UpdatecredentialsUserId(uid, displayName string, credentials []admin.UserKeySpec) []admin.UserKeySpec { + updatedCredentials := credentials + for i := range credentials { + updatedCredentials[i].UID = uid + updatedCredentials[i].User = displayName + } + + return updatedCredentials +} diff --git a/pkg/operator/ceph/object/user.go b/pkg/operator/ceph/object/user.go index 7cb2b670b682a..c1a6c5c85b6ff 100644 --- a/pkg/operator/ceph/object/user.go +++ b/pkg/operator/ceph/object/user.go @@ -241,17 +241,23 @@ func GenerateCephUserSecretName(store, username string) string { return fmt.Sprintf("rook-ceph-object-user-%s-%s", store, username) } -func generateCephUserSecret(userConfig *admin.User, endpoint, namespace, storeName, tlsSecretName string) *corev1.Secret { +func generateCephUserSecret(userConfig *admin.User, endpoint, namespace, storeName, tlsSecretName string) (*corev1.Secret, error) { secretName := GenerateCephUserSecretName(storeName, userConfig.ID) + secretKeys, err := json.Marshal(userConfig.Keys) + if err != nil { + return &corev1.Secret{}, err + } // Store the keys in a secret secrets := map[string]string{ - "AccessKey": userConfig.Keys[0].AccessKey, - "SecretKey": userConfig.Keys[0].SecretKey, - "Endpoint": endpoint, + "AccessKey": userConfig.Keys[0].AccessKey, + "SecretKey": userConfig.Keys[0].SecretKey, + "SecretKeys": string(secretKeys), + "Endpoint": endpoint, } if tlsSecretName != "" { secrets["SSLCertSecretName"] = tlsSecretName } + secret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ Name: secretName, @@ -266,15 +272,18 @@ func generateCephUserSecret(userConfig *admin.User, endpoint, namespace, storeNa StringData: secrets, Type: k8sutil.RookType, } - return secret + + return secret, nil } func ReconcileCephUserSecret(ctx context.Context, k8sclient client.Client, scheme *runtime.Scheme, ownerRef metav1.Object, userConfig *admin.User, endpoint, namespace, storeName, tlsSecretName string) (reconcile.Result, error) { // Generate Kubernetes Secret - secret := generateCephUserSecret(userConfig, endpoint, namespace, storeName, tlsSecretName) - + secret, err := generateCephUserSecret(userConfig, endpoint, namespace, storeName, tlsSecretName) + if err != nil { + return reconcile.Result{}, errors.Wrapf(err, "failed to get the ceph object user secret %q", GenerateCephUserSecretName(storeName, userConfig.ID)) + } // Set owner ref to the object store user object - err := controllerutil.SetControllerReference(ownerRef, secret, scheme) + err = controllerutil.SetControllerReference(ownerRef, secret, scheme) if err != nil { return reconcile.Result{}, errors.Wrapf(err, "failed to set owner reference of ceph object user secret %q", secret.Name) } diff --git a/pkg/operator/ceph/object/user/controller.go b/pkg/operator/ceph/object/user/controller.go index 1c7624e6c7c46..3adb2b0720e96 100644 --- a/pkg/operator/ceph/object/user/controller.go +++ b/pkg/operator/ceph/object/user/controller.go @@ -23,28 +23,28 @@ import ( "reflect" "github.com/ceph/go-ceph/rgw/admin" - opcontroller "github.com/rook/rook/pkg/operator/ceph/controller" - "github.com/rook/rook/pkg/operator/ceph/reporting" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/coreos/pkg/capnslog" "github.com/pkg/errors" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" "github.com/rook/rook/pkg/clusterd" cephclient "github.com/rook/rook/pkg/daemon/ceph/client" + opcontroller "github.com/rook/rook/pkg/operator/ceph/controller" "github.com/rook/rook/pkg/operator/ceph/object" + secret "github.com/rook/rook/pkg/operator/ceph/object/secret" + "github.com/rook/rook/pkg/operator/ceph/reporting" "github.com/rook/rook/pkg/operator/k8sutil" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" ) const ( @@ -250,7 +250,7 @@ func (r *ReconcileObjectStoreUser) reconcile(request reconcile.Request) (reconci } // CREATE/UPDATE CEPH USER - reconcileResponse, err = r.reconcileCephUser(cephObjectStoreUser) + reconcileResponse, userKeysSource, err := r.reconcileCephUser(cephObjectStoreUser) if err != nil { r.updateStatus(k8sutil.ObservedGenerationNotAvailable, request.NamespacedName, k8sutil.ReconcileFailedStatus) return reconcileResponse, *cephObjectStoreUser, err @@ -263,12 +263,13 @@ func (r *ReconcileObjectStoreUser) reconcile(request reconcile.Request) (reconci } tlsSecretName := store.Spec.Gateway.SSLCertificateRef - reconcileResponse, err = object.ReconcileCephUserSecret(r.opManagerContext, r.client, r.scheme, cephObjectStoreUser, r.userConfig, r.objContext.Endpoint, cephObjectStoreUser.Namespace, cephObjectStoreUser.Spec.Store, tlsSecretName) - if err != nil { - r.updateStatus(k8sutil.ObservedGenerationNotAvailable, request.NamespacedName, k8sutil.ReconcileFailedStatus) - return reconcileResponse, *cephObjectStoreUser, err + if !userKeysSource { + reconcileResponse, err = object.ReconcileCephUserSecret(r.opManagerContext, r.client, r.scheme, cephObjectStoreUser, r.userConfig, r.objContext.Endpoint, cephObjectStoreUser.Namespace, cephObjectStoreUser.Spec.Store, tlsSecretName) + if err != nil { + r.updateStatus(k8sutil.ObservedGenerationNotAvailable, request.NamespacedName, k8sutil.ReconcileFailedStatus) + return reconcileResponse, *cephObjectStoreUser, err + } } - // update ObservedGeneration in status at the end of reconcile // Set Ready status, we are done reconciling r.updateStatus(observedGeneration, request.NamespacedName, k8sutil.ReadyStatus) @@ -278,41 +279,117 @@ func (r *ReconcileObjectStoreUser) reconcile(request reconcile.Request) (reconci return reconcile.Result{}, *cephObjectStoreUser, nil } -func (r *ReconcileObjectStoreUser) reconcileCephUser(cephObjectStoreUser *cephv1.CephObjectStoreUser) (reconcile.Result, error) { - err := r.createOrUpdateCephUser(cephObjectStoreUser) +func (r *ReconcileObjectStoreUser) reconcileCephUser(cephObjectStoreUser *cephv1.CephObjectStoreUser) (reconcile.Result, bool, error) { + userKeysSource, err := r.createOrUpdateCephUser(cephObjectStoreUser) if err != nil { - return reconcile.Result{}, errors.Wrapf(err, "failed to create/update object store user %q", cephObjectStoreUser.Name) + return reconcile.Result{}, userKeysSource, errors.Wrapf(err, "failed to create/update object store user %q", cephObjectStoreUser.Name) } - return reconcile.Result{}, nil + return reconcile.Result{}, userKeysSource, nil } -func (r *ReconcileObjectStoreUser) createOrUpdateCephUser(u *cephv1.CephObjectStoreUser) error { +func (r *ReconcileObjectStoreUser) getUserDefinedSecret(u *cephv1.CephObjectStoreUser, userKeysSource bool) ([]admin.UserKeySpec, bool, error) { + var credentialsStruct []admin.UserKeySpec + secretName := object.GenerateCephUserSecretName(u.Spec.Store, u.Name) + namspacedName := types.NamespacedName{Namespace: u.Namespace, Name: secretName} + + cephObjectStoreUserSecret := &corev1.Secret{} + err := r.client.Get(r.clusterInfo.Context, namspacedName, cephObjectStoreUserSecret) + if err != nil { + if !kerrors.IsNotFound(err) { + return credentialsStruct, userKeysSource, errors.Wrapf(err, "failed to get user cephobjectstoreuser secret %q", secretName) + } + logger.Debugf("no user secret %q provided for cephobjectstoreuser", secretName) + + } else { + if secret.ForceUpdateObjectUserSecret(cephObjectStoreUserSecret.GetAnnotations()) { + userKeysSource = true + credentialsStruct, err = secret.UnmarshalKeys(cephObjectStoreUserSecret.Data["Credentials"]) + if err != nil { + return credentialsStruct, userKeysSource, errors.Wrapf(err, "invalid cephobjectstoreuser secret %q format", secretName) + } + credentialsStruct, err = secret.ValidateCredentials(string(cephObjectStoreUserSecret.Data["AccessKey"]), string(cephObjectStoreUserSecret.Data["SecretKey"]), credentialsStruct) + if err != nil { + return credentialsStruct, userKeysSource, errors.Wrapf(err, "invalid cephobjectstoreuser secret %q values", secretName) + } + } + } + + return credentialsStruct, userKeysSource, nil +} + +func (r *ReconcileObjectStoreUser) modifyObjectUserSecret(credentialsStruct []admin.UserKeySpec, user admin.User) error { + // if secret exists and source of truth is secret then use the user specified keys + r.userConfig.Keys = credentialsStruct + // update the userid on the credentials + credentialsStruct = secret.UpdatecredentialsUserId(user.ID, user.DisplayName, credentialsStruct) + // get and map all the secrets of the ceph user + credentialsExists := make(map[string]string) + for i := range user.Keys { + credentialsExists[user.Keys[i].AccessKey] = user.Keys[i].SecretKey + } + // add the new keys from the credentials array that are not already present in the ceph user + // addition and removal of keys can be done only one at a time + for _, secretKey := range credentialsStruct { + // remove the key from the credentialsExists map if it already exists + if credentialsExists[secretKey.AccessKey] == secretKey.SecretKey { + credentialsExists[secretKey.AccessKey] = "" + continue + } + // add the key to the ceph user + _, err := r.objContext.AdminOpsClient.CreateKey(r.opManagerContext, secretKey) + if err != nil { + return errors.Wrapf(err, "failed to update ceph object user %q key", r.userConfig.ID) + } + } + // remove the keys from the ceph user that are not in the credentialsStruct + for accessKey, secretKey := range credentialsExists { + if secretKey == "" { + break + } + // remove the key to the ceph user + err := r.objContext.AdminOpsClient.RemoveKey(r.opManagerContext, admin.UserKeySpec{AccessKey: accessKey, SecretKey: secretKey, UID: user.ID, User: user.DisplayName}) + if err != nil { + return errors.Wrapf(err, "failed to remove the ceph object user %q key", r.userConfig.ID) + } + } + return nil +} + +func (r *ReconcileObjectStoreUser) createOrUpdateCephUser(u *cephv1.CephObjectStoreUser) (bool, error) { logger.Infof("creating ceph object user %q in namespace %q", u.Name, u.Namespace) logCreateOrUpdate := fmt.Sprintf("retrieved existing ceph object user %q", u.Name) var user admin.User + userKeysSource := false var err error + + // get the user defined k8s s3 keys if exists + credentialsStruct, userKeysSource, err := r.getUserDefinedSecret(u, userKeysSource) + if err != nil { + return userKeysSource, errors.Wrapf(err, "failed to user defined k8s s3 keys of object user %q", r.userConfig.ID) + } + user, err = r.objContext.AdminOpsClient.GetUser(r.opManagerContext, *r.userConfig) if err != nil { if errors.Is(err, admin.ErrNoSuchUser) { user, err = r.objContext.AdminOpsClient.CreateUser(r.opManagerContext, *r.userConfig) if err != nil { - return errors.Wrapf(err, "failed to create ceph object user %v", &r.userConfig.ID) + return userKeysSource, errors.Wrapf(err, "failed to create ceph object user %v", &r.userConfig.ID) } logCreateOrUpdate = fmt.Sprintf("created ceph object user %q", u.Name) } else { - return errors.Wrapf(err, "failed to get details from ceph object user %q", u.Name) + return userKeysSource, errors.Wrapf(err, "failed to get details from ceph object user %q", u.Name) } } // Update max bucket if necessary logger.Tracef("user capabilities(id: %s, caps: %#v, user caps: %s, op mask: %s)", user.ID, user.Caps, user.UserCaps, user.OpMask) - if *user.MaxBuckets != *r.userConfig.MaxBuckets { + if *user.MaxBuckets != *r.userConfig.MaxBuckets || userKeysSource { user, err = r.objContext.AdminOpsClient.ModifyUser(r.opManagerContext, *r.userConfig) if err != nil { - return errors.Wrapf(err, "failed to update ceph object user %q max buckets", r.userConfig.ID) + return userKeysSource, errors.Wrapf(err, "failed to update ceph object user %q max buckets", r.userConfig.ID) } logCreateOrUpdate = fmt.Sprintf("updated ceph object user %q", u.Name) } @@ -325,14 +402,14 @@ func (r *ReconcileObjectStoreUser) createOrUpdateCephUser(u *cephv1.CephObjectSt logger.Tracef("remove capabilities %s from user %s", user.UserCaps, r.userConfig.ID) _, err = r.objContext.AdminOpsClient.RemoveUserCap(r.opManagerContext, r.userConfig.ID, user.UserCaps) if err != nil { - return errors.Wrapf(err, "failed to remove current ceph object user %q capabilities", r.userConfig.ID) + return userKeysSource, errors.Wrapf(err, "failed to remove current ceph object user %q capabilities", r.userConfig.ID) } } if r.userConfig.UserCaps != "" { logger.Tracef("set capabilities %s for user %s", r.userConfig.UserCaps, r.userConfig.ID) _, err = r.objContext.AdminOpsClient.AddUserCap(r.opManagerContext, r.userConfig.ID, r.userConfig.UserCaps) if err != nil { - return errors.Wrapf(err, "failed to update ceph object user %q capabilities", r.userConfig.ID) + return userKeysSource, errors.Wrapf(err, "failed to update ceph object user %q capabilities", r.userConfig.ID) } } logCreateOrUpdate = fmt.Sprintf("updated ceph object user %q", u.Name) @@ -359,18 +436,26 @@ func (r *ReconcileObjectStoreUser) createOrUpdateCephUser(u *cephv1.CephObjectSt } err = r.objContext.AdminOpsClient.SetUserQuota(r.opManagerContext, userQuota) if err != nil { - return errors.Wrapf(err, "failed to set quotas for user %q", u.Name) + return userKeysSource, errors.Wrapf(err, "failed to set quotas for user %q", u.Name) + } + + if userKeysSource { + err = r.modifyObjectUserSecret(credentialsStruct, user) + if err != nil { + return userKeysSource, errors.Wrapf(err, "failed to modify objectstoreuser %q secret, when the source-of-truth was secret", u.Name) + } + logger.Infof("updated objectstoreuser %q secret, when the source-of-truth was secret", u.Name) } // Set access and secret key if r.userConfig.Keys == nil { r.userConfig.Keys = make([]admin.UserKeySpec, 1) + r.userConfig.Keys[0].AccessKey = user.Keys[0].AccessKey + r.userConfig.Keys[0].SecretKey = user.Keys[0].SecretKey } - r.userConfig.Keys[0].AccessKey = user.Keys[0].AccessKey - r.userConfig.Keys[0].SecretKey = user.Keys[0].SecretKey logger.Info(logCreateOrUpdate) - return nil + return userKeysSource, nil } func (r *ReconcileObjectStoreUser) initializeObjectStoreContext(u *cephv1.CephObjectStoreUser) error { diff --git a/pkg/operator/ceph/object/user/controller_test.go b/pkg/operator/ceph/object/user/controller_test.go index b443ebca21362..31cdaa21d42f4 100644 --- a/pkg/operator/ceph/object/user/controller_test.go +++ b/pkg/operator/ceph/object/user/controller_test.go @@ -30,6 +30,7 @@ import ( "github.com/coreos/pkg/capnslog" cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1" rookclient "github.com/rook/rook/pkg/client/clientset/versioned/fake" + cephclient "github.com/rook/rook/pkg/daemon/ceph/client" "github.com/rook/rook/pkg/operator/k8sutil" "github.com/rook/rook/pkg/operator/test" @@ -491,21 +492,28 @@ func TestCreateOrUpdateCephUser(t *testing.T) { return nil, fmt.Errorf("unexpected request: %q. method %q. path %q", req.URL.RawQuery, req.Method, req.URL.Path) }, } + s := scheme.Scheme + cl := fake.NewClientBuilder().WithScheme(s).Build() adminClient, err := admin.New("rook-ceph-rgw-my-store.mycluster.svc", "53S6B9S809NUP19IJ2K3", "1bXPegzsGClvoGAiJdHQD1uOW2sQBLAZM9j9VtXR", mockClient) assert.NoError(t, err) userConfig := generateUserConfig(objectUser) + clusterInfo := &cephclient.ClusterInfo{ + Context: context.TODO(), + } r := &ReconcileObjectStoreUser{ objContext: &cephobject.AdminOpsContext{ AdminOpsClient: adminClient, }, userConfig: &userConfig, opManagerContext: context.TODO(), + client: cl, + clusterInfo: clusterInfo, } maxsize, err := resource.ParseQuantity(maxsizestr) assert.NoError(t, err) t.Run("user with empty name", func(t *testing.T) { - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.Error(t, err) }) @@ -513,7 +521,7 @@ func TestCreateOrUpdateCephUser(t *testing.T) { objectUser.Name = name userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) @@ -521,7 +529,7 @@ func TestCreateOrUpdateCephUser(t *testing.T) { objectUser.Spec.Quotas = &cephv1.ObjectUserQuotaSpec{MaxBuckets: &maxbucket} userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) @@ -535,7 +543,7 @@ func TestCreateOrUpdateCephUser(t *testing.T) { } userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) @@ -545,35 +553,35 @@ func TestCreateOrUpdateCephUser(t *testing.T) { objectUser.Spec.Quotas = &cephv1.ObjectUserQuotaSpec{MaxObjects: &maxobject} userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) t.Run("setting MaxSize for the user", func(t *testing.T) { objectUser.Spec.Quotas = &cephv1.ObjectUserQuotaSpec{MaxSize: &maxsize} userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) t.Run("resetting MaxSize and MaxObjects for the user", func(t *testing.T) { objectUser.Spec.Quotas = nil userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) t.Run("setting both MaxSize and MaxObjects for the user", func(t *testing.T) { objectUser.Spec.Quotas = &cephv1.ObjectUserQuotaSpec{MaxObjects: &maxobject, MaxSize: &maxsize} userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) t.Run("resetting MaxSize and MaxObjects again for the user", func(t *testing.T) { objectUser.Spec.Quotas = nil userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) @@ -587,7 +595,7 @@ func TestCreateOrUpdateCephUser(t *testing.T) { objectUser.Spec.Quotas = &cephv1.ObjectUserQuotaSpec{MaxBuckets: &maxbucket, MaxObjects: &maxobject, MaxSize: &maxsize} userConfig = generateUserConfig(objectUser) r.userConfig = &userConfig - err = r.createOrUpdateCephUser(objectUser) + _, err = r.createOrUpdateCephUser(objectUser) assert.NoError(t, err) }) }