github.com/verrazzano/verrazzano-monitoring-operator@v0.0.30/pkg/vmo/rolebindings.go (about) 1 // Copyright (C) 2020, 2022, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 package vmo 5 6 import ( 7 "context" 8 "errors" 9 10 "github.com/verrazzano/pkg/diff" 11 vmcontrollerv1 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/apis/vmcontroller/v1" 12 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/constants" 13 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/metricsexporter" 14 "github.com/verrazzano/verrazzano-monitoring-operator/pkg/resources" 15 rbacv1 "k8s.io/api/rbac/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/labels" 18 ) 19 20 // CreateRoleBindings creates/updates VMO RoleBindings k8s resources 21 func CreateRoleBindings(controller *Controller, vmo *vmcontrollerv1.VerrazzanoMonitoringInstance) error { 22 metric, metricErr := metricsexporter.GetCounterMetrics(metricsexporter.NamesRoleBindings) 23 if metricErr != nil { 24 return metricErr 25 } 26 metric.Inc() 27 controller.log.Oncef("Creating/updating RoleBindings for VMI %s", vmo.Name) 28 29 newRoleBindings, err := NewRoleBindings(vmo, controller) 30 if err != nil { 31 return err 32 } 33 34 var roleBindingNames []string 35 ownerReferences := getHyperOperatorOwnerReferences(controller) 36 for _, newRoleBinding := range newRoleBindings { 37 roleBindingNames = append(roleBindingNames, newRoleBinding.Name) 38 newRoleBinding.OwnerReferences = ownerReferences // set OwnerReferences to the Hyper Operator deployment 39 existingRoleBinding, _ := controller.roleBindingLister.RoleBindings(vmo.Namespace).Get(newRoleBinding.Name) 40 var err error 41 if existingRoleBinding != nil { 42 specDiffs := diff.Diff(existingRoleBinding, newRoleBinding) 43 if specDiffs != "" { 44 controller.log.Debugf("RoleBinding %s : Spec differences %s", newRoleBinding.Name, specDiffs) 45 err = controller.kubeclientset.RbacV1().RoleBindings(vmo.Namespace).Delete(context.TODO(), newRoleBinding.Name, metav1.DeleteOptions{}) 46 if err != nil { 47 controller.log.Errorf("Failed deleting role binding %s: %v", newRoleBinding.Name, err) 48 } 49 _, err = controller.kubeclientset.RbacV1().RoleBindings(vmo.Namespace).Create(context.TODO(), newRoleBinding, metav1.CreateOptions{}) 50 } 51 } else { 52 _, err = controller.kubeclientset.RbacV1().RoleBindings(vmo.Namespace).Create(context.TODO(), newRoleBinding, metav1.CreateOptions{}) 53 } 54 if err != nil { 55 return err 56 } 57 } 58 59 // Delete RoleBindings that shouldn't exist 60 controller.log.Debugf("Deleting unwanted RoleBindings for VMI '%s' in namespace '%s'", vmo.Name, vmo.Namespace) 61 selector := labels.SelectorFromSet(map[string]string{constants.VMOLabel: vmo.Name}) 62 existingRoleBindings, err := controller.roleBindingLister.RoleBindings(vmo.Namespace).List(selector) 63 if err != nil { 64 return err 65 } 66 // While we transition from the old to the new per-VMO RoleBinding name, the following line explicitly adds 67 // the *old* RoleBinding to the list of RoleBindings to remove. It is otherwise not in the existingRoleBindingsList 68 // list because we didn't originally add our usual VMO label set to it... 69 oldRoleBinding, _ := controller.roleBindingLister.RoleBindings(vmo.Namespace).Get("vmo-instance-role-binding") 70 if oldRoleBinding != nil { 71 existingRoleBindings = append(existingRoleBindings, oldRoleBinding) 72 } 73 for _, roleBinding := range existingRoleBindings { 74 if !contains(roleBindingNames, roleBinding.Name) { 75 controller.log.Oncef("Deleting RoleBinding %s", roleBinding.Name) 76 err := controller.kubeclientset.RbacV1().RoleBindings(vmo.Namespace).Delete(context.TODO(), roleBinding.Name, metav1.DeleteOptions{}) 77 if err != nil { 78 controller.log.Errorf("Failed to delete RoleBinding %s: %v", roleBinding.Name, err) 79 return err 80 } 81 } 82 } 83 timeMetric, timeErr := metricsexporter.GetTimestampMetrics(metricsexporter.NamesRoleBindings) 84 if timeErr != nil { 85 return timeErr 86 } 87 timeMetric.SetLastTime() 88 return nil 89 } 90 91 // NewRoleBindings constructs the necessary RoleBindings for a VMO instance's Sub-Operator. 92 func NewRoleBindings(vmo *vmcontrollerv1.VerrazzanoMonitoringInstance, controller *Controller) ([]*rbacv1.RoleBinding, error) { 93 instanceClusterRole, err := findClusterRole(controller, constants.ClusterRoleForVMOInstances) 94 if err != nil { 95 return nil, err 96 } 97 98 roleBindings := []*rbacv1.RoleBinding{ 99 { 100 ObjectMeta: metav1.ObjectMeta{ 101 Labels: resources.GetMetaLabels(vmo), 102 Name: resources.GetMetaName(vmo.Name, constants.RoleBindingForVMOInstance), 103 Namespace: vmo.Namespace, 104 OwnerReferences: resources.GetOwnerReferences(vmo), 105 }, 106 Subjects: []rbacv1.Subject{ 107 { 108 Kind: "ServiceAccount", 109 Name: "default", 110 Namespace: vmo.Namespace, 111 }, 112 }, 113 RoleRef: rbacv1.RoleRef{ 114 APIGroup: "rbac.authorization.k8s.io", 115 Kind: "ClusterRole", 116 Name: instanceClusterRole.Name, 117 }, 118 }, 119 } 120 return roleBindings, nil 121 } 122 123 // Search first for a ClusterRole associated with the namespace. If that fails, look for one associated 124 // with the default namespace. This check is mainly to keep integration tests (one particular situation where the Helm 125 // chart is deployed with a namespace) working smoothly. 126 func findClusterRole(controller *Controller, prefix string) (*rbacv1.ClusterRole, error) { 127 clusterRole, _ := controller.clusterRoleLister.Get(prefix + "-" + controller.namespace) 128 if clusterRole == nil { 129 clusterRole, _ = controller.clusterRoleLister.Get(prefix + "-default") 130 if clusterRole == nil { 131 return nil, errors.New("unable to find a valid ClusterRole to assign to VMO instances") 132 } 133 } 134 return clusterRole, nil 135 }