Skip to content

Commit

Permalink
Update DynamicRoles and DynamicClusterRoles when an inherited Role or…
Browse files Browse the repository at this point in the history
… ClusterRole is modified (#7)
  • Loading branch information
jacobsee authored Mar 1, 2021
1 parent 34df0f8 commit b3a3553
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 29 deletions.
61 changes: 61 additions & 0 deletions controllers/clusterrole_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
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"

"github.com/go-logr/logr"
"github.com/redhat-cop/dynamic-rbac-operator/helpers"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

rbacv1 "k8s.io/api/rbac/v1"
)

// ClusterRoleReconciler reconciles a ClusterRole object
type ClusterRoleReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Cache *helpers.ResourceCache
}

// +kubebuilder:rbac:groups=rbac.redhatcop.redhat.io,resources=clusterroles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=rbac.redhatcop.redhat.io,resources=clusterroles/status,verbs=get;update;patch

func (r *ClusterRoleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("clusterrole", req.NamespacedName)

result := ctrl.Result{}
var err error

if _, exists := r.Cache.WatchedClusterRoles[req.NamespacedName]; exists {
r.Log.Info("A cluster role referenced by a dynamic resource has been updated - reconciling now")
result, err = UpdateAllDynamicResources(r.Client, r.Log, r.Scheme, r.Cache)
}

return result, err
}

func (r *ClusterRoleReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&rbacv1.ClusterRole{}).
Complete(r)
}
30 changes: 4 additions & 26 deletions controllers/crd_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

rbacv1alpha1 "github.com/redhat-cop/dynamic-rbac-operator/api/v1alpha1"
helpers "github.com/redhat-cop/dynamic-rbac-operator/helpers"
)

Expand Down Expand Up @@ -87,33 +86,12 @@ func (r *CustomResourceDefinitionReconciler) Reconcile(req ctrl.Request) (ctrl.R
r.Cache.AllPolicies = &allPossibleRules
r.Log.Info("Rebuilt cluster policy cache")

dynamicRoleList := &rbacv1alpha1.DynamicRoleList{}
err = r.Client.List(context.TODO(), dynamicRoleList)
if err != nil {
r.Log.Error(err, "could not list Dynamic Roles")
return reconcile.Result{}, err
}
for _, dynamicRole := range dynamicRoleList.Items {
_, err := ReconcileDynamicRole(&dynamicRole, r.Client, r.Scheme, r.Log, r.Cache)
if err != nil {
return reconcile.Result{}, err
}
}
dynamicClusterRoleList := &rbacv1alpha1.DynamicClusterRoleList{}
err = r.Client.List(context.TODO(), dynamicClusterRoleList)
if err != nil {
r.Log.Error(err, "could not list Dynamic Cluster Roles")
return reconcile.Result{}, err
}
for _, dynamicClusterRole := range dynamicClusterRoleList.Items {
_, err := ReconcileDynamicClusterRole(&dynamicClusterRole, r.Client, r.Scheme, r.Log, r.Cache)
if err != nil {
return reconcile.Result{}, err
}
}
// Recompute everything using the newly-refreshed cache
result, err := UpdateAllDynamicResources(r.Client, r.Log, r.Scheme, r.Cache)

r.Log.Info("All computed roles have been reconciled")

return ctrl.Result{}, nil
return result, err
}

func (r *CustomResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
Expand Down
48 changes: 48 additions & 0 deletions controllers/meta.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package controllers

import (
"context"

"github.com/go-logr/logr"
rbacv1alpha1 "github.com/redhat-cop/dynamic-rbac-operator/api/v1alpha1"
"github.com/redhat-cop/dynamic-rbac-operator/helpers"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

// UpdateAllDynamicResources loops through all DynamicRoles and DynamicClusterRoles and updates their rules/specs as required based on current cache info
func UpdateAllDynamicResources(client client.Client, log logr.Logger, scheme *runtime.Scheme, cache *helpers.ResourceCache) (ctrl.Result, error) {
// Clear the watched roles cache maps since we're about to recreate them anyway - gets rid of anything we used to care about but no longer need
cache.WatchedRoles = map[types.NamespacedName]bool{}
cache.WatchedClusterRoles = map[types.NamespacedName]bool{}

dynamicRoleList := &rbacv1alpha1.DynamicRoleList{}
err := client.List(context.TODO(), dynamicRoleList)
if err != nil {
log.Error(err, "could not list Dynamic Roles")
return reconcile.Result{}, err
}
for _, dynamicRole := range dynamicRoleList.Items {
_, err := ReconcileDynamicRole(&dynamicRole, client, scheme, log, cache)
if err != nil {
return reconcile.Result{}, err
}
}
dynamicClusterRoleList := &rbacv1alpha1.DynamicClusterRoleList{}
err = client.List(context.TODO(), dynamicClusterRoleList)
if err != nil {
log.Error(err, "could not list Dynamic Cluster Roles")
return reconcile.Result{}, err
}
for _, dynamicClusterRole := range dynamicClusterRoleList.Items {
_, err := ReconcileDynamicClusterRole(&dynamicClusterRole, client, scheme, log, cache)
if err != nil {
return reconcile.Result{}, err
}
}
log.Info("All computed roles have been reconciled")
return reconcile.Result{}, nil
}
61 changes: 61 additions & 0 deletions controllers/role_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
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"

"github.com/go-logr/logr"
"github.com/redhat-cop/dynamic-rbac-operator/helpers"
"k8s.io/apimachinery/pkg/runtime"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"

rbacv1 "k8s.io/api/rbac/v1"
)

// RoleReconciler reconciles a Role object
type RoleReconciler struct {
client.Client
Log logr.Logger
Scheme *runtime.Scheme
Cache *helpers.ResourceCache
}

// +kubebuilder:rbac:groups=rbac.redhatcop.redhat.io,resources=roles,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=rbac.redhatcop.redhat.io,resources=roles/status,verbs=get;update;patch

func (r *RoleReconciler) Reconcile(req ctrl.Request) (ctrl.Result, error) {
_ = context.Background()
_ = r.Log.WithValues("role", req.NamespacedName)

result := ctrl.Result{}
var err error

if _, exists := r.Cache.WatchedRoles[req.NamespacedName]; exists {
r.Log.Info("A role referenced by a dynamic resource has been updated - reconciling now")
result, err = UpdateAllDynamicResources(r.Client, r.Log, r.Scheme, r.Cache)
}

return result, err
}

func (r *RoleReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&rbacv1.Role{}).
Complete(r)
}
9 changes: 7 additions & 2 deletions helpers/resource_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,18 @@ import (
"sync"

rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/types"
)

var lock = &sync.Mutex{}

// ResourceCache holds information about the kube cluster state and
// its policies so that it doesn't need to be queried for every reconciliation.
type ResourceCache struct {
CRDs map[string]string
AllPolicies *[]rbacv1.PolicyRule
CRDs map[string]string
AllPolicies *[]rbacv1.PolicyRule
WatchedRoles map[types.NamespacedName]bool
WatchedClusterRoles map[types.NamespacedName]bool
}

var instance *ResourceCache
Expand All @@ -25,6 +28,8 @@ func GetCacheInstance() *ResourceCache {
if instance == nil {
instance = &ResourceCache{}
instance.CRDs = map[string]string{}
instance.WatchedRoles = map[types.NamespacedName]bool{}
instance.WatchedClusterRoles = map[types.NamespacedName]bool{}
}
}
return instance
Expand Down
4 changes: 3 additions & 1 deletion helpers/rule_manipulation.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ func BuildPolicyRules(client client.Client, cache ResourceCache, roleType RoleTy
if err != nil {
return nil, err
}
// nonResourceURLs do not make sense to move from a ClusterRole to a Role
cache.WatchedClusterRoles[clusterRoleNamespacedName] = true
var enumeratedPolicyRules []v1.PolicyRule
if roleType == Role {
// nonResourceURLs do not make sense to move from a ClusterRole to a Role
enumeratedPolicyRules, err = EnumeratePolicyRules(StripNonResourceURLs(inheritedClusterRole.Rules), &cache)
} else {
enumeratedPolicyRules, err = EnumeratePolicyRules(inheritedClusterRole.Rules, &cache)
Expand All @@ -57,6 +58,7 @@ func BuildPolicyRules(client client.Client, cache ResourceCache, roleType RoleTy
if err != nil {
return nil, err
}
cache.WatchedRoles[roleNamespacedName] = true
enumeratedPolicyRules, err := EnumeratePolicyRules(inheritedRole.Rules, &cache)
expandedPolicyRules := ExpandPolicyRules(enumeratedPolicyRules)
rules = MergeExpandedPolicyRules(rules, expandedPolicyRules)
Expand Down
18 changes: 18 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@ func main() {
setupLog.Error(err, "unable to create controller", "controller", "DynamicClusterRole")
os.Exit(1)
}
if err = (&controllers.RoleReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("Role"),
Scheme: mgr.GetScheme(),
Cache: cache,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Role")
os.Exit(1)
}
if err = (&controllers.ClusterRoleReconciler{
Client: mgr.GetClient(),
Log: ctrl.Log.WithName("controllers").WithName("ClusterRole"),
Scheme: mgr.GetScheme(),
Cache: cache,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "ClusterRole")
os.Exit(1)
}
// +kubebuilder:scaffold:builder

// Begin cache setup
Expand Down

0 comments on commit b3a3553

Please sign in to comment.