diff --git a/cmd/controller/app/server.go b/cmd/controller/app/server.go index 0d25c8b2b..495d1b1a5 100644 --- a/cmd/controller/app/server.go +++ b/cmd/controller/app/server.go @@ -3,11 +3,11 @@ package app import ( - "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clustersetmapper" - "github.com/open-cluster-management/multicloud-operators-foundation/pkg/helpers" "io/ioutil" "path" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clustersetmapper" + clusterv1 "github.com/open-cluster-management/api/cluster/v1" clusterv1alaph1 "github.com/open-cluster-management/api/cluster/v1alpha1" "github.com/open-cluster-management/multicloud-operators-foundation/cmd/controller/app/options" @@ -18,8 +18,10 @@ import ( "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clusterinfo" "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clusterrbac" "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clusterrole" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/clusterset/clusterrolebinding" "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/gc" "github.com/open-cluster-management/multicloud-operators-foundation/pkg/controllers/inventory" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/helpers" hivev1 "github.com/openshift/hive/pkg/apis/hive/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes" @@ -45,6 +47,9 @@ func init() { } func Run(o *options.ControllerRunOptions, stopCh <-chan struct{}) error { + + clustersetToSubject := helpers.NewClustersetSubjectsMapper() + kubeConfig, err := clientcmd.BuildConfigFromFlags("", o.KubeConfig) if err != nil { klog.Errorf("unable to get kube config: %v", err) @@ -105,6 +110,11 @@ func Run(o *options.ControllerRunOptions, stopCh <-chan struct{}) error { return err } + if err = clusterrolebinding.SetupWithManager(mgr, clustersetToSubject); err != nil { + klog.Errorf("unable to setup clusterrolebinding reconciler: %v", err) + return err + } + if o.EnableRBAC { if err = clusterrbac.SetupWithManager(mgr, kubeClient); err != nil { klog.Errorf("unable to setup clusterrbac reconciler: %v", err) diff --git a/deploy/foundation/hub/resources/clusterrole.yaml b/deploy/foundation/hub/resources/clusterrole.yaml index 4a6d9f3d0..11be37603 100644 --- a/deploy/foundation/hub/resources/clusterrole.yaml +++ b/deploy/foundation/hub/resources/clusterrole.yaml @@ -18,7 +18,7 @@ metadata: resources: ["roles", "rolebindings"] verbs: ["get", "list", "watch", "create", "update", "delete"] - apiGroups: ["rbac.authorization.k8s.io"] - resources: ["clusterroles"] + resources: ["clusterroles","clusterrolebindings"] verbs: ["get", "list", "watch", "create", "update", "delete", "escalate"] - apiGroups: ["authorization.k8s.io"] resources: ["subjectaccessreviews"] diff --git a/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller.go b/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller.go new file mode 100644 index 000000000..39bc4adb0 --- /dev/null +++ b/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller.go @@ -0,0 +1,173 @@ +package clusterrolebinding + +import ( + "context" + + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/helpers" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/utils" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/klog" + ctrl "sigs.k8s.io/controller-runtime" + "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 ( + ClustersetFinalizerName string = "open-cluster-management.io/clusterset" + ClusterSetLabel string = "clusterset.cluster.open-cluster-management.io" +) + +//This Controller will generate a Clusterset to Subjects map, and this map will be used to sync +// clusterset related clusterrolebinding. +type Reconciler struct { + client client.Client + scheme *runtime.Scheme + clusterroleToClusterset map[string]sets.String + clustersetToSubject *helpers.ClustersetSubjectsMapper +} + +func SetupWithManager(mgr manager.Manager, clustersetToSubject *helpers.ClustersetSubjectsMapper) error { + if err := add(mgr, newReconciler(mgr, clustersetToSubject)); err != nil { + klog.Errorf("Failed to create clustersetToSubject controller, %v", err) + return err + } + return nil +} + +// newReconciler returns a new reconcile.Reconciler +func newReconciler(mgr manager.Manager, clustersetToSubject *helpers.ClustersetSubjectsMapper) reconcile.Reconciler { + var clusterroleToClusterset = make(map[string]sets.String) + return &Reconciler{ + client: mgr.GetClient(), + scheme: mgr.GetScheme(), + clustersetToSubject: clustersetToSubject, + clusterroleToClusterset: clusterroleToClusterset, + } +} + +// add adds a new Controller to mgr with r as the reconcile.Reconciler +func add(mgr manager.Manager, r reconcile.Reconciler) error { + // Create a new controller + c, err := controller.New("clusterrolebinding-controller", mgr, controller.Options{Reconciler: r}) + if err != nil { + return err + } + err = c.Watch(&source.Kind{Type: &rbacv1.ClusterRole{}}, &handler.EnqueueRequestForObject{}) + if err != nil { + return err + } + err = c.Watch( + &source.Kind{Type: &rbacv1.ClusterRoleBinding{}}, + &handler.EnqueueRequestsFromMapFunc{ + ToRequests: handler.ToRequestsFunc(func(a handler.MapObject) []reconcile.Request { + clusterrolebinding, ok := a.Object.(*rbacv1.ClusterRoleBinding) + if !ok { + // not a clusterrolebinding, returning empty + klog.Error("Clusterrolebinding handler received non-SyncSet object") + return []reconcile.Request{} + } + var requests []reconcile.Request + requests = append(requests, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: clusterrolebinding.RoleRef.Name, + }, + }) + return requests + }), + }) + if err != nil { + return err + } + + return nil +} + +func (r *Reconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) { + ctx := context.Background() + clusterrole := &rbacv1.ClusterRole{} + err := r.client.Get(ctx, req.NamespacedName, clusterrole) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + //generate clusterrole's all clustersets + clustersetsInRule := utils.GetClustersetInRules(clusterrole.Rules) + //Add Finalizer to clusterset related clusterrole + if clustersetsInRule.Len() != 0 && !utils.ContainsString(clusterrole.GetFinalizers(), ClustersetFinalizerName) { + klog.Infof("adding ClusterRoleBinding Finalizer to ClusterRole %v", clusterrole.Name) + clusterrole.ObjectMeta.Finalizers = append(clusterrole.ObjectMeta.Finalizers, ClustersetFinalizerName) + if err := r.client.Update(context.TODO(), clusterrole); err != nil { + klog.Warningf("will reconcile since failed to add finalizer to ClusterRole %v, %v", clusterrole.Name, err) + return reconcile.Result{}, err + } + } + //clusterrole is deleted or clusterrole is not related to any clusterset + if clustersetsInRule.Len() == 0 || !clusterrole.GetDeletionTimestamp().IsZero() { + // The object is being deleted + if utils.ContainsString(clusterrole.GetFinalizers(), ClustersetFinalizerName) { + klog.Infof("removing ClusterRoleBinding Finalizer in ClusterRole %v", clusterrole.Name) + clusterrole.ObjectMeta.Finalizers = utils.RemoveString(clusterrole.ObjectMeta.Finalizers, ClustersetFinalizerName) + if err := r.client.Update(context.TODO(), clusterrole); err != nil { + klog.Warningf("will reconcile since failed to remove Finalizer from ClusterRole %v, %v", clusterrole.Name, err) + return reconcile.Result{}, err + } + } + if _, ok := r.clusterroleToClusterset[clusterrole.Name]; !ok { + return ctrl.Result{}, nil + } + delete(r.clusterroleToClusterset, clusterrole.Name) + } else { + r.clusterroleToClusterset[clusterrole.Name] = clustersetsInRule + } + curClustersetToRoles := generateClustersetToClusterroles(r.clusterroleToClusterset) + var clustersetToSubjects = make(map[string][]rbacv1.Subject) + for curClusterset, curClusterRoles := range curClustersetToRoles { + var clustersetSubjects []rbacv1.Subject + for _, curClusterRole := range curClusterRoles { + subjects, err := r.getClusterroleSubject(ctx, curClusterRole) + if err != nil { + klog.Errorf("Failed to get clusterrole subject. clusterrole: %v, error:%v", curClusterRole, err) + continue + } + clustersetSubjects = utils.Mergesubjects(clustersetSubjects, subjects) + } + clustersetToSubjects[curClusterset] = clustersetSubjects + } + r.clustersetToSubject.SetMap(clustersetToSubjects) + return ctrl.Result{}, nil +} + +func (r *Reconciler) getClusterroleSubject(ctx context.Context, clusterroleName string) ([]rbacv1.Subject, error) { + var subjects []rbacv1.Subject + clusterrolebindinglist := &rbacv1.ClusterRoleBindingList{} + err := r.client.List(ctx, clusterrolebindinglist) + if err != nil { + return nil, err + } + + for _, clusterrolebinding := range clusterrolebindinglist.Items { + if _, ok := clusterrolebinding.Labels[ClusterSetLabel]; ok { + continue + } + if clusterrolebinding.RoleRef.APIGroup == rbacv1.GroupName && clusterrolebinding.RoleRef.Kind == "ClusterRole" && clusterrolebinding.RoleRef.Name == clusterroleName { + subjects = utils.Mergesubjects(subjects, clusterrolebinding.Subjects) + } + } + return subjects, nil +} + +func generateClustersetToClusterroles(clusterroleToClusterset map[string]sets.String) map[string][]string { + var clustersetToClusterroles = make(map[string][]string) + for currole, cursets := range clusterroleToClusterset { + for curset := range cursets { + clustersetToClusterroles[curset] = append(clustersetToClusterroles[curset], currole) + } + } + return clustersetToClusterroles +} diff --git a/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller_test.go b/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller_test.go new file mode 100644 index 000000000..76f36a85c --- /dev/null +++ b/pkg/controllers/clusterset/clusterrolebinding/clusterrolebinding_controller_test.go @@ -0,0 +1,250 @@ +package clusterrolebinding + +import ( + "testing" + + clusterv1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/helpers" + "github.com/open-cluster-management/multicloud-operators-foundation/pkg/utils" + rbacv1 "k8s.io/api/rbac/v1" + 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/fake" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +var ( + scheme = runtime.NewScheme() +) + +func newTestReconciler(clusterroleToClusterset map[string]sets.String, clustersetToSubject *helpers.ClustersetSubjectsMapper, roleobjs, rolebindingobjs []runtime.Object) *Reconciler { + objs := roleobjs + objs = append(objs, rolebindingobjs...) + + r := &Reconciler{ + client: fake.NewFakeClient(objs...), + scheme: scheme, + clusterroleToClusterset: clusterroleToClusterset, + clustersetToSubject: clustersetToSubject, + } + return r +} + +func generateClusterserSubjectMap() *helpers.ClustersetSubjectsMapper { + clusterserSubject := make(map[string][]rbacv1.Subject) + subjects1 := []rbacv1.Subject{ + {Kind: "k1", APIGroup: "a1", Name: "n1"}} + clusterserSubject["s1"] = subjects1 + clustersetToSubject := helpers.NewClustersetSubjectsMapper() + clustersetToSubject.SetMap(clusterserSubject) + return clustersetToSubject +} + +func TestReconcile(t *testing.T) { + clusterroleToClusterset := make(map[string]sets.String) + clustersetToSubject := helpers.NewClustersetSubjectsMapper() + + tests := []struct { + name string + clusterroleToClusterset map[string]sets.String + clustersetToSubject *helpers.ClustersetSubjectsMapper + clusterRoleObjs []runtime.Object + clusterRoleBindingObjs []runtime.Object + expectedMapperData map[string][]rbacv1.Subject + req reconcile.Request + }{ + { + name: "one set in clusterrole", + clusterroleToClusterset: clusterroleToClusterset, + clustersetToSubject: clustersetToSubject, + clusterRoleObjs: []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRole1", + }, + Rules: []rbacv1.PolicyRule{ + {Verbs: []string{"*"}, APIGroups: []string{"*"}, Resources: []string{"*"}, ResourceNames: []string{"*"}}, + }, + }, + }, + clusterRoleBindingObjs: []runtime.Object{ + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRolebinding1", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: "clusterRole1", + }, + Subjects: []rbacv1.Subject{ + {Kind: "k1", APIGroup: "a1", Name: "n1"}, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "clusterRole1", + }, + }, + expectedMapperData: map[string][]rbacv1.Subject{"*": {{Kind: "k1", APIGroup: "a1", Name: "n1"}}}, + }, + { + name: "two clusterrole", + clusterroleToClusterset: clusterroleToClusterset, + clustersetToSubject: clustersetToSubject, + clusterRoleObjs: []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRole1", + }, + Rules: []rbacv1.PolicyRule{ + {Verbs: []string{"create"}, APIGroups: []string{clusterv1alpha1.GroupName}, Resources: []string{"managedclustersets/bind"}, ResourceNames: []string{"s1", "s2"}}, + }, + }, + }, + clusterRoleBindingObjs: []runtime.Object{ + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRolebinding1", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: "clusterRole1", + }, + Subjects: []rbacv1.Subject{ + {Kind: "k1", APIGroup: "a1", Name: "n1"}, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "clusterRole1", + }, + }, + expectedMapperData: map[string][]rbacv1.Subject{"s1": {{Kind: "k1", APIGroup: "a1", Name: "n1"}}, "s2": {{Kind: "k1", APIGroup: "a1", Name: "n1"}}}, + }, + { + name: "update clusterrole", + clusterroleToClusterset: map[string]sets.String{ + "clusterRole1": sets.NewString("s1", "s3"), + }, + clustersetToSubject: generateClusterserSubjectMap(), + clusterRoleObjs: []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRole1", + }, + Rules: []rbacv1.PolicyRule{ + {Verbs: []string{"create"}, APIGroups: []string{clusterv1alpha1.GroupName}, Resources: []string{"managedclustersets/bind"}, ResourceNames: []string{"s1", "s2"}}, + }, + }, + }, + clusterRoleBindingObjs: []runtime.Object{ + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRolebinding1", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: "clusterRole1", + }, + Subjects: []rbacv1.Subject{ + {Kind: "k1", APIGroup: "a1", Name: "n1"}, + }, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "clusterRole1", + }, + }, + expectedMapperData: map[string][]rbacv1.Subject{"s1": {{Kind: "k1", APIGroup: "a1", Name: "n1"}}, "s2": {{Kind: "k1", APIGroup: "a1", Name: "n1"}}}, + }, + { + name: "delete clusterrolebinding", + clusterroleToClusterset: map[string]sets.String{ + "clusterRole1": sets.NewString("s1", "s3"), + }, + clustersetToSubject: generateClusterserSubjectMap(), + clusterRoleObjs: []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRole1", + }, + Rules: []rbacv1.PolicyRule{ + {Verbs: []string{"create"}, APIGroups: []string{clusterv1alpha1.GroupName}, Resources: []string{"managedclustersets/bind"}, ResourceNames: []string{"s1", "s2"}}, + }, + }, + }, + clusterRoleBindingObjs: []runtime.Object{}, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "clusterRole1", + }, + }, + expectedMapperData: map[string][]rbacv1.Subject{"s1": {}, "s2": {}}, + }, + { + name: "remove subjects", + clusterroleToClusterset: map[string]sets.String{ + "clusterRole1": sets.NewString("s1", "s3"), + }, + clustersetToSubject: generateClusterserSubjectMap(), + clusterRoleObjs: []runtime.Object{ + &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRole1", + }, + Rules: []rbacv1.PolicyRule{ + {Verbs: []string{"create"}, APIGroups: []string{clusterv1alpha1.GroupName}, Resources: []string{"managedclustersets/bind"}, ResourceNames: []string{"s1", "s2"}}, + }, + }, + }, + clusterRoleBindingObjs: []runtime.Object{ + &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clusterRolebinding1", + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: rbacv1.GroupName, + Kind: "ClusterRole", + Name: "clusterRole1", + }, + Subjects: []rbacv1.Subject{}, + }, + }, + req: reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: "clusterRole1", + }, + }, + expectedMapperData: map[string][]rbacv1.Subject{"s1": {}, "s2": {}}, + }, + } + + for _, test := range tests { + r := newTestReconciler(test.clusterroleToClusterset, test.clustersetToSubject, test.clusterRoleBindingObjs, test.clusterRoleObjs) + r.Reconcile(test.req) + validateResult(t, r, test.expectedMapperData) + } +} + +func validateResult(t *testing.T, r *Reconciler, expectedMapperData map[string][]rbacv1.Subject) { + mapperData := r.clustersetToSubject.GetMap() + if len(mapperData) != len(expectedMapperData) { + t.Errorf("Expect map is not same as result, return Map:%v, expect Map: %v", mapperData, expectedMapperData) + } + for clusterSet, subjects := range mapperData { + if _, ok := expectedMapperData[clusterSet]; !ok { + t.Errorf("Expect map is not same as result, return Map:%v, expect Map: %v", mapperData, expectedMapperData) + } + if !utils.EqualSubjects(expectedMapperData[clusterSet], subjects) { + t.Errorf("Expect map is not same as result, return Map:%v, expect Map: %v", mapperData, expectedMapperData) + } + } +} diff --git a/pkg/helpers/clustersetToSubjects.go b/pkg/helpers/clustersetToSubjects.go new file mode 100644 index 000000000..279583fce --- /dev/null +++ b/pkg/helpers/clustersetToSubjects.go @@ -0,0 +1,31 @@ +package helpers + +import ( + "sync" + + rbacv1 "k8s.io/api/rbac/v1" +) + +type ClustersetSubjectsMapper struct { + mutex sync.RWMutex + // mapping: ClusterSet - Subjects + clustersetToSubjects map[string][]rbacv1.Subject +} + +func NewClustersetSubjectsMapper() *ClustersetSubjectsMapper { + return &ClustersetSubjectsMapper{ + clustersetToSubjects: make(map[string][]rbacv1.Subject), + } +} + +func (c *ClustersetSubjectsMapper) SetMap(m map[string][]rbacv1.Subject) { + c.mutex.Lock() + defer c.mutex.Unlock() + c.clustersetToSubjects = m +} + +func (c *ClustersetSubjectsMapper) GetMap() map[string][]rbacv1.Subject { + c.mutex.RLock() + defer c.mutex.RUnlock() + return c.clustersetToSubjects +} diff --git a/pkg/utils/role.go b/pkg/utils/role.go new file mode 100644 index 000000000..5c7336a23 --- /dev/null +++ b/pkg/utils/role.go @@ -0,0 +1,69 @@ +package utils + +import ( + "reflect" + + clusterv1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +func Mergesubjects(subjects []rbacv1.Subject, cursubjects []rbacv1.Subject) []rbacv1.Subject { + var subjectmap = make(map[rbacv1.Subject]bool) + returnSubjects := subjects + for _, subject := range subjects { + subjectmap[subject] = true + } + for _, cursubject := range cursubjects { + if _, ok := subjectmap[cursubject]; !ok { + returnSubjects = append(returnSubjects, cursubject) + } + } + return returnSubjects +} + +func generateMapKey(subject rbacv1.Subject) string { + return subject.APIGroup + subject.Kind + subject.Name +} + +func GetClustersetInRules(rules []rbacv1.PolicyRule) sets.String { + clustersetNames := sets.NewString() + for _, rule := range rules { + if ContainsString(rule.APIGroups, "*") && ContainsString(rule.Resources, "*") && ContainsString(rule.Verbs, "*") { + clustersetNames.Insert("*") + } + if !ContainsString(rule.APIGroups, clusterv1alpha1.GroupName) { + continue + } + if !ContainsString(rule.Resources, "managedclustersets/bind") && !ContainsString(rule.Resources, "*") { + continue + } + + if !ContainsString(rule.Verbs, "create") && !ContainsString(rule.Verbs, "*") { + continue + } + for _, resourcename := range rule.ResourceNames { + if resourcename == "*" { + return sets.NewString("*") + } + clustersetNames.Insert(resourcename) + } + } + return clustersetNames +} + +func EqualSubjects(subjects1, subjects2 []rbacv1.Subject) bool { + if len(subjects1) != len(subjects2) { + return false + } + var subjectMap1 = make(map[rbacv1.Subject]bool) + for _, curSubject := range subjects1 { + subjectMap1[curSubject] = true + } + + var subjectMap2 = make(map[rbacv1.Subject]bool) + for _, curSubject := range subjects2 { + subjectMap2[curSubject] = true + } + return reflect.DeepEqual(subjectMap1, subjectMap2) +} diff --git a/pkg/utils/role_test.go b/pkg/utils/role_test.go new file mode 100644 index 000000000..1c95bd60b --- /dev/null +++ b/pkg/utils/role_test.go @@ -0,0 +1,72 @@ +package utils + +import ( + "testing" + + clusterv1alpha1 "github.com/open-cluster-management/api/cluster/v1alpha1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/util/sets" +) + +func TestMergesubjects(t *testing.T) { + type args struct { + subjects []rbacv1.Subject + cursubjects []rbacv1.Subject + } + tests := []struct { + name string + args args + want []rbacv1.Subject + }{ + {"test1", args{subjects: []rbacv1.Subject{{Kind: "R1", APIGroup: "G1", Name: "N1"}}, cursubjects: []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}}}, []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}, {Kind: "R1", APIGroup: "G1", Name: "N1"}}}, + {"test2", args{cursubjects: []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}}}, []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}}}, + {"test3", args{subjects: []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}}}, []rbacv1.Subject{{Kind: "R2", APIGroup: "G2", Name: "N2"}}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := Mergesubjects(tt.args.subjects, tt.args.cursubjects) + if len(res) != len(tt.want) { + t.Errorf("Mergesubjects() = %v, want %v", res, tt.want) + } + }) + } +} + +func createPolicyRule(groups, verbs, res, resnames []string) *rbacv1.PolicyRule { + return &rbacv1.PolicyRule{ + APIGroups: groups, + Verbs: verbs, + Resources: res, + ResourceNames: resnames, + } +} + +func TestGetClustersetInRules(t *testing.T) { + policyr1 := createPolicyRule([]string{"*"}, []string{"*"}, []string{"*"}, []string{"*"}) + policyr2 := createPolicyRule([]string{clusterv1alpha1.GroupName}, []string{"*"}, []string{"*"}, []string{"*"}) + policyr3 := createPolicyRule([]string{clusterv1alpha1.GroupName}, []string{"*"}, []string{"*"}, []string{"res1", "res2"}) + policyr4 := createPolicyRule([]string{clusterv1alpha1.GroupName}, []string{"create"}, []string{"managedclustersets/bind"}, []string{"res1", "res2"}) + + type args struct { + rules []rbacv1.PolicyRule + } + tests := []struct { + name string + args args + want sets.String + }{ + {"test1", args{rules: []rbacv1.PolicyRule{}}, sets.NewString()}, + {"test2", args{rules: []rbacv1.PolicyRule{*policyr1}}, sets.NewString("*")}, + {"test3", args{rules: []rbacv1.PolicyRule{*policyr2}}, sets.NewString("*")}, + {"test4", args{rules: []rbacv1.PolicyRule{*policyr3}}, sets.NewString("res1", "res2")}, + {"test5", args{rules: []rbacv1.PolicyRule{*policyr4}}, sets.NewString("res1", "res2")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + res := GetClustersetInRules(tt.args.rules) + if !res.Equal(tt.want) { + t.Errorf("Mergesubjects() = %v, want %v", res, tt.want) + } + }) + } +}