github.com/jenkins-x/jx/v2@v2.1.155/pkg/kube/roles.go (about)

     1  package kube
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	v1 "github.com/jenkins-x/jx-api/pkg/apis/jenkins.io/v1"
     8  	"github.com/jenkins-x/jx-api/pkg/client/clientset/versioned"
     9  	"github.com/jenkins-x/jx-logging/pkg/log"
    10  	"github.com/jenkins-x/jx/v2/pkg/util"
    11  	"github.com/pkg/errors"
    12  	rbacv1 "k8s.io/api/rbac/v1"
    13  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    14  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/client-go/kubernetes"
    16  )
    17  
    18  const (
    19  	clusterRoleBindingKind = "ClusterRoleBinding"
    20  	clusterRoleKind        = "ClusterRole"
    21  	apiGroup               = "rbac.authorization.k8s.io"
    22  	subjectKind            = "ServiceAccount"
    23  )
    24  
    25  // GetTeamRoles returns the roles for the given team dev namespace
    26  func GetTeamRoles(kubeClient kubernetes.Interface, ns string) (map[string]*rbacv1.Role, []string, error) {
    27  	m := map[string]*rbacv1.Role{}
    28  
    29  	names := []string{}
    30  	resources, err := kubeClient.RbacV1().Roles(ns).List(metav1.ListOptions{
    31  		LabelSelector: LabelKind + "=" + ValueKindEnvironmentRole,
    32  	})
    33  	if err != nil {
    34  		return m, names, err
    35  	}
    36  	for _, env := range resources.Items {
    37  		n := env.Name
    38  		copy := env
    39  		m[n] = &copy
    40  		if n != "" {
    41  			names = append(names, n)
    42  		}
    43  	}
    44  	sort.Strings(names)
    45  	return m, names, nil
    46  }
    47  
    48  // GetEnvironmentRoles returns all the environment role binding names and details
    49  func GetEnvironmentRoles(jxClient versioned.Interface, ns string) (map[string]*v1.EnvironmentRoleBinding, []string, error) {
    50  	names := []string{}
    51  	m := map[string]*v1.EnvironmentRoleBinding{}
    52  	envRoleBindingsList, err := jxClient.JenkinsV1().EnvironmentRoleBindings(ns).List(metav1.ListOptions{})
    53  	if err != nil {
    54  		return m, names, fmt.Errorf("Failed to retrieve EnvironmentRoleBinding list for namespace %s: %s", ns, err)
    55  	}
    56  	for _, envRoleBinding := range envRoleBindingsList.Items {
    57  		copy := envRoleBinding
    58  		name := copy.Name
    59  		m[name] = &copy
    60  		names = append(names, name)
    61  	}
    62  	return m, names, err
    63  }
    64  
    65  // UpdateUserRoles updates the EnvironmentRoleBinding values based on the given userRoles
    66  // userKind is "User" or "ServiceAccount"
    67  func GetUserRoles(kubeClient kubernetes.Interface, jxClient versioned.Interface, ns string, userKind string, userName string) ([]string, error) {
    68  	envRoles, _, err := GetEnvironmentRoles(jxClient, ns)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	adminNs, err := GetAdminNamespace(kubeClient, ns)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  	currentRoles := userRolesFor(userKind, userName, adminNs, envRoles)
    77  	return currentRoles, err
    78  }
    79  
    80  // UpdateUserRoles updates the EnvironmentRoleBinding values based on the given userRoles.
    81  // userKind is "User" or "ServiceAccount"
    82  func UpdateUserRoles(kubeClient kubernetes.Interface, jxClient versioned.Interface, ns string, userKind string, userName string, userRoles []string, roles map[string]*rbacv1.Role) error {
    83  
    84  	envRoleInterface := jxClient.JenkinsV1().EnvironmentRoleBindings(ns)
    85  	envRoles, _, err := GetEnvironmentRoles(jxClient, ns)
    86  
    87  	adminNs, err := GetAdminNamespace(kubeClient, ns)
    88  	if err != nil {
    89  		return errors.Wrapf(err, "Obtaining admin namespace for user lookup")
    90  	}
    91  
    92  	// make sure all of the EnvironmentRoleBinding are created
    93  	for name := range roles {
    94  		envRole := envRoles[name]
    95  		if envRole == nil {
    96  			envRole = &v1.EnvironmentRoleBinding{
    97  				ObjectMeta: metav1.ObjectMeta{
    98  					Name:      name,
    99  					Namespace: ns,
   100  					Labels: map[string]string{
   101  						LabelKind: ValueKindEnvironmentRole,
   102  					},
   103  				},
   104  				Spec: v1.EnvironmentRoleBindingSpec{
   105  					RoleRef: rbacv1.RoleRef{
   106  						Kind:     "Role",
   107  						Name:     name,
   108  						APIGroup: "rbac.authorization.k8s.io",
   109  					},
   110  					Subjects: []rbacv1.Subject{},
   111  				},
   112  			}
   113  			_, err = envRoleInterface.Create(envRole)
   114  			if err != nil {
   115  				return errors.Wrapf(err, "Failed to create EnvironmentRoleBinding %s", name)
   116  			}
   117  			envRoles[name] = envRole
   118  		}
   119  	}
   120  
   121  	oldRoles := userRolesFor(userKind, userName, adminNs, envRoles)
   122  
   123  	deleteRoles, createRoles := util.DiffSlices(oldRoles, userRoles)
   124  
   125  	// should we use a single patch or create at the end and be atomic for the whole operation?
   126  	for _, name := range deleteRoles {
   127  		envRole := envRoles[name]
   128  		if envRole == nil {
   129  			log.Logger().Warnf("Could not remove user %s kind %s from EnvironmentRoleBinding %s as it does not exist", userName, userKind, name)
   130  		} else {
   131  			found := false
   132  			for idx, subject := range envRole.Spec.Subjects {
   133  				if subject.Kind == userKind && subject.Name == userName && subject.Namespace == adminNs {
   134  					found = true
   135  					envRole.Spec.Subjects = append(envRole.Spec.Subjects[0:idx], envRole.Spec.Subjects[idx+1:]...)
   136  					_, err = envRoleInterface.PatchUpdate(envRole)
   137  					if err != nil {
   138  						return errors.Wrapf(err, "Failed to remove User %s kind %s as a Subject of EnvironmentRoleBinding %s: %s", userName, userKind, name, err)
   139  					}
   140  					break
   141  				}
   142  			}
   143  			if !found {
   144  				log.Logger().Warnf("User %s kind %s is not a Subject of EnvironmentRoleBinding %s", userName, userKind, name)
   145  			}
   146  		}
   147  	}
   148  	for _, name := range createRoles {
   149  		envRole := envRoles[name]
   150  		if envRole == nil {
   151  			// TODO lazily create the EnvironmentRoleBinding?
   152  			log.Logger().Warnf("Could not add user %s to EnvironmentRoleBinding %s as it does not exist!", userName, name)
   153  		} else {
   154  			found := false
   155  			for _, subject := range envRole.Spec.Subjects {
   156  				if subject.Kind == userKind && subject.Name == userName && subject.Namespace == adminNs {
   157  					found = true
   158  				}
   159  			}
   160  			if found {
   161  				log.Logger().Warnf("User %s kind %s is already a Subject of EnvironmentRoleBinding %s", userName, userKind, name)
   162  			} else {
   163  				newSubject := rbacv1.Subject{
   164  					Name:      userName,
   165  					Kind:      userKind,
   166  					Namespace: adminNs,
   167  				}
   168  				newEnvRole, err := envRoleInterface.Get(envRole.Name, metav1.GetOptions{})
   169  				create := false
   170  				if err != nil {
   171  					create = true
   172  					newEnvRole = envRole
   173  				} else {
   174  					newEnvRole.Spec = envRole.Spec
   175  				}
   176  				newEnvRole.Spec.Subjects = append(newEnvRole.Spec.Subjects, newSubject)
   177  				if create {
   178  					_, err = envRoleInterface.Create(newEnvRole)
   179  					if err != nil {
   180  						return errors.Wrapf(err, "Failed to create EnvironmentRoleBinding %s with Subject User %s kind %s: %s", name, userName, userKind, err)
   181  					}
   182  				} else {
   183  					_, err = envRoleInterface.PatchUpdate(newEnvRole)
   184  					if err != nil {
   185  						return errors.Wrapf(err, "Failed to add User %s kind %s as a Subject of EnvironmentRoleBinding %s: %s", userName, userKind, name, err)
   186  					}
   187  				}
   188  			}
   189  		}
   190  	}
   191  	return nil
   192  }
   193  
   194  func userRolesFor(userKind string, userName string, userNamespace string, envRoles map[string]*v1.EnvironmentRoleBinding) []string {
   195  	answer := []string{}
   196  	for _, envRole := range envRoles {
   197  		for _, subject := range envRole.Spec.Subjects {
   198  			if subject.Kind == userKind && subject.Name == userName && subject.Namespace == userNamespace {
   199  				answer = append(answer, envRole.Name)
   200  			}
   201  		}
   202  	}
   203  	return answer
   204  }
   205  
   206  // IsClusterRoleBinding checks if the cluster role binding exists
   207  func IsClusterRoleBinding(kubeClient kubernetes.Interface, name string) bool {
   208  	_, err := kubeClient.RbacV1().ClusterRoleBindings().Get(name, metav1.GetOptions{})
   209  	if apierrors.IsNotFound(err) {
   210  		return false
   211  	}
   212  	return true
   213  }
   214  
   215  // CreateClusterRoleBinding creates acluster role binding in a given namespace for a service account
   216  func CreateClusterRoleBinding(kubeClient kubernetes.Interface, namespace string, name string,
   217  	serviceAccountName string, clusterRoleName string) error {
   218  	rb := &rbacv1.ClusterRoleBinding{
   219  		TypeMeta: metav1.TypeMeta{
   220  			Kind:       clusterRoleBindingKind,
   221  			APIVersion: "v1",
   222  		},
   223  		ObjectMeta: metav1.ObjectMeta{
   224  			Name: name,
   225  		},
   226  		Subjects: []rbacv1.Subject{
   227  			{
   228  				Kind:      subjectKind,
   229  				Name:      serviceAccountName,
   230  				Namespace: namespace,
   231  			},
   232  		},
   233  		RoleRef: rbacv1.RoleRef{
   234  			APIGroup: apiGroup,
   235  			Kind:     clusterRoleKind,
   236  			Name:     clusterRoleName,
   237  		},
   238  	}
   239  
   240  	_, err := kubeClient.RbacV1().ClusterRoleBindings().Create(rb)
   241  	if err != nil {
   242  		return errors.Wrap(err, "creating cluster role binding")
   243  	}
   244  	return nil
   245  }
   246  
   247  // DeleteClusterRoleBinding deltes a cluster role binding
   248  func DeleteClusterRoleBinding(kubeClient kubernetes.Interface, name string) error {
   249  	_, err := kubeClient.RbacV1().ClusterRoleBindings().Get(name, metav1.GetOptions{})
   250  	if err == nil {
   251  		return kubeClient.RbacV1().ClusterRoleBindings().Delete(name, &metav1.DeleteOptions{})
   252  	}
   253  	return nil
   254  }
   255  
   256  // IsClusterRole checks if a cluster role exists
   257  func IsClusterRole(kubeClient kubernetes.Interface, name string) bool {
   258  	_, err := kubeClient.RbacV1().ClusterRoles().Get(name, metav1.GetOptions{})
   259  	if apierrors.IsNotFound(err) {
   260  		return false
   261  	}
   262  	return true
   263  }
   264  
   265  // CreateClusterRole creates a new cluster role
   266  func CreateClusterRole(kubeClient kubernetes.Interface, namesapce string, name string,
   267  	apiGroups []string, resources []string, verbs []string) error {
   268  	role := &rbacv1.ClusterRole{
   269  		TypeMeta: metav1.TypeMeta{
   270  			Kind:       clusterRoleKind,
   271  			APIVersion: "v1",
   272  		},
   273  		ObjectMeta: metav1.ObjectMeta{
   274  			Name: name,
   275  		},
   276  		Rules: []rbacv1.PolicyRule{
   277  			{
   278  				APIGroups: apiGroups,
   279  				Resources: resources,
   280  				Verbs:     verbs,
   281  			},
   282  		},
   283  	}
   284  	_, err := kubeClient.RbacV1().ClusterRoles().Create(role)
   285  	if err != nil {
   286  		return errors.Wrap(err, "creating custer role")
   287  	}
   288  	return nil
   289  }
   290  
   291  // DeleteClusterRole deletes a cluster role if exists
   292  func DeleteClusterRole(kubeClient kubernetes.Interface, name string) error {
   293  	if IsClusterRole(kubeClient, name) {
   294  		return kubeClient.RbacV1().ClusterRoles().Delete(name, &metav1.DeleteOptions{})
   295  	}
   296  	return nil
   297  }