Skip to content

Commit

Permalink
Merge pull request #232 from ldpliu/add-clusterset-subject
Browse files Browse the repository at this point in the history
Add clusterset subject
  • Loading branch information
openshift-merge-robot authored Sep 10, 2020
2 parents 2299d65 + 7d5ef03 commit ed24484
Show file tree
Hide file tree
Showing 7 changed files with 608 additions and 3 deletions.
14 changes: 12 additions & 2 deletions cmd/controller/app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion deploy/foundation/hub/resources/clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit ed24484

Please sign in to comment.