diff --git a/config/hub/rbac/role.yaml b/config/hub/rbac/role.yaml index ed6212341..6fc67d7d8 100644 --- a/config/hub/rbac/role.yaml +++ b/config/hub/rbac/role.yaml @@ -88,6 +88,20 @@ rules: - patch - update - watch +- apiGroups: + - ramendr.openshift.io + resources: + - drpolicies/finalizers + verbs: + - update +- apiGroups: + - ramendr.openshift.io + resources: + - drpolicies/status + verbs: + - get + - patch + - update - apiGroups: - view.open-cluster-management.io resources: diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 9311875a3..0244e97c1 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -118,6 +118,20 @@ rules: - patch - update - watch +- apiGroups: + - ramendr.openshift.io + resources: + - drpolicies/finalizers + verbs: + - update +- apiGroups: + - ramendr.openshift.io + resources: + - drpolicies/status + verbs: + - get + - patch + - update - apiGroups: - ramendr.openshift.io resources: diff --git a/controllers/drplacementcontrol_controller.go b/controllers/drplacementcontrol_controller.go index e85a4af14..cfd3beca0 100644 --- a/controllers/drplacementcontrol_controller.go +++ b/controllers/drplacementcontrol_controller.go @@ -482,7 +482,7 @@ func (r *DRPlacementControlReconciler) finalizeDRPC(ctx context.Context, drpc *r clustersToClean = append(clustersToClean, drpc.Spec.FailoverCluster) } - // delete manifestworks (VRG and Roles) + // delete manifestworks (VRG) for idx := range clustersToClean { err := mwu.DeleteManifestWorksForCluster(clustersToClean[idx]) if err != nil { @@ -797,7 +797,7 @@ func (d *DRPCInstance) runInitialDeployment() (bool, error) { d.setDRState(rmn.Deploying) // Create VRG first, to leverage user PlacementRule decision to skip placement and move to cleanup - err := d.createVRGAndRolesManifestWorks(homeCluster) + err := d.createVRGManifestWork(homeCluster) if err != nil { return false, err } @@ -1122,7 +1122,7 @@ func (d *DRPCInstance) createManifestWorks(targetCluster string) (bool, error) { if err != nil { if errors.IsNotFound(err) { // Create VRG first, to leverage user PlacementRule decision to skip placement and move to cleanup - err := d.createVRGAndRolesManifestWorks(targetCluster) + err := d.createVRGManifestWork(targetCluster) if err != nil { return !created, err } @@ -1346,17 +1346,10 @@ func (d *DRPCInstance) updateUserPlacementRuleStatus(status plrv1.PlacementRuleS return nil } -func (d *DRPCInstance) createVRGAndRolesManifestWorks(homeCluster string) error { - d.log.Info("Creating VRG/Roles ManifestWorks", +func (d *DRPCInstance) createVRGManifestWork(homeCluster string) error { + d.log.Info("Creating VRG ManifestWork", "Last State", d.getLastDRState(), "cluster", homeCluster) - if err := d.mwu.CreateOrUpdateVRGRolesManifestWork(homeCluster); err != nil { - d.log.Error(err, "failed to create or update VolumeReplicationGroup Roles manifest") - - return fmt.Errorf("failed to create or update VolumeReplicationGroup Roles manifest in namespace %s (%w)", - homeCluster, err) - } - if err := d.mwu.CreateOrUpdateVRGManifestWork( d.instance.Name, d.instance.Namespace, homeCluster, d.drPolicy, diff --git a/controllers/drplacementcontrol_controller_test.go b/controllers/drplacementcontrol_controller_test.go index 88c5fb60f..740134353 100644 --- a/controllers/drplacementcontrol_controller_test.go +++ b/controllers/drplacementcontrol_controller_test.go @@ -87,6 +87,25 @@ var ( } schedulingInterval = "1h" + + drPolicy = &rmn.DRPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Name: DRPolicyName, + }, + Spec: rmn.DRPolicySpec{ + DRClusterSet: []rmn.ManagedCluster{ + { + Name: EastManagedCluster, + S3ProfileName: "fakeS3Profile", + }, + { + Name: WestManagedCluster, + S3ProfileName: "fakeS3Profile", + }, + }, + SchedulingInterval: schedulingInterval, + }, + } ) var drstate string @@ -439,22 +458,15 @@ func createManagedClusters() { } } -func createDRPolicy(name, namespace string, drClusterSet []rmn.ManagedCluster) { - drPolicy := &rmn.DRPolicy{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: rmn.DRPolicySpec{ - DRClusterSet: drClusterSet, - SchedulingInterval: schedulingInterval, - }, - } - +func createDRPolicy() { err := k8sClient.Create(context.TODO(), drPolicy) Expect(err).NotTo(HaveOccurred()) } +func deleteDRPolicy() { + Expect(k8sClient.Delete(context.TODO(), drPolicy)).To(Succeed()) +} + func updateManifestWorkStatus(clusterNamespace, mwType, workType string) { manifestLookupKey := types.NamespacedName{ Name: rmnutil.ManifestWorkName(DRPCName, DRPCNamespaceName, mwType), @@ -529,17 +541,7 @@ func InitialDeployment(namespace, placementName, homeCluster string) (*plrv1.Pla createNamespaces() createManagedClusters() - createDRPolicy(DRPolicyName, DRPCNamespaceName, - []rmn.ManagedCluster{ - { - Name: EastManagedCluster, - S3ProfileName: "fakeS3Profile", - }, - { - Name: WestManagedCluster, - S3ProfileName: "fakeS3Profile", - }, - }) + createDRPolicy() placementRule := createPlacementRule(placementName, namespace) drpc := createDRPC(DRPCName, DRPCNamespaceName) @@ -549,7 +551,7 @@ func InitialDeployment(namespace, placementName, homeCluster string) (*plrv1.Pla func verifyVRGManifestWorkCreatedAsPrimary(managedCluster string) { vrgManifestLookupKey := types.NamespacedName{ - Name: "ramendr-vrg-roles", + Name: rmnutil.ClusterRolesManifestWorkName, Namespace: managedCluster, } createdVRGRolesManifest := &ocmworkv1.ManifestWork{} @@ -765,7 +767,7 @@ var _ = Describe("DRPlacementControl Reconciler", func() { recoverToFailoverCluster(drpc, userPlacementRule) Expect(getManifestWorkCount(WestManagedCluster)).Should(Equal(2)) // MW for VRG+ROLES - Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MWs + Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MW val, err := rmnutil.GetMetricValueSingle("ramen_failover_time", dto.MetricType_GAUGE) Expect(err).NotTo(HaveOccurred()) @@ -796,7 +798,7 @@ var _ = Describe("DRPlacementControl Reconciler", func() { recoverToFailoverCluster(drpc, userPlacementRule) Expect(getManifestWorkCount(WestManagedCluster)).Should(Equal(2)) // MW for VRG+ROLES - Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MWs + Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MW drpc := getLatestDRPC(DRPCName, DRPCNamespaceName) // At this point expect the DRPC status condition to have 2 types @@ -814,7 +816,7 @@ var _ = Describe("DRPlacementControl Reconciler", func() { relocateToPreferredCluster(drpc, userPlacementRule) Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(2)) // MWs for VRG+ROLES - Expect(getManifestWorkCount(WestManagedCluster)).Should(Equal(1)) // Roles MWs + Expect(getManifestWorkCount(WestManagedCluster)).Should(Equal(1)) // Roles MW drpc = getLatestDRPC(DRPCName, DRPCNamespaceName) // At this point expect the DRPC status condition to have 2 types @@ -831,7 +833,8 @@ var _ = Describe("DRPlacementControl Reconciler", func() { By("\n\n*** DELETE DRPC ***\n\n") deleteDRPC() waitForCompletion("deleted") - Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MWs + Expect(getManifestWorkCount(EastManagedCluster)).Should(Equal(1)) // Roles MW + deleteDRPolicy() }) }) }) diff --git a/controllers/drpolicy_controller.go b/controllers/drpolicy_controller.go new file mode 100644 index 000000000..dc93eceab --- /dev/null +++ b/controllers/drpolicy_controller.go @@ -0,0 +1,124 @@ +/* +Copyright 2021 The RamenDR authors. + +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 controllers + +import ( + "context" + "fmt" + + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + ramen "github.com/ramendr/ramen/api/v1alpha1" + "github.com/ramendr/ramen/controllers/util" +) + +// DRPolicyReconciler reconciles a DRPolicy object +type DRPolicyReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=drpolicies,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=drpolicies/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=drpolicies/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the DRPolicy object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.9.2/pkg/reconcile +func (r *DRPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := ctrl.Log.WithName("controllers").WithName("drpolicy").WithValues("name", req.NamespacedName.Name) + log.Info("reconcile enter") + + defer log.Info("reconcile exit") + + drpolicy := &ramen.DRPolicy{} + if err := r.Client.Get(ctx, req.NamespacedName, drpolicy); err != nil { + return ctrl.Result{}, client.IgnoreNotFound(fmt.Errorf("get: %w", err)) + } + + manifestWorkUtil := util.MWUtil{Client: r.Client, Ctx: ctx, Log: log, InstName: "", InstNamespace: ""} + + switch drpolicy.ObjectMeta.DeletionTimestamp.IsZero() { + case true: + log.Info("create/update") + + if err := finalizerAdd(ctx, drpolicy, r.Client, log); err != nil { + return ctrl.Result{}, fmt.Errorf("finalizer add update: %w", err) + } + + if err := manifestWorkUtil.ClusterRolesCreate(drpolicy); err != nil { + return ctrl.Result{}, fmt.Errorf("cluster roles create: %w", err) + } + default: + log.Info("delete") + + if err := manifestWorkUtil.ClusterRolesDelete(drpolicy); err != nil { + return ctrl.Result{}, fmt.Errorf("cluster roles delete: %w", err) + } + + if err := finalizerRemove(ctx, drpolicy, r.Client, log); err != nil { + return ctrl.Result{}, fmt.Errorf("finalizer remove update: %w", err) + } + } + + return ctrl.Result{}, nil +} + +const finalizerName = "drpolicies.ramendr.openshift.io/ramen" + +func finalizerAdd(ctx context.Context, drpolicy *ramen.DRPolicy, client client.Client, log logr.Logger) error { + finalizerCount := len(drpolicy.ObjectMeta.Finalizers) + controllerutil.AddFinalizer(drpolicy, finalizerName) + + if len(drpolicy.ObjectMeta.Finalizers) != finalizerCount { + log.Info("finalizer add") + + return client.Update(ctx, drpolicy) + } + + return nil +} + +func finalizerRemove(ctx context.Context, drpolicy *ramen.DRPolicy, client client.Client, log logr.Logger) error { + finalizerCount := len(drpolicy.ObjectMeta.Finalizers) + controllerutil.RemoveFinalizer(drpolicy, finalizerName) + + if len(drpolicy.ObjectMeta.Finalizers) != finalizerCount { + log.Info("finalizer remove") + + return client.Update(ctx, drpolicy) + } + + return nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DRPolicyReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&ramen.DRPolicy{}). + Complete(r) +} diff --git a/controllers/drpolicy_controller_test.go b/controllers/drpolicy_controller_test.go new file mode 100644 index 000000000..f4061ac82 --- /dev/null +++ b/controllers/drpolicy_controller_test.go @@ -0,0 +1,107 @@ +package controllers_test + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/sets" + + ramen "github.com/ramendr/ramen/api/v1alpha1" + "github.com/ramendr/ramen/controllers/util" +) + +var _ = Describe("DrpolicyController", func() { + clusterNamesCurrent := &sets.String{} + clusterNames := func(drpolicy *ramen.DRPolicy) sets.String { + return sets.NewString(util.DrpolicyClusterNames(drpolicy)...) + } + clusterRolesExpect := func() { + Eventually( + func(g Gomega) { + clusterNames := sets.String{} + g.Expect(util.ClusterRolesList(context.TODO(), k8sClient, &clusterNames)).To(Succeed()) + fmt.Fprintf( + GinkgoWriter, + "expect: %v\nactual: %v\n", + *clusterNamesCurrent, + clusterNames, + ) + g.Expect(clusterNamesCurrent.Equal(clusterNames)).To(BeTrue()) + }, + 10, + 0.25, + ).Should(Succeed()) + } + drpolicyCreate := func(drpolicy *ramen.DRPolicy) { + for _, clusterName := range clusterNames(drpolicy).Difference(*clusterNamesCurrent).UnsortedList() { + Expect(k8sClient.Create( + context.TODO(), + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: clusterName}}, + )).To(Succeed()) + *clusterNamesCurrent = clusterNamesCurrent.Insert(clusterName) + } + Expect(k8sClient.Create(context.TODO(), drpolicy)).To(Succeed()) + clusterRolesExpect() + } + drpolicyDelete := func(drpolicy *ramen.DRPolicy, clusterNamesExpected sets.String) { + Expect(k8sClient.Delete(context.TODO(), drpolicy)).To(Succeed()) + for _, clusterName := range clusterNamesCurrent.Difference(clusterNamesExpected).UnsortedList() { + Expect(k8sClient.Delete( + context.TODO(), + &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: clusterName}}, + )).To(Succeed()) + *clusterNamesCurrent = clusterNamesCurrent.Delete(clusterName) + } + clusterRolesExpect() + } + clusters := [...]ramen.ManagedCluster{ + {Name: "cluster-a", S3ProfileName: ""}, + {Name: "cluster-b", S3ProfileName: ""}, + {Name: "cluster-c", S3ProfileName: ""}, + } + drpolicies := [...]ramen.DRPolicy{ + { + ObjectMeta: metav1.ObjectMeta{Name: "drpolicy0"}, + Spec: ramen.DRPolicySpec{DRClusterSet: clusters[0:2]}, + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "drpolicy1"}, + Spec: ramen.DRPolicySpec{DRClusterSet: clusters[1:3]}, + }, + } + clusterNamesNone := sets.String{} + When("a 1st drpolicy is created", func() { + It("should create a cluster roles manifest work for each cluster specified in a 1st drpolicy", func() { + drpolicyCreate(&drpolicies[0]) + }) + }) + When("TODO a 1st drpolicy is updated to add some clusters and remove some other clusters", func() { + It("should create a cluster roles manifest work for each cluster added and "+ + "delete a cluster roles manifest work for each cluster removed", func() { + }) + }) + When("a 2nd drpolicy is created specifying some clusters in a 1st drpolicy and some not", func() { + It("should create a cluster roles manifest work for each cluster specified in a 2nd drpolicy but not a 1st drpolicy", + func() { + drpolicyCreate(&drpolicies[1]) + }, + ) + }) + When("a 1st drpolicy is deleted", func() { + It("should delete a cluster roles manifest work for each cluster specified in a 1st drpolicy but not a 2nd drpolicy", + func() { + drpolicyDelete(&drpolicies[0], clusterNames(&drpolicies[1])) + }, + ) + }) + When("a 2nd drpolicy is deleted", func() { + It("should delete a cluster roles manifest work for each cluster specified in a 2nd drpolicy", func() { + drpolicyDelete(&drpolicies[1], clusterNamesNone) + }) + }) +}) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index bfbd98ae8..a3e474976 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -104,6 +104,11 @@ var _ = BeforeSuite(func() { }) Expect(err).ToNot(HaveOccurred()) + Expect((&ramencontrollers.DRPolicyReconciler{ + Client: k8sManager.GetClient(), + Scheme: k8sManager.GetScheme(), + }).SetupWithManager(k8sManager)).To(Succeed()) + err = (&ramencontrollers.VolumeReplicationGroupReconciler{ Client: k8sManager.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("VolumeReplicationGroup"), diff --git a/controllers/util/drpolicy_util.go b/controllers/util/drpolicy_util.go index 257f6c98a..50683c884 100644 --- a/controllers/util/drpolicy_util.go +++ b/controllers/util/drpolicy_util.go @@ -20,6 +20,15 @@ import ( rmn "github.com/ramendr/ramen/api/v1alpha1" ) +func DrpolicyClusterNames(drpolicy *rmn.DRPolicy) []string { + clusterNames := make([]string, len(drpolicy.Spec.DRClusterSet)) + for i := range drpolicy.Spec.DRClusterSet { + clusterNames[i] = drpolicy.Spec.DRClusterSet[i].Name + } + + return clusterNames +} + // Return a list of unique S3 profiles to upload the relevant cluster state of // the given home cluster func s3UploadProfileList(drPolicy rmn.DRPolicy, homeCluster string) (s3ProfileList []string) { diff --git a/controllers/util/mw_util.go b/controllers/util/mw_util.go index c9d0ca843..4fd44e8ed 100644 --- a/controllers/util/mw_util.go +++ b/controllers/util/mw_util.go @@ -21,6 +21,7 @@ import ( "encoding/json" "fmt" "reflect" + "sync" "github.com/go-logr/logr" ocmworkv1 "github.com/open-cluster-management/api/work/v1" @@ -30,6 +31,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" dto "github.com/prometheus/client_model/go" @@ -39,19 +41,18 @@ import ( ) const ( + ClusterRolesManifestWorkName = "ramendr-roles" + // ManifestWorkNameFormat is a formated a string used to generate the manifest name // The format is name-namespace-type-mw where: // - name is the DRPC name // - namespace is the DRPC namespace - // - type is either "vrg" or "roles" + // - type is "vrg" ManifestWorkNameFormat string = "%s-%s-%s-mw" // ManifestWork VRG Type MWTypeVRG string = "vrg" - // ManifestWork Roles Type - MWTypeRoles string = "roles" - // Annotations for MW and PlacementRule DRPCNameAnnotation = "drplacementcontrol.ramendr.openshift.io/drpc-name" DRPCNamespaceAnnotation = "drplacementcontrol.ramendr.openshift.io/drpc-namespace" @@ -173,74 +174,68 @@ func (mwu *MWUtil) generateVRGManifest( }) } -func (mwu *MWUtil) CreateOrUpdateVRGRolesManifestWork(namespace string) error { - manifestWork, err := mwu.generateVRGRolesManifestWork(namespace) - if err != nil { - return err +func ClusterRolesList(ctx context.Context, client client.Client, clusterNames *sets.String) error { + manifestworks := &ocmworkv1.ManifestWorkList{} + if err := client.List(ctx, manifestworks); err != nil { + return fmt.Errorf("manifestworks list: %w", err) } - return mwu.createOrUpdateManifestWork(manifestWork, namespace) + for i := range manifestworks.Items { + manifestwork := &manifestworks.Items[i] + if manifestwork.ObjectMeta.Name == ClusterRolesManifestWorkName { + *clusterNames = clusterNames.Insert(manifestwork.ObjectMeta.Namespace) + } + } + + return nil } -func (mwu *MWUtil) generateVRGRolesManifestWork(namespace string) (*ocmworkv1.ManifestWork, error) { - vrgClusterRole, err := mwu.generateVRGClusterRoleManifest() - if err != nil { - mwu.Log.Error(err, "failed to generate VolumeReplicationGroup ClusterRole manifest") +var clusterRolesMutex sync.Mutex - return nil, err +func (mwu *MWUtil) ClusterRolesCreate(drpolicy *rmn.DRPolicy) error { + clusterRolesMutex.Lock() + defer clusterRolesMutex.Unlock() + + for _, clusterName := range DrpolicyClusterNames(drpolicy) { + if err := mwu.createOrUpdateClusterRolesManifestWork(clusterName); err != nil { + return err + } } - vrgClusterRoleBinding, err := mwu.generateVRGClusterRoleBindingManifest() - if err != nil { - mwu.Log.Error(err, "failed to generate VolumeReplicationGroup ClusterRoleBinding manifest") + return nil +} - return nil, err - } +func (mwu *MWUtil) ClusterRolesDelete(drpolicy *rmn.DRPolicy) error { + drpolicies := rmn.DRPolicyList{} + clusterNames := sets.String{} - manifests := []ocmworkv1.Manifest{*vrgClusterRole, *vrgClusterRoleBinding} + clusterRolesMutex.Lock() + defer clusterRolesMutex.Unlock() - return mwu.newManifestWork( - "ramendr-vrg-roles", - namespace, - map[string]string{}, - manifests), nil -} + if err := mwu.Client.List(mwu.Ctx, &drpolicies); err != nil { + return fmt.Errorf("drpolicies list: %w", err) + } -func (mwu *MWUtil) generateVRGClusterRoleManifest() (*ocmworkv1.Manifest, error) { - return mwu.GenerateManifest(&rbacv1.ClusterRole{ - TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit"}, - Rules: []rbacv1.PolicyRule{ - { - APIGroups: []string{"ramendr.openshift.io"}, - Resources: []string{"volumereplicationgroups"}, - Verbs: []string{"create", "get", "list", "update", "delete"}, - }, - }, - }) -} + for i := range drpolicies.Items { + drpolicy1 := &drpolicies.Items[i] + if drpolicy1.ObjectMeta.Name != drpolicy.ObjectMeta.Name { + clusterNames = clusterNames.Insert(DrpolicyClusterNames(drpolicy1)...) + } + } -func (mwu *MWUtil) generateVRGClusterRoleBindingManifest() (*ocmworkv1.Manifest, error) { - return mwu.GenerateManifest(&rbacv1.ClusterRoleBinding{ - TypeMeta: metav1.TypeMeta{Kind: "ClusterRoleBinding", APIVersion: "rbac.authorization.k8s.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit"}, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: "klusterlet-work-sa", - Namespace: "open-cluster-management-agent", - }, - }, - RoleRef: rbacv1.RoleRef{ - APIGroup: "rbac.authorization.k8s.io", - Kind: "ClusterRole", - Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit", - }, - }) + for _, clusterName := range DrpolicyClusterNames(drpolicy) { + if !clusterNames.Has(clusterName) { + if err := mwu.deleteManifestWork(ClusterRolesManifestWorkName, clusterName); err != nil { + return err + } + } + } + + return nil } -func (mwu *MWUtil) CreateOrUpdatePVRolesManifestWork(namespace string) error { - manifestWork, err := mwu.generatePVRolesManifestWork(namespace) +func (mwu *MWUtil) createOrUpdateClusterRolesManifestWork(namespace string) error { + manifestWork, err := mwu.generateClusterRolesManifestWork(namespace) if err != nil { return err } @@ -248,48 +243,51 @@ func (mwu *MWUtil) CreateOrUpdatePVRolesManifestWork(namespace string) error { return mwu.createOrUpdateManifestWork(manifestWork, namespace) } -func (mwu *MWUtil) generatePVRolesManifestWork(namespace string) (*ocmworkv1.ManifestWork, error) { - pvClusterRole, err := mwu.generatePVClusterRoleManifest() +func (mwu *MWUtil) generateClusterRolesManifestWork(namespace string) (*ocmworkv1.ManifestWork, error) { + vrgClusterRole, err := mwu.generateVRGClusterRoleManifest() if err != nil { - mwu.Log.Error(err, "failed to generate PersistentVolume ClusterRole manifest") + mwu.Log.Error(err, "failed to generate VolumeReplicationGroup ClusterRole manifest") return nil, err } - pvClusterRoleBinding, err := mwu.generatePVClusterRoleBindingManifest() + vrgClusterRoleBinding, err := mwu.generateVRGClusterRoleBindingManifest() if err != nil { - mwu.Log.Error(err, "failed to generate PersistentVolume ClusterRoleBinding manifest") + mwu.Log.Error(err, "failed to generate VolumeReplicationGroup ClusterRoleBinding manifest") return nil, err } - manifests := []ocmworkv1.Manifest{*pvClusterRole, *pvClusterRoleBinding} + manifests := []ocmworkv1.Manifest{ + *vrgClusterRole, + *vrgClusterRoleBinding, + } return mwu.newManifestWork( - "ramendr-pv-roles", + ClusterRolesManifestWorkName, namespace, map[string]string{}, manifests), nil } -func (mwu *MWUtil) generatePVClusterRoleManifest() (*ocmworkv1.Manifest, error) { +func (mwu *MWUtil) generateVRGClusterRoleManifest() (*ocmworkv1.Manifest, error) { return mwu.GenerateManifest(&rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRole", APIVersion: "rbac.authorization.k8s.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:pv-edit"}, + ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit"}, Rules: []rbacv1.PolicyRule{ { - APIGroups: []string{""}, - Resources: []string{"persistentvolumes"}, + APIGroups: []string{"ramendr.openshift.io"}, + Resources: []string{"volumereplicationgroups"}, Verbs: []string{"create", "get", "list", "update", "delete"}, }, }, }) } -func (mwu *MWUtil) generatePVClusterRoleBindingManifest() (*ocmworkv1.Manifest, error) { +func (mwu *MWUtil) generateVRGClusterRoleBindingManifest() (*ocmworkv1.Manifest, error) { return mwu.GenerateManifest(&rbacv1.ClusterRoleBinding{ TypeMeta: metav1.TypeMeta{Kind: "ClusterRoleBinding", APIVersion: "rbac.authorization.k8s.io/v1"}, - ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:pv-edit"}, + ObjectMeta: metav1.ObjectMeta{Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit"}, Subjects: []rbacv1.Subject{ { Kind: "ServiceAccount", @@ -300,7 +298,7 @@ func (mwu *MWUtil) generatePVClusterRoleBindingManifest() (*ocmworkv1.Manifest, RoleRef: rbacv1.RoleRef{ APIGroup: "rbac.authorization.k8s.io", Kind: "ClusterRole", - Name: "open-cluster-management:klusterlet-work-sa:agent:pv-edit", + Name: "open-cluster-management:klusterlet-work-sa:agent:volrepgroup-edit", }, }) } diff --git a/go.mod b/go.mod index d9ef40082..e6c710305 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/ghodss/yaml v1.0.1-0.20190212211648-25d852aebe32 github.com/go-logr/logr v0.4.0 github.com/onsi/ginkgo v1.16.4 - github.com/onsi/gomega v1.14.0 + github.com/onsi/gomega v1.15.0 github.com/open-cluster-management/api v0.0.0-20210527013639-a6845f2ebcb1 github.com/open-cluster-management/multicloud-operators-foundation v0.0.0-20210722145122-534b4232f1c9 github.com/open-cluster-management/multicloud-operators-placementrule v1.2.2-2-20201130-98cfd.0.20210722134723-73b5f55a3813 diff --git a/go.sum b/go.sum index 0a4df50df..9dbbf47de 100644 --- a/go.sum +++ b/go.sum @@ -1435,8 +1435,8 @@ github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoT github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48= -github.com/onsi/gomega v1.14.0 h1:ep6kpPVwmr/nTbklSx2nrLNSIO62DoYAhnPNIMhK8gI= -github.com/onsi/gomega v1.14.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= +github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= +github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/open-cluster-management/addon-framework v0.0.0-20210621074027-a81f712c10c2/go.mod h1:mcpd6pc0j/L+WLFwV2MXHVMr+86ri2iUdTK2M8RHJ7U= github.com/open-cluster-management/api v0.0.0-20200903203421-64b667f5455c/go.mod h1:F1hDJHtWuV7BAUtfL4XRS9GZjUpksleLgEcisNXvQEw= diff --git a/main.go b/main.go index 01bb92c4e..717baa3e5 100644 --- a/main.go +++ b/main.go @@ -105,6 +105,14 @@ func newManager() (ctrl.Manager, error) { func setupReconcilers(mgr ctrl.Manager) { if controllerType == ramendrv1alpha1.DRHub { + if err := (&controllers.DRPolicyReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "DRPolicy") + os.Exit(1) + } + drpcReconciler := (&controllers.DRPlacementControlReconciler{ Client: mgr.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("DRPlacementControl"),