diff --git a/controllers/capabilities/tenant_controller.go b/controllers/capabilities/tenant_controller.go index 0640999ce..268640a1d 100644 --- a/controllers/capabilities/tenant_controller.go +++ b/controllers/capabilities/tenant_controller.go @@ -19,22 +19,22 @@ package controllers import ( "bytes" "context" + "encoding/json" "fmt" - "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/reconcile" capabilitiesv1alpha1 "github.com/3scale/3scale-operator/apis/capabilities/v1alpha1" "github.com/3scale/3scale-operator/pkg/3scale/amp/component" controllerhelper "github.com/3scale/3scale-operator/pkg/controller/helper" + "github.com/3scale/3scale-operator/pkg/reconcilers" + "github.com/3scale/3scale-operator/version" ) // tenant deletion state @@ -47,30 +47,31 @@ const tenantFinalizer = "tenant.capabilities.3scale.net/finalizer" const TenantAdminPasswordSecretField = "admin_password" // Tenant's credentials secret field name for access token -const TenantProviderKeySecretField = "token" +const TenantAccessTokenSecretField = "token" // Tenant's credentials secret field name for admin domain url const TenantAdminDomainKeySecretField = "adminURL" // TenantReconciler reconciles a Tenant object type TenantReconciler struct { - Client client.Client - Log logr.Logger - Scheme *runtime.Scheme - EventRecorder record.EventRecorder + *reconcilers.BaseReconciler } +// blank assignment to verify that TenantReconciler implements reconcile.Reconciler +var _ reconcile.Reconciler = &TenantReconciler{} + // +kubebuilder:rbac:groups=capabilities.3scale.net,namespace=placeholder,resources=tenants,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=capabilities.3scale.net,namespace=placeholder,resources=tenants/status,verbs=get;update;patch // +kubebuilder:rbac:groups=capabilities.3scale.net,namespace=placeholder,resources=tenants/finalizers,verbs=get;list;watch;create;update;patch;delete func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { _ = context.Background() - reqLogger := r.Log.WithValues("tenant", req.NamespacedName) + reqLogger := r.Logger().WithValues("tenant", req.NamespacedName) + reqLogger.Info("Reconcile Tenant", "Operator version", version.Version) // Fetch the Tenant instance tenantR := &capabilitiesv1alpha1.Tenant{} - err := r.Client.Get(context.TODO(), req.NamespacedName, tenantR) + err := r.Client().Get(context.TODO(), req.NamespacedName, tenantR) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. @@ -83,7 +84,15 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { return ctrl.Result{}, err } - masterAccessToken, err := r.FetchMasterCredentials(r.Client, tenantR) + if reqLogger.V(1).Enabled() { + jsonData, err := json.MarshalIndent(tenantR, "", " ") + if err != nil { + return ctrl.Result{}, err + } + reqLogger.V(1).Info(string(jsonData)) + } + + masterAccessToken, err := r.fetchMasterCredentials(tenantR) if err != nil { reqLogger.Error(err, "Error fetching master credentials secret") // Error reading the object - requeue the request. @@ -99,7 +108,7 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { // Tenant has been marked for deletion if tenantR.GetDeletionTimestamp() != nil && controllerutil.ContainsFinalizer(tenantR, tenantFinalizer) { - existingTenant, err := controllerhelper.FetchTenant(tenantR, portaClient) + existingTenant, err := controllerhelper.FetchTenant(tenantR.Status.TenantId, portaClient) if err != nil { return ctrl.Result{}, err } @@ -110,7 +119,7 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { if existingTenant.Signup.Account.State != scheduledForDeletionState { err := portaClient.DeleteTenant(tenantR.Status.TenantId) if err != nil { - r.EventRecorder.Eventf(tenantR, corev1.EventTypeWarning, "Failed to delete tenant", "%v", err) + r.EventRecorder().Eventf(tenantR, corev1.EventTypeWarning, "Failed to delete tenant", "%v", err) return ctrl.Result{}, err } } else { @@ -118,8 +127,8 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } } - // add or remove finalizer, tenant CR will be deleted if tenant in 3scale does not exists, if tenant is deleted or if it's already marked for deletion - err = controllerhelper.ReconcileFinalizers(tenantR, r.Client, tenantFinalizer) + controllerutil.RemoveFinalizer(tenantR, tenantFinalizer) + err = r.UpdateResource(tenantR) if err != nil { return ctrl.Result{}, err } @@ -134,7 +143,8 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { } if !controllerutil.ContainsFinalizer(tenantR, tenantFinalizer) { - err = controllerhelper.ReconcileFinalizers(tenantR, r.Client, tenantFinalizer) + controllerutil.AddFinalizer(tenantR, tenantFinalizer) + err = r.UpdateResource(tenantR) if err != nil { return ctrl.Result{}, err } @@ -143,7 +153,7 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { changed := tenantR.SetDefaults() if changed { - err = r.Client.Update(context.TODO(), tenantR) + err = r.UpdateResource(tenantR) if err != nil { return ctrl.Result{}, err } @@ -152,14 +162,18 @@ func (r *TenantReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { return ctrl.Result{}, nil } - internalReconciler := NewTenantInternalReconciler(r.Client, tenantR, portaClient, reqLogger) - err = internalReconciler.Run() + internalReconciler := NewTenantInternalReconciler(r.BaseReconciler, tenantR, portaClient, reqLogger) + res, err := internalReconciler.Run() if err != nil { reqLogger.Error(err, "Error in tenant reconciliation") // Error reading the object - requeue the request. return ctrl.Result{}, err } + if res.Requeue { + return res, nil + } + reqLogger.Info("Tenant reconciled successfully") return ctrl.Result{}, nil } @@ -170,11 +184,10 @@ func (r *TenantReconciler) SetupWithManager(mgr ctrl.Manager) error { Complete(r) } -// FetchMasterCredentials get secret using k8s client -func (r *TenantReconciler) FetchMasterCredentials(k8sClient client.Client, tenantR *capabilitiesv1alpha1.Tenant) (string, error) { +func (r *TenantReconciler) fetchMasterCredentials(tenantR *capabilitiesv1alpha1.Tenant) (string, error) { masterCredentialsSecret := &v1.Secret{} - err := k8sClient.Get(context.TODO(), + err := r.Client().Get(context.TODO(), types.NamespacedName{ Name: tenantR.Spec.MasterCredentialsRef.Name, Namespace: tenantR.Spec.MasterCredentialsRef.Namespace, diff --git a/controllers/capabilities/tenant_internal_reconciler.go b/controllers/capabilities/tenant_internal_reconciler.go index e28070c8b..25450c985 100644 --- a/controllers/capabilities/tenant_internal_reconciler.go +++ b/controllers/capabilities/tenant_internal_reconciler.go @@ -4,35 +4,35 @@ import ( "bytes" "context" "fmt" - "reflect" - apiv1alpha1 "github.com/3scale/3scale-operator/apis/capabilities/v1alpha1" - controllerhelper "github.com/3scale/3scale-operator/pkg/controller/helper" porta_client_pkg "github.com/3scale/3scale-porta-go-client/client" "github.com/go-logr/logr" v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" + ctrl "sigs.k8s.io/controller-runtime" + + apiv1alpha1 "github.com/3scale/3scale-operator/apis/capabilities/v1alpha1" + controllerhelper "github.com/3scale/3scale-operator/pkg/controller/helper" + "github.com/3scale/3scale-operator/pkg/reconcilers" ) // TenantInternalReconciler reconciles a Tenant object type TenantInternalReconciler struct { - k8sClient client.Client + *reconcilers.BaseReconciler tenantR *apiv1alpha1.Tenant portaClient *porta_client_pkg.ThreeScaleClient logger logr.Logger } // NewTenantInternalReconciler constructs InternalReconciler object -func NewTenantInternalReconciler(k8sClient client.Client, tenantR *apiv1alpha1.Tenant, +func NewTenantInternalReconciler(b *reconcilers.BaseReconciler, tenantR *apiv1alpha1.Tenant, portaClient *porta_client_pkg.ThreeScaleClient, log logr.Logger) *TenantInternalReconciler { return &TenantInternalReconciler{ - k8sClient: k8sClient, - tenantR: tenantR, - portaClient: portaClient, - logger: log, + BaseReconciler: b, + tenantR: tenantR, + portaClient: portaClient, + logger: log, } } @@ -41,53 +41,69 @@ func NewTenantInternalReconciler(k8sClient client.Client, tenantR *apiv1alpha1.T // - Have 3scale Tenant Account // - Have active admin user // - Have secret with tenant's access_token -func (r *TenantInternalReconciler) Run() error { - tenantDef, err := r.reconcileTenant() +func (r *TenantInternalReconciler) Run() (ctrl.Result, error) { + res, err := r.reconcileTenant() if err != nil { - return err + return ctrl.Result{}, err } - adminUserDef, err := r.reconcileAdminUser(tenantDef) - if err != nil { - return err + if res.Requeue { + return res, nil } - err = r.reconcileAccessTokenSecret(tenantDef) + err = r.reconcileAdminUser() if err != nil { - return err + return ctrl.Result{}, err } - tenantStatus := r.getTenantStatus(tenantDef, adminUserDef) - - return r.updateTenantStatus(tenantStatus) + return ctrl.Result{}, err } // This method makes sure that tenant exists, otherwise it will create one // On method completion: // * tenant will exist // * tenant's attributes will be updated if required -func (r *TenantInternalReconciler) reconcileTenant() (*porta_client_pkg.Tenant, error) { - tenantDef, err := controllerhelper.FetchTenant(r.tenantR, r.portaClient) +func (r *TenantInternalReconciler) reconcileTenant() (ctrl.Result, error) { + tenantDef, err := controllerhelper.FetchTenant(r.tenantR.Status.TenantId, r.portaClient) if err != nil { - return nil, err + return ctrl.Result{}, err } if tenantDef == nil { tenantDef, err = r.createTenant() if err != nil { - return nil, err + return ctrl.Result{}, err } - } else { - r.logger.Info("Tenant already exists", "TenantId", tenantDef.Signup.Account.ID) - // Tenant is not created, check tenant desired state matches current state - // When created, not needed to update - err := r.syncTenant(tenantDef) + + // Early save access token as it is only available on the response of the + // tenant creation call + + err = r.reconcileAccessTokenSecret(tenantDef) + if err != nil { + return ctrl.Result{}, err + } + + // Early update status with tenantID + r.tenantR.Status.TenantId = tenantDef.Signup.Account.ID + + r.logger.Info("Update tenant status with tenantID", "tenantID", tenantDef.Signup.Account.ID) + err = r.UpdateResourceStatus(r.tenantR) if err != nil { - return nil, err + return ctrl.Result{}, err } + + // requeue to have a new run with the updated tenant resource + return ctrl.Result{Requeue: true}, nil + } + + r.logger.Info("Tenant already exists", "TenantId", tenantDef.Signup.Account.ID) + // Check tenant desired state matches current state + err = r.syncTenant(tenantDef) + if err != nil { + return ctrl.Result{}, err } - return tenantDef, nil + return ctrl.Result{}, nil } func (r *TenantInternalReconciler) syncTenant(tenantDef *porta_client_pkg.Tenant) error { @@ -126,41 +142,82 @@ func (r *TenantInternalReconciler) syncTenant(tenantDef *porta_client_pkg.Tenant // This method makes sure admin user: // * is active // * user's attributes will be updated if required -func (r *TenantInternalReconciler) reconcileAdminUser(tenantDef *porta_client_pkg.Tenant) (*porta_client_pkg.User, error) { - adminUserDef, err := r.fetchAdminUser(tenantDef) +func (r *TenantInternalReconciler) reconcileAdminUser() error { + tenantDef, err := controllerhelper.FetchTenant(r.tenantR.Status.TenantId, r.portaClient) if err != nil { - return nil, err + return err + } + + if tenantDef == nil { + return fmt.Errorf("tenant with ID %d not found", r.tenantR.Status.TenantId) + } + + var adminUserDef *porta_client_pkg.User + if r.tenantR.Status.AdminId == 0 { + // UserID not in status field + adminUserDef, err = r.findAdminUser(tenantDef) + if err != nil { + return err + } + + r.tenantR.Status.AdminId = adminUserDef.ID + + r.logger.Info("Update tenant status with adminID", "adminID", adminUserDef.ID) + err = r.UpdateResourceStatus(r.tenantR) + if err != nil { + return err + } + + } else { + adminUserDef, err = r.portaClient.ReadUser(tenantDef.Signup.Account.ID, r.tenantR.Status.AdminId) + if err != nil { + return err + } } err = r.syncAdminUser(tenantDef, adminUserDef) if err != nil { - return nil, err + return err } - return adminUserDef, nil + return nil } // This method makes sure secret with tenant's access_token exists func (r *TenantInternalReconciler) reconcileAccessTokenSecret(tenantDef *porta_client_pkg.Tenant) error { - tenantProviderKeySecretNN := types.NamespacedName{ - Name: r.tenantR.Spec.TenantSecretRef.Name, - Namespace: r.tenantR.Spec.TenantSecretRef.Namespace, - } - tenantProviderKeySecret, err := r.findAccessTokenSecret(tenantProviderKeySecretNN) + adminURL, err := controllerhelper.URLFromDomain(tenantDef.Signup.Account.AdminDomain) if err != nil { return err } - if tenantProviderKeySecret == nil { - err = r.createTenantProviderKeySecret(tenantDef, tenantProviderKeySecretNN) - if err != nil { - return err - } - } else { - r.logger.Info("Admin user access token secret already exists", - "Secret NS", tenantProviderKeySecretNN.Namespace, "Secret name", tenantProviderKeySecretNN.Name) + desiredSecret := &v1.Secret{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Secret", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: r.tenantR.Spec.TenantSecretRef.Namespace, + Name: r.tenantR.Spec.TenantSecretRef.Name, + Labels: map[string]string{"app": "3scale-operator"}, + }, + StringData: map[string]string{ + TenantAccessTokenSecretField: tenantDef.Signup.AccessToken.Value, + TenantAdminDomainKeySecretField: adminURL.String(), + }, + Type: v1.SecretTypeOpaque, } - return nil + + err = r.SetOwnerReference(r.tenantR, desiredSecret) + if err != nil { + return err + } + + tenantSecretMutator := reconcilers.DeploymentSecretMutator( + reconcilers.SecretReconcileField(TenantAccessTokenSecretField), + reconcilers.SecretReconcileField(TenantAdminDomainKeySecretField), + ) + + return r.ReconcileResource(&v1.Secret{}, desiredSecret, tenantSecretMutator) } // Create Tenant using porta client @@ -184,7 +241,7 @@ func (r *TenantInternalReconciler) getAdminPassword() (string, error) { // Get tenant admin password from secret reference tenantAdminSecret := &v1.Secret{} - err := r.k8sClient.Get(context.TODO(), + err := r.Client().Get(context.TODO(), types.NamespacedName{ Name: r.tenantR.Spec.PasswordCredentialsRef.Name, Namespace: r.tenantR.Namespace, @@ -205,17 +262,6 @@ func (r *TenantInternalReconciler) getAdminPassword() (string, error) { return bytes.NewBuffer(passwordByteArray).String(), err } -// -func (r *TenantInternalReconciler) fetchAdminUser(tenantDef *porta_client_pkg.Tenant) (*porta_client_pkg.User, error) { - if r.tenantR.Status.AdminId == 0 { - // UserID not in status field - return r.findAdminUser(tenantDef) - } - - // - return r.portaClient.ReadUser(tenantDef.Signup.Account.ID, r.tenantR.Status.AdminId) -} - func (r *TenantInternalReconciler) findAdminUser(tenantDef *porta_client_pkg.Tenant) (*porta_client_pkg.User, error) { // Only admin users // Any state @@ -237,6 +283,7 @@ func (r *TenantInternalReconciler) findAdminUser(tenantDef *porta_client_pkg.Ten "TenantId: %d. Admin Username: %s, Admin email: %s", tenantDef.Signup.Account.ID, r.tenantR.Spec.Username, r.tenantR.Spec.Email) } + func (r *TenantInternalReconciler) syncAdminUser(tenantDef *porta_client_pkg.Tenant, adminUser *porta_client_pkg.User) error { // If adminUser desired state is not current state, update if adminUser.State == "pending" { @@ -281,100 +328,3 @@ func (r *TenantInternalReconciler) activateAdminUser(tenantDef *porta_client_pkg r.logger.Info("Activating pending admin user", "Account ID", tenantDef.Signup.Account.ID, "ID", adminUser.ID) return r.portaClient.ActivateUser(tenantDef.Signup.Account.ID, adminUser.ID) } - -func (r *TenantInternalReconciler) findAccessTokenSecret(nn types.NamespacedName) (*v1.Secret, error) { - adminAccessTokenSecret := &v1.Secret{} - - err := r.k8sClient.Get(context.TODO(), nn, adminAccessTokenSecret) - - if err != nil && errors.IsNotFound(err) { - return nil, nil - } - - if err != nil { - return nil, err - } - - return adminAccessTokenSecret, nil -} - -func (r *TenantInternalReconciler) createTenantProviderKeySecret(tenantDef *porta_client_pkg.Tenant, nn types.NamespacedName) error { - r.logger.Info("Creating admin access token secret", "Secret NS", nn.Namespace, "Secret name", nn.Name) - - tenantProviderKey, err := r.findTenantProviderKey(tenantDef) - if err != nil { - return err - } - - adminURL, err := controllerhelper.URLFromDomain(tenantDef.Signup.Account.AdminDomain) - if err != nil { - return err - } - - secret := &v1.Secret{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Secret", - }, - ObjectMeta: metav1.ObjectMeta{ - Namespace: nn.Namespace, - Name: nn.Name, - Labels: map[string]string{"app": "3scale-operator"}, - }, - StringData: map[string]string{ - TenantProviderKeySecretField: tenantProviderKey, - TenantAdminDomainKeySecretField: adminURL.String(), - }, - Type: v1.SecretTypeOpaque, - } - r.addOwnerRefToObject(secret, r.asOwner(r.tenantR)) - return r.k8sClient.Create(context.TODO(), secret) -} - -func (r *TenantInternalReconciler) findTenantProviderKey(tenantDef *porta_client_pkg.Tenant) (string, error) { - // Tenant Provider Key is available on provider application list - appList, err := r.portaClient.ListApplications(tenantDef.Signup.Account.ID) - if err != nil { - return "", err - } - - if len(appList.Applications) != 1 { - return "", fmt.Errorf("Unexpected application list. TenantId: %d", tenantDef.Signup.Account.ID) - } - - return appList.Applications[0].Application.UserKey, nil -} - -func (r *TenantInternalReconciler) getTenantStatus(tenantDef *porta_client_pkg.Tenant, adminUserDef *porta_client_pkg.User) *apiv1alpha1.TenantStatus { - return &apiv1alpha1.TenantStatus{ - TenantId: tenantDef.Signup.Account.ID, - AdminId: adminUserDef.ID, - } -} - -func (r *TenantInternalReconciler) updateTenantStatus(tenantStatus *apiv1alpha1.TenantStatus) error { - // don't update the status if there aren't any changes. - if reflect.DeepEqual(r.tenantR.Status, *tenantStatus) { - return nil - } - r.logger.Info("update tenant status", "status", tenantStatus) - r.tenantR.Status = *tenantStatus - return r.k8sClient.Status().Update(context.TODO(), r.tenantR) -} - -// addOwnerRefToObject appends the desired OwnerReference to the object -func (r *TenantInternalReconciler) addOwnerRefToObject(o metav1.Object, ref metav1.OwnerReference) { - o.SetOwnerReferences(append(o.GetOwnerReferences(), ref)) -} - -// asOwner returns an owner reference set as the tenant CR -func (r *TenantInternalReconciler) asOwner(t *apiv1alpha1.Tenant) metav1.OwnerReference { - trueVar := true - return metav1.OwnerReference{ - APIVersion: apiv1alpha1.GroupVersion.String(), - Kind: apiv1alpha1.TenantKind, - Name: t.Name, - UID: t.UID, - Controller: &trueVar, - } -} diff --git a/main.go b/main.go index c3364f5d1..4d73a68e3 100644 --- a/main.go +++ b/main.go @@ -165,11 +165,17 @@ func main() { os.Exit(1) } + discoveryClientTenant, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig()) + if err != nil { + setupLog.Error(err, "unable to create discovery client") + os.Exit(1) + } if err = (&capabilitiescontroller.TenantReconciler{ - Client: mgr.GetClient(), - Log: ctrl.Log.WithName("controllers").WithName("Tenant"), - Scheme: mgr.GetScheme(), - EventRecorder: mgr.GetEventRecorderFor("Tenant"), + BaseReconciler: reconcilers.NewBaseReconciler( + context.Background(), mgr.GetClient(), mgr.GetScheme(), mgr.GetAPIReader(), + ctrl.Log.WithName("controllers").WithName("Tenant"), + discoveryClientTenant, + mgr.GetEventRecorderFor("Tenant")), }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "Tenant") os.Exit(1) diff --git a/pkg/controller/helper/finalizers.go b/pkg/controller/helper/finalizers.go deleted file mode 100644 index f01d85802..000000000 --- a/pkg/controller/helper/finalizers.go +++ /dev/null @@ -1,31 +0,0 @@ -package helper - -import ( - "context" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" -) - -/* -ReconcileFinalizers reconciles the finalizers, it requires -- object -- k8client -- finalizer -If the deletion timestamp is found, the finalizer will be removed. -If the deletion timestamp is not present, a finalizer will be reconciled -*/ -func ReconcileFinalizers(object controllerutil.Object, client client.Client, finalizer string) error { - var err error - if object.GetDeletionTimestamp() == nil { - controllerutil.AddFinalizer(object, finalizer) - err = client.Update(context.TODO(), object) - } else { - controllerutil.RemoveFinalizer(object, finalizer) - err = client.Update(context.TODO(), object) - } - if err != nil { - return err - } - - return nil -} diff --git a/pkg/controller/helper/tenant.go b/pkg/controller/helper/tenant.go index 387e868fa..dd184b847 100644 --- a/pkg/controller/helper/tenant.go +++ b/pkg/controller/helper/tenant.go @@ -1,23 +1,20 @@ package helper import ( - capabilitiesv1alpha1 "github.com/3scale/3scale-operator/apis/capabilities/v1alpha1" porta_client_pkg "github.com/3scale/3scale-porta-go-client/client" ) /* FetchTenant fetches tenant from 3scale -- tenant +- tenantID - portaClient */ -func FetchTenant(tenant *capabilitiesv1alpha1.Tenant, portaClient *porta_client_pkg.ThreeScaleClient) (*porta_client_pkg.Tenant, error) { - if tenant.Status.TenantId == 0 { - // tenantId not in status field - // Tenant has to be created +func FetchTenant(tenantID int64, portaClient *porta_client_pkg.ThreeScaleClient) (*porta_client_pkg.Tenant, error) { + if tenantID == 0 { return nil, nil } - tenantDef, err := portaClient.ShowTenant(tenant.Status.TenantId) + tenantDef, err := portaClient.ShowTenant(tenantID) if err != nil && porta_client_pkg.IsNotFound(err) { return nil, nil } else if err != nil { diff --git a/pkg/reconcilers/secret.go b/pkg/reconcilers/secret.go index 751356181..fb17c8990 100644 --- a/pkg/reconcilers/secret.go +++ b/pkg/reconcilers/secret.go @@ -37,27 +37,55 @@ func DefaultsOnlySecretMutator(existingObj, desiredObj common.KubernetesObject) return updated, nil } -func SecretReconcileField(desired, existing *v1.Secret, fieldName string) bool { - updated := false +// SecretMutateFn is a function which mutates the existing Secret into it's desired state. +type SecretMutateFn func(desired, existing *v1.Secret) bool - if existing.Data == nil { - existing.Data = map[string][]byte{} - } - if existing.StringData == nil { - existing.StringData = map[string]string{} +func DeploymentSecretMutator(opts ...SecretMutateFn) MutateFn { + return func(existingObj, desiredObj common.KubernetesObject) (bool, error) { + existing, ok := existingObj.(*v1.Secret) + if !ok { + return false, fmt.Errorf("%T is not a *v1.Secret", existingObj) + } + desired, ok := desiredObj.(*v1.Secret) + if !ok { + return false, fmt.Errorf("%T is not a *v1.Secret", desiredObj) + } + + update := false + + // Loop through each option + for _, opt := range opts { + tmpUpdate := opt(desired, existing) + update = update || tmpUpdate + } + + return update, nil } +} - valB, ok := existing.Data[fieldName] - if !ok { - existing.StringData[fieldName] = desired.StringData[fieldName] - updated = true - } else { - valStr := string(valB) - if desired.StringData[fieldName] != valStr { - // should merge existing key in Data struct +func SecretReconcileField(fieldName string) func(desired, existing *v1.Secret) bool { + return func(desired, existing *v1.Secret) bool { + updated := false + + if existing.Data == nil { + existing.Data = map[string][]byte{} + } + if existing.StringData == nil { + existing.StringData = map[string]string{} + } + + valB, ok := existing.Data[fieldName] + if !ok { existing.StringData[fieldName] = desired.StringData[fieldName] updated = true + } else { + valStr := string(valB) + if desired.StringData[fieldName] != valStr { + // should merge existing key in Data struct + existing.StringData[fieldName] = desired.StringData[fieldName] + updated = true + } } + return updated } - return updated }