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  }