github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/rbac.go (about)

     1  // Copyright 2019 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provider
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"reflect"
    10  	"sort"
    11  	"time"
    12  
    13  	jujuclock "github.com/juju/clock"
    14  	"github.com/juju/collections/set"
    15  	"github.com/juju/errors"
    16  	"github.com/juju/names/v5"
    17  	"github.com/juju/retry"
    18  	authenticationv1 "k8s.io/api/authentication/v1"
    19  	core "k8s.io/api/core/v1"
    20  	rbacv1 "k8s.io/api/rbac/v1"
    21  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    22  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	k8slabels "k8s.io/apimachinery/pkg/labels"
    24  	"k8s.io/apimachinery/pkg/types"
    25  
    26  	"github.com/juju/juju/caas/kubernetes/provider/constants"
    27  	"github.com/juju/juju/caas/kubernetes/provider/resources"
    28  	k8sspecs "github.com/juju/juju/caas/kubernetes/provider/specs"
    29  	"github.com/juju/juju/caas/kubernetes/provider/utils"
    30  	environsbootstrap "github.com/juju/juju/environs/bootstrap"
    31  )
    32  
    33  // AppNameForServiceAccount returns the juju application name associated with a
    34  // given ServiceAccount. If app name cannot be obtained from the service
    35  // account, errors.NotFound is returned.
    36  func AppNameForServiceAccount(sa *core.ServiceAccount) (string, error) {
    37  	if appName, ok := sa.Labels[constants.LabelKubernetesAppName]; ok {
    38  		return appName, nil
    39  	} else if appName, ok := sa.Labels[constants.LegacyLabelKubernetesAppName]; ok {
    40  		return appName, nil
    41  	}
    42  	return "", errors.NotFoundf("application labels for service account %s", sa.Name)
    43  }
    44  
    45  // RBACLabels returns a set of labels that should be present for RBAC objects.
    46  func RBACLabels(appName, model string, global, legacy bool) map[string]string {
    47  	labels := utils.LabelsForApp(appName, legacy)
    48  	if global {
    49  		labels = utils.LabelsMerge(labels, utils.LabelsForModel(model, legacy))
    50  	}
    51  	return labels
    52  }
    53  
    54  func (k *kubernetesClient) ensureServiceAccountForApp(
    55  	appName string, annotations map[string]string, rbacDefinition k8sspecs.K8sRBACSpecConverter,
    56  ) (cleanups []func(), err error) {
    57  	ctx := context.TODO()
    58  
    59  	prefixNameSpace := func(name string) string {
    60  		return fmt.Sprintf("%s-%s", k.namespace, name)
    61  	}
    62  	getBindingName := func(sa, cR k8sspecs.NameGetter) string {
    63  		if sa.GetName() == cR.GetName() {
    64  			return sa.GetName()
    65  		}
    66  		return fmt.Sprintf("%s-%s", sa.GetName(), cR.GetName())
    67  	}
    68  	getSAMeta := func(name string) v1.ObjectMeta {
    69  		return v1.ObjectMeta{
    70  			Name:        name,
    71  			Namespace:   k.namespace,
    72  			Labels:      RBACLabels(appName, k.CurrentModel(), false, k.IsLegacyLabels()),
    73  			Annotations: annotations,
    74  		}
    75  	}
    76  	getRoleClusterRoleName := func(roleName, serviceAccountName string, index int, global bool) (out string) {
    77  		defer func() {
    78  			if global {
    79  				out = prefixNameSpace(out)
    80  			}
    81  		}()
    82  		if roleName != "" {
    83  			return roleName
    84  		}
    85  		out = serviceAccountName
    86  		if index == 0 {
    87  			return out
    88  		}
    89  		return fmt.Sprintf("%s%d", out, index)
    90  	}
    91  	getRoleMeta := func(roleName, serviceAccountName string, index int) v1.ObjectMeta {
    92  		return v1.ObjectMeta{
    93  			Name:        getRoleClusterRoleName(roleName, serviceAccountName, index, false),
    94  			Namespace:   k.namespace,
    95  			Labels:      RBACLabels(appName, k.CurrentModel(), false, k.IsLegacyLabels()),
    96  			Annotations: annotations,
    97  		}
    98  	}
    99  	getClusterRoleMeta := func(roleName, serviceAccountName string, index int) v1.ObjectMeta {
   100  		return v1.ObjectMeta{
   101  			Name:        getRoleClusterRoleName(roleName, serviceAccountName, index, true),
   102  			Namespace:   k.namespace,
   103  			Labels:      RBACLabels(appName, k.CurrentModel(), true, k.IsLegacyLabels()),
   104  			Annotations: annotations,
   105  		}
   106  	}
   107  	getBindingMeta := func(sa, role k8sspecs.NameGetter) v1.ObjectMeta {
   108  		return v1.ObjectMeta{
   109  			Name:        getBindingName(sa, role),
   110  			Namespace:   k.namespace,
   111  			Labels:      RBACLabels(appName, k.CurrentModel(), false, k.IsLegacyLabels()),
   112  			Annotations: annotations,
   113  		}
   114  	}
   115  	getClusterBindingMeta := func(sa, clusterRole k8sspecs.NameGetter) v1.ObjectMeta {
   116  		return v1.ObjectMeta{
   117  			Name:        getBindingName(sa, clusterRole),
   118  			Namespace:   k.namespace,
   119  			Labels:      RBACLabels(appName, k.CurrentModel(), true, k.IsLegacyLabels()),
   120  			Annotations: annotations,
   121  		}
   122  	}
   123  
   124  	serviceAccounts, roles, clusterroles, roleBindings, clusterRoleBindings := rbacDefinition.ToK8s(
   125  		getSAMeta,
   126  		getRoleMeta,
   127  		getClusterRoleMeta,
   128  		getBindingMeta,
   129  		getClusterBindingMeta,
   130  	)
   131  
   132  	for _, spec := range serviceAccounts {
   133  		_, sacleanups, err := k.ensureServiceAccount(&spec)
   134  		cleanups = append(cleanups, sacleanups...)
   135  		if err != nil {
   136  			return cleanups, errors.Trace(err)
   137  		}
   138  	}
   139  
   140  	for _, spec := range roles {
   141  		_, rCleanups, err := k.ensureRole(&spec)
   142  		cleanups = append(cleanups, rCleanups...)
   143  		if err != nil {
   144  			return cleanups, errors.Trace(err)
   145  		}
   146  	}
   147  	for _, spec := range roleBindings {
   148  		_, rbCleanups, err := k.ensureRoleBinding(&spec)
   149  		cleanups = append(cleanups, rbCleanups...)
   150  		if err != nil {
   151  			return cleanups, errors.Trace(err)
   152  		}
   153  	}
   154  
   155  	for _, spec := range clusterroles {
   156  		cr := resources.ClusterRole{spec}
   157  		cRCleanups, err := cr.Ensure(
   158  			ctx,
   159  			k.client(),
   160  			resources.ClaimJujuOwnership,
   161  		)
   162  		cleanups = append(cleanups, cRCleanups...)
   163  		if err != nil {
   164  			return cleanups, errors.Trace(err)
   165  		}
   166  	}
   167  	for _, spec := range clusterRoleBindings {
   168  		clusterRoleBinding := resources.NewClusterRoleBinding(spec.Name, &spec)
   169  		crbCleanups, err := clusterRoleBinding.Ensure(
   170  			ctx,
   171  			k.client(),
   172  			resources.ClaimJujuOwnership,
   173  		)
   174  		cleanups = append(cleanups, crbCleanups...)
   175  		if err != nil {
   176  			return cleanups, errors.Trace(err)
   177  		}
   178  	}
   179  
   180  	return cleanups, nil
   181  }
   182  
   183  func (k *kubernetesClient) deleteAllServiceAccountResources(appName string) error {
   184  	selectorNamespaced := utils.LabelsToSelector(
   185  		RBACLabels(appName, k.CurrentModel(), false, k.IsLegacyLabels()))
   186  	selectorGlobal := utils.LabelsToSelector(
   187  		RBACLabels(appName, k.CurrentModel(), true, k.IsLegacyLabels()))
   188  	if err := k.deleteRoleBindings(selectorNamespaced); err != nil {
   189  		return errors.Trace(err)
   190  	}
   191  	if err := k.deleteClusterRoleBindings(selectorGlobal); err != nil {
   192  		return errors.Trace(err)
   193  	}
   194  	if err := k.deleteRoles(selectorNamespaced); err != nil {
   195  		return errors.Trace(err)
   196  	}
   197  	if err := k.deleteClusterRoles(selectorGlobal); err != nil {
   198  		return errors.Trace(err)
   199  	}
   200  	if err := k.deleteServiceAccounts(selectorNamespaced); err != nil {
   201  		return errors.Trace(err)
   202  	}
   203  	return nil
   204  }
   205  
   206  func (k *kubernetesClient) createServiceAccount(sa *core.ServiceAccount) (*core.ServiceAccount, error) {
   207  	if k.namespace == "" {
   208  		return nil, errNoNamespace
   209  	}
   210  	utils.PurifyResource(sa)
   211  	out, err := k.client().CoreV1().ServiceAccounts(k.namespace).Create(context.TODO(), sa, v1.CreateOptions{})
   212  	if k8serrors.IsAlreadyExists(err) {
   213  		return nil, errors.AlreadyExistsf("service account %q", sa.GetName())
   214  	}
   215  	return out, errors.Trace(err)
   216  }
   217  
   218  func (k *kubernetesClient) updateServiceAccount(sa *core.ServiceAccount) (*core.ServiceAccount, error) {
   219  	if k.namespace == "" {
   220  		return nil, errNoNamespace
   221  	}
   222  	out, err := k.client().CoreV1().ServiceAccounts(k.namespace).Update(context.TODO(), sa, v1.UpdateOptions{})
   223  	if k8serrors.IsNotFound(err) {
   224  		return nil, errors.NotFoundf("service account %q", sa.GetName())
   225  	}
   226  	return out, errors.Trace(err)
   227  }
   228  
   229  func (k *kubernetesClient) ensureServiceAccount(sa *core.ServiceAccount) (out *core.ServiceAccount, cleanups []func(), err error) {
   230  	out, err = k.createServiceAccount(sa)
   231  	if err == nil {
   232  		logger.Debugf("service account %q created", out.GetName())
   233  		cleanups = append(cleanups, func() { _ = k.deleteServiceAccount(out.GetName(), out.GetUID()) })
   234  		return out, cleanups, nil
   235  	}
   236  	if !errors.IsAlreadyExists(err) {
   237  		return nil, cleanups, errors.Trace(err)
   238  	}
   239  	_, err = k.listServiceAccount(sa.GetLabels())
   240  	if err != nil {
   241  		if errors.IsNotFound(err) {
   242  			// sa.Name is already used for an existing service account.
   243  			return nil, cleanups, errors.AlreadyExistsf("service account %q", sa.GetName())
   244  		}
   245  		return nil, cleanups, errors.Trace(err)
   246  	}
   247  	out, err = k.updateServiceAccount(sa)
   248  	logger.Debugf("updating service account %q", sa.GetName())
   249  	return out, cleanups, errors.Trace(err)
   250  }
   251  
   252  func (k *kubernetesClient) getServiceAccount(name string) (*core.ServiceAccount, error) {
   253  	if k.namespace == "" {
   254  		return nil, errNoNamespace
   255  	}
   256  	out, err := k.client().CoreV1().ServiceAccounts(k.namespace).Get(context.TODO(), name, v1.GetOptions{})
   257  	if k8serrors.IsNotFound(err) {
   258  		return nil, errors.NotFoundf("service account %q", name)
   259  	}
   260  	return out, errors.Trace(err)
   261  }
   262  
   263  func (k *kubernetesClient) deleteServiceAccount(name string, uid types.UID) error {
   264  	if k.namespace == "" {
   265  		return errNoNamespace
   266  	}
   267  	err := k.client().CoreV1().ServiceAccounts(k.namespace).Delete(context.TODO(), name, utils.NewPreconditionDeleteOptions(uid))
   268  	if k8serrors.IsNotFound(err) {
   269  		return nil
   270  	}
   271  	return errors.Trace(err)
   272  }
   273  
   274  func (k *kubernetesClient) deleteServiceAccounts(selectors ...k8slabels.Selector) error {
   275  	for _, selector := range selectors {
   276  		err := k.client().CoreV1().ServiceAccounts(k.namespace).DeleteCollection(
   277  			context.TODO(),
   278  			v1.DeleteOptions{
   279  				PropagationPolicy: constants.DefaultPropagationPolicy(),
   280  			}, v1.ListOptions{
   281  				LabelSelector: selector.String(),
   282  			})
   283  		if !k8serrors.IsNotFound(err) {
   284  			return errors.Trace(err)
   285  		}
   286  	}
   287  	return nil
   288  }
   289  
   290  func (k *kubernetesClient) listServiceAccount(labels map[string]string) ([]core.ServiceAccount, error) {
   291  	if k.namespace == "" {
   292  		return nil, errNoNamespace
   293  	}
   294  	listOps := v1.ListOptions{
   295  		LabelSelector: utils.LabelsToSelector(labels).String(),
   296  	}
   297  	saList, err := k.client().CoreV1().ServiceAccounts(k.namespace).List(context.TODO(), listOps)
   298  	if err != nil {
   299  		return nil, errors.Trace(err)
   300  	}
   301  	if len(saList.Items) == 0 {
   302  		return nil, errors.NotFoundf("service account with labels %v", labels)
   303  	}
   304  	return saList.Items, nil
   305  }
   306  
   307  func (k *kubernetesClient) createRole(role *rbacv1.Role) (*rbacv1.Role, error) {
   308  	if k.namespace == "" {
   309  		return nil, errNoNamespace
   310  	}
   311  	utils.PurifyResource(role)
   312  	out, err := k.client().RbacV1().Roles(k.namespace).Create(context.TODO(), role, v1.CreateOptions{})
   313  	if k8serrors.IsAlreadyExists(err) {
   314  		return nil, errors.AlreadyExistsf("role %q", role.GetName())
   315  	}
   316  	return out, errors.Trace(err)
   317  }
   318  
   319  func (k *kubernetesClient) updateRole(role *rbacv1.Role) (*rbacv1.Role, error) {
   320  	if k.namespace == "" {
   321  		return nil, errNoNamespace
   322  	}
   323  	out, err := k.client().RbacV1().Roles(k.namespace).Update(context.TODO(), role, v1.UpdateOptions{})
   324  	if k8serrors.IsNotFound(err) {
   325  		return nil, errors.NotFoundf("role %q", role.GetName())
   326  	}
   327  	return out, errors.Trace(err)
   328  }
   329  
   330  func (k *kubernetesClient) ensureRole(role *rbacv1.Role) (out *rbacv1.Role, cleanups []func(), err error) {
   331  	out, err = k.createRole(role)
   332  	if err == nil {
   333  		logger.Debugf("role %q created", out.GetName())
   334  		cleanups = append(cleanups, func() { _ = k.deleteRole(out.GetName(), out.GetUID()) })
   335  		return out, cleanups, nil
   336  	}
   337  	if !errors.IsAlreadyExists(err) {
   338  		return nil, cleanups, errors.Trace(err)
   339  	}
   340  	_, err = k.listRoles(utils.LabelsToSelector(role.GetLabels()))
   341  	if err != nil {
   342  		if errors.IsNotFound(err) {
   343  			// role.Name is already used for an existing role.
   344  			return nil, cleanups, errors.AlreadyExistsf("role %q", role.GetName())
   345  		}
   346  		return nil, cleanups, errors.Trace(err)
   347  	}
   348  	out, err = k.updateRole(role)
   349  	logger.Debugf("updating role %q", role.GetName())
   350  	return out, cleanups, errors.Trace(err)
   351  }
   352  
   353  func (k *kubernetesClient) getRole(name string) (*rbacv1.Role, error) {
   354  	if k.namespace == "" {
   355  		return nil, errNoNamespace
   356  	}
   357  	out, err := k.client().RbacV1().Roles(k.namespace).Get(context.TODO(), name, v1.GetOptions{})
   358  	if k8serrors.IsNotFound(err) {
   359  		return nil, errors.NotFoundf("role %q", name)
   360  	}
   361  	return out, errors.Trace(err)
   362  }
   363  
   364  func (k *kubernetesClient) deleteRole(name string, uid types.UID) error {
   365  	if k.namespace == "" {
   366  		return errNoNamespace
   367  	}
   368  	err := k.client().RbacV1().Roles(k.namespace).Delete(context.TODO(), name, utils.NewPreconditionDeleteOptions(uid))
   369  	if k8serrors.IsNotFound(err) {
   370  		return nil
   371  	}
   372  	return errors.Trace(err)
   373  }
   374  
   375  func (k *kubernetesClient) deleteRoles(selectors ...k8slabels.Selector) error {
   376  	if k.namespace == "" {
   377  		return errNoNamespace
   378  	}
   379  	for _, selector := range selectors {
   380  		err := k.client().RbacV1().Roles(k.namespace).DeleteCollection(
   381  			context.TODO(),
   382  			v1.DeleteOptions{
   383  				PropagationPolicy: constants.DefaultPropagationPolicy(),
   384  			}, v1.ListOptions{
   385  				LabelSelector: selector.String(),
   386  			})
   387  		if !k8serrors.IsNotFound(err) {
   388  			return errors.Trace(err)
   389  		}
   390  	}
   391  	return nil
   392  }
   393  
   394  func (k *kubernetesClient) listRoles(selector k8slabels.Selector) ([]rbacv1.Role, error) {
   395  	if k.namespace == "" {
   396  		return nil, errNoNamespace
   397  	}
   398  	listOps := v1.ListOptions{
   399  		LabelSelector: selector.String(),
   400  	}
   401  	rList, err := k.client().RbacV1().Roles(k.namespace).List(context.TODO(), listOps)
   402  	if err != nil {
   403  		return nil, errors.Trace(err)
   404  	}
   405  	if len(rList.Items) == 0 {
   406  		return nil, errors.NotFoundf("role with selector %q", selector)
   407  	}
   408  	return rList.Items, nil
   409  }
   410  
   411  func (k *kubernetesClient) createClusterRole(clusterrole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
   412  	if k.namespace == "" {
   413  		return nil, errNoNamespace
   414  	}
   415  	utils.PurifyResource(clusterrole)
   416  	out, err := k.client().RbacV1().ClusterRoles().Create(context.TODO(), clusterrole, v1.CreateOptions{})
   417  	if k8serrors.IsAlreadyExists(err) {
   418  		return nil, errors.AlreadyExistsf("clusterrole %q", clusterrole.GetName())
   419  	}
   420  	return out, errors.Trace(err)
   421  }
   422  
   423  func (k *kubernetesClient) updateClusterRole(clusterrole *rbacv1.ClusterRole) (*rbacv1.ClusterRole, error) {
   424  	if k.namespace == "" {
   425  		return nil, errNoNamespace
   426  	}
   427  	out, err := k.client().RbacV1().ClusterRoles().Update(context.TODO(), clusterrole, v1.UpdateOptions{})
   428  	if k8serrors.IsNotFound(err) {
   429  		return nil, errors.NotFoundf("clusterrole %q", clusterrole.GetName())
   430  	}
   431  	return out, errors.Trace(err)
   432  }
   433  
   434  func (k *kubernetesClient) getClusterRole(name string) (*rbacv1.ClusterRole, error) {
   435  	if k.namespace == "" {
   436  		return nil, errNoNamespace
   437  	}
   438  	out, err := k.client().RbacV1().ClusterRoles().Get(context.TODO(), name, v1.GetOptions{})
   439  	if k8serrors.IsNotFound(err) {
   440  		return nil, errors.NotFoundf("clusterrole %q", name)
   441  	}
   442  	return out, errors.Trace(err)
   443  }
   444  
   445  func (k *kubernetesClient) deleteClusterRoles(selector k8slabels.Selector) error {
   446  	err := k.client().RbacV1().ClusterRoles().DeleteCollection(context.TODO(), v1.DeleteOptions{
   447  		PropagationPolicy: constants.DefaultPropagationPolicy(),
   448  	}, v1.ListOptions{
   449  		LabelSelector: selector.String(),
   450  	})
   451  	if k8serrors.IsNotFound(err) {
   452  		return nil
   453  	}
   454  	return errors.Trace(err)
   455  }
   456  
   457  func (k *kubernetesClient) listClusterRoles(selector k8slabels.Selector) ([]rbacv1.ClusterRole, error) {
   458  	listOps := v1.ListOptions{
   459  		LabelSelector: selector.String(),
   460  	}
   461  	cRList, err := k.client().RbacV1().ClusterRoles().List(context.TODO(), listOps)
   462  	if err != nil {
   463  		return nil, errors.Trace(err)
   464  	}
   465  	if len(cRList.Items) == 0 {
   466  		return nil, errors.NotFoundf("cluster role with selector %q", selector)
   467  	}
   468  	return cRList.Items, nil
   469  }
   470  
   471  func (k *kubernetesClient) createRoleBinding(rb *rbacv1.RoleBinding) (*rbacv1.RoleBinding, error) {
   472  	if k.namespace == "" {
   473  		return nil, errNoNamespace
   474  	}
   475  	utils.PurifyResource(rb)
   476  	out, err := k.client().RbacV1().RoleBindings(k.namespace).Create(context.TODO(), rb, v1.CreateOptions{})
   477  	if k8serrors.IsAlreadyExists(err) {
   478  		return nil, errors.AlreadyExistsf("role binding %q", rb.GetName())
   479  	}
   480  	return out, errors.Trace(err)
   481  }
   482  
   483  func ensureResourceDeleted(clock jujuclock.Clock, getResource func() error) error {
   484  	notReadyYetErr := errors.New("resource is still being deleted")
   485  	deletionChecker := func() error {
   486  		err := getResource()
   487  		if errors.IsNotFound(err) {
   488  			return nil
   489  		}
   490  		if err == nil {
   491  			return notReadyYetErr
   492  		}
   493  		return errors.Trace(err)
   494  	}
   495  
   496  	err := retry.Call(retry.CallArgs{
   497  		Attempts: 10,
   498  		Delay:    2 * time.Second,
   499  		Clock:    clock,
   500  		Func:     deletionChecker,
   501  		IsFatalError: func(err error) bool {
   502  			return err != nil && err != notReadyYetErr
   503  		},
   504  		NotifyFunc: func(error, int) {
   505  			logger.Debugf("waiting for resource to be deleted")
   506  		},
   507  	})
   508  	return errors.Trace(err)
   509  }
   510  
   511  func isRoleBindingEqual(a, b rbacv1.RoleBinding) bool {
   512  	sortSubjects := func(s []rbacv1.Subject) {
   513  		sort.Slice(s, func(i, j int) bool {
   514  			return s[i].Name+s[i].Namespace+s[i].Kind > s[j].Name+s[j].Namespace+s[j].Kind
   515  		})
   516  	}
   517  	sortSubjects(a.Subjects)
   518  	sortSubjects(b.Subjects)
   519  
   520  	// We don't compare labels.
   521  	return reflect.DeepEqual(a.RoleRef, b.RoleRef) &&
   522  		reflect.DeepEqual(a.Subjects, b.Subjects) &&
   523  		reflect.DeepEqual(a.ObjectMeta.Annotations, b.ObjectMeta.Annotations)
   524  }
   525  
   526  func (k *kubernetesClient) ensureRoleBinding(rb *rbacv1.RoleBinding) (out *rbacv1.RoleBinding, cleanups []func(), err error) {
   527  	isFirstDeploy := false
   528  	// RoleRef is immutable, so delete first then re-create.
   529  	existing, err := k.getRoleBinding(rb.GetName())
   530  	if errors.Is(err, errors.NotFound) {
   531  		isFirstDeploy = true
   532  	} else if err != nil {
   533  		return nil, cleanups, errors.Trace(err)
   534  	}
   535  	if existing != nil {
   536  		if isRoleBindingEqual(*existing, *rb) {
   537  			return existing, cleanups, nil
   538  		}
   539  		name := existing.GetName()
   540  		UID := existing.GetUID()
   541  		if err := k.deleteRoleBinding(name, UID); err != nil {
   542  			return nil, cleanups, errors.Trace(err)
   543  		}
   544  
   545  		if err := ensureResourceDeleted(
   546  			k.clock,
   547  			func() error {
   548  				_, err := k.getRoleBinding(name)
   549  				return errors.Trace(err)
   550  			},
   551  		); err != nil {
   552  			return nil, cleanups, errors.Trace(err)
   553  		}
   554  	}
   555  	out, err = k.createRoleBinding(rb)
   556  	if err != nil {
   557  		return nil, cleanups, errors.Trace(err)
   558  	}
   559  	if isFirstDeploy {
   560  		// only do cleanup for the first time, don't do this for existing deployments.
   561  		cleanups = append(cleanups, func() { _ = k.deleteRoleBinding(out.GetName(), out.GetUID()) })
   562  	}
   563  	logger.Debugf("role binding %q created", rb.GetName())
   564  	return out, cleanups, nil
   565  }
   566  
   567  func (k *kubernetesClient) getRoleBinding(name string) (*rbacv1.RoleBinding, error) {
   568  	if k.namespace == "" {
   569  		return nil, errNoNamespace
   570  	}
   571  	out, err := k.client().RbacV1().RoleBindings(k.namespace).Get(context.TODO(), name, v1.GetOptions{})
   572  	if k8serrors.IsNotFound(err) {
   573  		return nil, errors.NotFoundf("role binding %q", name)
   574  	}
   575  	return out, errors.Trace(err)
   576  }
   577  
   578  func (k *kubernetesClient) deleteRoleBinding(name string, uid types.UID) error {
   579  	if k.namespace == "" {
   580  		return errNoNamespace
   581  	}
   582  	err := k.client().RbacV1().RoleBindings(k.namespace).Delete(context.TODO(), name, utils.NewPreconditionDeleteOptions(uid))
   583  	if k8serrors.IsNotFound(err) {
   584  		return nil
   585  	}
   586  	return errors.Trace(err)
   587  }
   588  
   589  func (k *kubernetesClient) deleteRoleBindings(selectors ...k8slabels.Selector) error {
   590  	if k.namespace == "" {
   591  		return errNoNamespace
   592  	}
   593  	for _, selector := range selectors {
   594  		err := k.client().RbacV1().RoleBindings(k.namespace).DeleteCollection(
   595  			context.TODO(),
   596  			v1.DeleteOptions{
   597  				PropagationPolicy: constants.DefaultPropagationPolicy(),
   598  			}, v1.ListOptions{
   599  				LabelSelector: selector.String(),
   600  			})
   601  		if !k8serrors.IsNotFound(err) {
   602  			return errors.Trace(err)
   603  		}
   604  	}
   605  	return nil
   606  }
   607  
   608  func (k *kubernetesClient) deleteClusterRoleBindings(selector k8slabels.Selector) error {
   609  	err := k.client().RbacV1().ClusterRoleBindings().DeleteCollection(context.TODO(), v1.DeleteOptions{
   610  		PropagationPolicy: constants.DefaultPropagationPolicy(),
   611  	}, v1.ListOptions{
   612  		LabelSelector: selector.String(),
   613  	})
   614  	if k8serrors.IsNotFound(err) {
   615  		return nil
   616  	}
   617  	return errors.Trace(err)
   618  }
   619  
   620  func (k *kubernetesClient) listClusterRoleBindings(selector k8slabels.Selector) ([]rbacv1.ClusterRoleBinding, error) {
   621  	listOps := v1.ListOptions{
   622  		LabelSelector: selector.String(),
   623  	}
   624  	cRBList, err := k.client().RbacV1().ClusterRoleBindings().List(context.TODO(), listOps)
   625  	if err != nil {
   626  		return nil, errors.Trace(err)
   627  	}
   628  	if len(cRBList.Items) == 0 {
   629  		return nil, errors.NotFoundf("cluster role binding with selector %q", selector)
   630  	}
   631  	return cRBList.Items, nil
   632  }
   633  
   634  // TODO: make this configurable.
   635  var expiresInSeconds = int64(60 * 10)
   636  
   637  // EnsureSecretAccessToken ensures the RBAC resources created and updated for the provided resource name.
   638  func (k *kubernetesClient) EnsureSecretAccessToken(tag names.Tag, owned, read, removed []string) (string, error) {
   639  	appName := tag.Id()
   640  	if tag.Kind() == names.UnitTagKind {
   641  		appName, _ = names.UnitApplication(tag.Id())
   642  	}
   643  	labels := utils.LabelsForApp(appName, k.IsLegacyLabels())
   644  
   645  	objMeta := v1.ObjectMeta{
   646  		Name:      tag.String(),
   647  		Labels:    labels,
   648  		Namespace: k.namespace,
   649  	}
   650  
   651  	sa := &core.ServiceAccount{
   652  		ObjectMeta:                   objMeta,
   653  		AutomountServiceAccountToken: boolPtr(true),
   654  	}
   655  	_, _, err := k.ensureServiceAccount(sa)
   656  	if err != nil {
   657  		return "", errors.Annotatef(err, "cannot ensure service account %q", sa.GetName())
   658  	}
   659  
   660  	if err := k.ensureBindingForSecretAccessToken(sa, objMeta, owned, read, removed); err != nil {
   661  		return "", errors.Trace(err)
   662  	}
   663  
   664  	treq := &authenticationv1.TokenRequest{
   665  		Spec: authenticationv1.TokenRequestSpec{
   666  			ExpirationSeconds: &expiresInSeconds,
   667  		},
   668  	}
   669  	tr, err := k.client().CoreV1().ServiceAccounts(k.namespace).CreateToken(context.TODO(), sa.Name, treq, v1.CreateOptions{})
   670  	if err != nil {
   671  		return "", errors.Annotatef(err, "cannot request a token for %q", sa.Name)
   672  	}
   673  	return tr.Status.Token, nil
   674  }
   675  
   676  func (k *kubernetesClient) ensureClusterBindingForSecretAccessToken(sa *core.ServiceAccount, objMeta v1.ObjectMeta, owned, read, removed []string) error {
   677  	objMeta.Name = fmt.Sprintf("%s-%s", k.namespace, objMeta.Name)
   678  	clusterRole, err := k.getClusterRole(objMeta.Name)
   679  	if err != nil && !errors.Is(err, errors.NotFound) {
   680  		return errors.Trace(err)
   681  	}
   682  	if errors.Is(err, errors.NotFound) {
   683  		clusterRole, err = k.createClusterRole(
   684  			&rbacv1.ClusterRole{
   685  				ObjectMeta: objMeta,
   686  				Rules:      rulesForSecretAccess(k.namespace, true, nil, owned, read, removed),
   687  			},
   688  		)
   689  	} else {
   690  		clusterRole.Rules = rulesForSecretAccess(k.namespace, true, clusterRole.Rules, owned, read, removed)
   691  		clusterRole, err = k.updateClusterRole(clusterRole)
   692  	}
   693  	if err != nil {
   694  		return errors.Trace(err)
   695  	}
   696  	bindingSpec := &rbacv1.ClusterRoleBinding{
   697  		ObjectMeta: objMeta,
   698  		RoleRef: rbacv1.RoleRef{
   699  			APIGroup: "rbac.authorization.k8s.io",
   700  			Kind:     "ClusterRole",
   701  			Name:     clusterRole.Name,
   702  		},
   703  		Subjects: []rbacv1.Subject{
   704  			{
   705  				Kind:      "ServiceAccount",
   706  				Name:      sa.Name,
   707  				Namespace: sa.Namespace,
   708  			},
   709  		},
   710  	}
   711  	clusterRoleBinding := resources.NewClusterRoleBinding(bindingSpec.Name, bindingSpec)
   712  	_, err = clusterRoleBinding.Ensure(context.TODO(), k.client(), resources.ClaimJujuOwnership)
   713  	if err != nil {
   714  		return errors.Trace(err)
   715  	}
   716  
   717  	// Ensure role binding exists before we return to avoid a race where a client
   718  	// attempts to perform an operation before the role is allowed.
   719  	return errors.Trace(retry.Call(retry.CallArgs{
   720  		Func: func() error {
   721  			api := k.client().RbacV1().ClusterRoleBindings()
   722  			_, err := api.Get(context.TODO(), clusterRoleBinding.Name, v1.GetOptions{ResourceVersion: clusterRoleBinding.ResourceVersion})
   723  			if k8serrors.IsNotFound(err) {
   724  				return errors.NewNotFound(err, "k8s")
   725  			}
   726  			return errors.Trace(err)
   727  		},
   728  		IsFatalError: func(err error) bool {
   729  			return !errors.Is(err, errors.NotFound)
   730  		},
   731  		Clock:    jujuclock.WallClock,
   732  		Attempts: 5,
   733  		Delay:    time.Second,
   734  	}))
   735  }
   736  
   737  func (k *kubernetesClient) ensureBindingForSecretAccessToken(sa *core.ServiceAccount, objMeta v1.ObjectMeta, owned, read, removed []string) error {
   738  	if k.Config().Name() == environsbootstrap.ControllerModelName {
   739  		return k.ensureClusterBindingForSecretAccessToken(sa, objMeta, owned, read, removed)
   740  	}
   741  
   742  	role, err := k.getRole(objMeta.Name)
   743  	if err != nil && !errors.Is(err, errors.NotFound) {
   744  		return errors.Trace(err)
   745  	}
   746  	if errors.Is(err, errors.NotFound) {
   747  		role, err = k.createRole(
   748  			&rbacv1.Role{
   749  				ObjectMeta: objMeta,
   750  				Rules:      rulesForSecretAccess(k.namespace, false, nil, owned, read, removed),
   751  			},
   752  		)
   753  	} else {
   754  		role.Rules = rulesForSecretAccess(k.namespace, false, role.Rules, owned, read, removed)
   755  		role, err = k.updateRole(role)
   756  	}
   757  	if err != nil {
   758  		return errors.Trace(err)
   759  	}
   760  	bindingSpec := &rbacv1.RoleBinding{
   761  		ObjectMeta: objMeta,
   762  		RoleRef: rbacv1.RoleRef{
   763  			APIGroup: "rbac.authorization.k8s.io",
   764  			Kind:     "Role",
   765  			Name:     role.Name,
   766  		},
   767  		Subjects: []rbacv1.Subject{
   768  			{
   769  				Kind:      "ServiceAccount",
   770  				Name:      sa.Name,
   771  				Namespace: sa.Namespace,
   772  			},
   773  		},
   774  	}
   775  	roleBinding := resources.NewRoleBinding(bindingSpec.Name, bindingSpec.Namespace, bindingSpec)
   776  	err = roleBinding.Apply(context.TODO(), k.client())
   777  	if err != nil {
   778  		return errors.Trace(err)
   779  	}
   780  
   781  	// Ensure role binding exists before we return to avoid a race where a client
   782  	// attempts to perform an operation before the role is allowed.
   783  	return errors.Trace(retry.Call(retry.CallArgs{
   784  		Func: func() error {
   785  			api := k.client().RbacV1().RoleBindings(k.namespace)
   786  			_, err := api.Get(context.TODO(), roleBinding.Name, v1.GetOptions{ResourceVersion: roleBinding.ResourceVersion})
   787  			if k8serrors.IsNotFound(err) {
   788  				return errors.NewNotFound(err, "k8s")
   789  			}
   790  			return errors.Trace(err)
   791  		},
   792  		IsFatalError: func(err error) bool {
   793  			return !errors.Is(err, errors.NotFound)
   794  		},
   795  		Clock:    jujuclock.WallClock,
   796  		Attempts: 5,
   797  		Delay:    time.Second,
   798  	}))
   799  }
   800  
   801  func cleanRules(existing []rbacv1.PolicyRule, shouldRemove func(string) bool) []rbacv1.PolicyRule {
   802  	if len(existing) == 0 {
   803  		return nil
   804  	}
   805  
   806  	i := 0
   807  	for _, r := range existing {
   808  		if len(r.ResourceNames) == 1 && shouldRemove(r.ResourceNames[0]) {
   809  			continue
   810  		}
   811  		existing[i] = r
   812  		i++
   813  	}
   814  	return existing[:i]
   815  }
   816  
   817  func rulesForSecretAccess(
   818  	namespace string, isControllerModel bool,
   819  	existing []rbacv1.PolicyRule, owned, read, removed []string,
   820  ) []rbacv1.PolicyRule {
   821  	if len(existing) == 0 {
   822  		existing = []rbacv1.PolicyRule{
   823  			{
   824  				APIGroups: []string{rbacv1.APIGroupAll},
   825  				Resources: []string{"secrets"},
   826  				Verbs: []string{
   827  					"create",
   828  					"patch", // TODO: we really should only allow "create" but not patch  but currently we uses .Apply() which requres patch!!!
   829  				},
   830  			},
   831  		}
   832  		if isControllerModel {
   833  			// We need to be able to list/get all namespaces for units in controller model.
   834  			existing = append(existing, rbacv1.PolicyRule{
   835  				APIGroups: []string{rbacv1.APIGroupAll},
   836  				Resources: []string{"namespaces"},
   837  				Verbs:     []string{"get", "list"},
   838  			})
   839  		} else {
   840  			// We just need to be able to list/get our own namespace for units in other models.
   841  			existing = append(existing, rbacv1.PolicyRule{
   842  				APIGroups:     []string{rbacv1.APIGroupAll},
   843  				Resources:     []string{"namespaces"},
   844  				Verbs:         []string{"get", "list"},
   845  				ResourceNames: []string{namespace},
   846  			})
   847  		}
   848  	}
   849  
   850  	ownedIDs := set.NewStrings(owned...)
   851  	readIDs := set.NewStrings(read...)
   852  	removedIDs := set.NewStrings(removed...)
   853  
   854  	existing = cleanRules(existing,
   855  		func(s string) bool {
   856  			return ownedIDs.Contains(s) || readIDs.Contains(s) || removedIDs.Contains(s)
   857  		},
   858  	)
   859  
   860  	for _, rName := range owned {
   861  		if removedIDs.Contains(rName) {
   862  			continue
   863  		}
   864  		existing = append(existing, rbacv1.PolicyRule{
   865  			APIGroups:     []string{rbacv1.APIGroupAll},
   866  			Resources:     []string{"secrets"},
   867  			Verbs:         []string{rbacv1.VerbAll},
   868  			ResourceNames: []string{rName},
   869  		})
   870  	}
   871  	for _, rName := range read {
   872  		if removedIDs.Contains(rName) {
   873  			continue
   874  		}
   875  		existing = append(existing, rbacv1.PolicyRule{
   876  			APIGroups:     []string{rbacv1.APIGroupAll},
   877  			Resources:     []string{"secrets"},
   878  			Verbs:         []string{"get"},
   879  			ResourceNames: []string{rName},
   880  		})
   881  	}
   882  	return existing
   883  }
   884  
   885  // RemoveSecretAccessToken removes the RBAC resources for the provided resource name.
   886  func (k *kubernetesClient) RemoveSecretAccessToken(unit names.Tag) error {
   887  	name := unit.String()
   888  	if err := k.deleteRoleBinding(name, ""); err != nil {
   889  		logger.Warningf("cannot delete service account %q", name)
   890  	}
   891  	if err := k.deleteRole(name, ""); err != nil {
   892  		logger.Warningf("cannot delete service account %q", name)
   893  	}
   894  	if err := k.deleteServiceAccount(name, ""); err != nil {
   895  		logger.Warningf("cannot delete service account %q", name)
   896  	}
   897  	return nil
   898  }