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

     1  // Copyright 2018 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provider_test
     5  
     6  import (
     7  	"github.com/juju/names/v5"
     8  	jc "github.com/juju/testing/checkers"
     9  	"go.uber.org/mock/gomock"
    10  	gc "gopkg.in/check.v1"
    11  	authenticationv1 "k8s.io/api/authentication/v1"
    12  	core "k8s.io/api/core/v1"
    13  	rbacv1 "k8s.io/api/rbac/v1"
    14  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    15  	"k8s.io/apimachinery/pkg/types"
    16  
    17  	"github.com/juju/juju/caas/kubernetes/provider"
    18  	k8sconstants "github.com/juju/juju/caas/kubernetes/provider/constants"
    19  	environsbootstrap "github.com/juju/juju/environs/bootstrap"
    20  	"github.com/juju/juju/environs/config"
    21  	"github.com/juju/juju/testing"
    22  	coretesting "github.com/juju/juju/testing"
    23  )
    24  
    25  var _ = gc.Suite(&rbacSuite{})
    26  
    27  type rbacSuite struct {
    28  	BaseSuite
    29  }
    30  
    31  func (s *rbacSuite) TestEnsureSecretAccessTokenCreate(c *gc.C) {
    32  	ctrl := s.setupController(c)
    33  	defer ctrl.Finish()
    34  
    35  	tag := names.NewUnitTag("gitlab/0")
    36  
    37  	objMeta := v1.ObjectMeta{
    38  		Name:      tag.String(),
    39  		Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "gitlab"},
    40  		Namespace: s.namespace,
    41  	}
    42  	automountServiceAccountToken := true
    43  	sa := &core.ServiceAccount{
    44  		ObjectMeta:                   objMeta,
    45  		AutomountServiceAccountToken: &automountServiceAccountToken,
    46  	}
    47  	role := &rbacv1.Role{
    48  		ObjectMeta: objMeta,
    49  		Rules: []rbacv1.PolicyRule{
    50  			{
    51  				Verbs:     []string{"create", "patch"},
    52  				APIGroups: []string{"*"},
    53  				Resources: []string{"secrets"},
    54  			},
    55  			{
    56  				Verbs:         []string{"get", "list"},
    57  				APIGroups:     []string{"*"},
    58  				Resources:     []string{"namespaces"},
    59  				ResourceNames: []string{"test"},
    60  			},
    61  		},
    62  	}
    63  	roleBinding := &rbacv1.RoleBinding{
    64  		ObjectMeta: objMeta,
    65  		RoleRef: rbacv1.RoleRef{
    66  			APIGroup: "rbac.authorization.k8s.io",
    67  			Kind:     "Role",
    68  			Name:     role.Name,
    69  		},
    70  		Subjects: []rbacv1.Subject{
    71  			{
    72  				Kind:      "ServiceAccount",
    73  				Name:      sa.Name,
    74  				Namespace: sa.Namespace,
    75  			},
    76  		},
    77  	}
    78  	expiresInSeconds := int64(60 * 10)
    79  	treq := &authenticationv1.TokenRequest{
    80  		Spec: authenticationv1.TokenRequestSpec{
    81  			ExpirationSeconds: &expiresInSeconds,
    82  		},
    83  	}
    84  	gomock.InOrder(
    85  		s.mockServiceAccounts.EXPECT().Create(gomock.Any(), sa, v1.CreateOptions{}).
    86  			Return(sa, nil),
    87  		s.mockRoles.EXPECT().Get(gomock.Any(), "unit-gitlab-0", v1.GetOptions{}).Return(nil, s.k8sNotFoundError()),
    88  		s.mockRoles.EXPECT().Create(gomock.Any(), role, v1.CreateOptions{}).Return(role, nil),
    89  		s.mockRoleBindings.EXPECT().Patch(
    90  			gomock.Any(), "unit-gitlab-0", types.StrategicMergePatchType, gomock.Any(), v1.PatchOptions{FieldManager: "juju"},
    91  		).Return(nil, s.k8sNotFoundError()),
    92  		s.mockRoleBindings.EXPECT().Create(gomock.Any(), roleBinding, v1.CreateOptions{FieldManager: "juju"}).Return(roleBinding, nil),
    93  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "unit-gitlab-0", v1.GetOptions{}).Return(roleBinding, nil),
    94  		s.mockServiceAccounts.EXPECT().CreateToken(gomock.Any(), "unit-gitlab-0", treq, v1.CreateOptions{}).Return(
    95  			&authenticationv1.TokenRequest{Status: authenticationv1.TokenRequestStatus{Token: "token"}}, nil,
    96  		),
    97  	)
    98  
    99  	token, err := s.broker.EnsureSecretAccessToken(tag, nil, nil, nil)
   100  	c.Assert(err, jc.ErrorIsNil)
   101  	c.Assert(token, gc.Equals, "token")
   102  }
   103  
   104  func (s *rbacSuite) switchToControllerModel(c *gc.C) {
   105  	cfg, err := config.New(config.UseDefaults, coretesting.FakeConfig().Merge(coretesting.Attrs{
   106  		config.NameKey:                  environsbootstrap.ControllerModelName,
   107  		k8sconstants.OperatorStorageKey: "",
   108  		k8sconstants.WorkloadStorageKey: "",
   109  	}))
   110  	c.Assert(err, jc.ErrorIsNil)
   111  	s.cfg = cfg
   112  	s.namespace = "controller-k1"
   113  }
   114  
   115  func (s *rbacSuite) TestEnsureSecretAccessTokenControllerModelCreate(c *gc.C) {
   116  	s.switchToControllerModel(c)
   117  	ctrl := s.setupController(c)
   118  	defer ctrl.Finish()
   119  
   120  	tag := names.NewUnitTag("gitlab/0")
   121  
   122  	objMeta := v1.ObjectMeta{
   123  		Name:      tag.String(),
   124  		Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "gitlab"},
   125  		Namespace: s.namespace,
   126  	}
   127  	automountServiceAccountToken := true
   128  	sa := &core.ServiceAccount{
   129  		ObjectMeta:                   objMeta,
   130  		AutomountServiceAccountToken: &automountServiceAccountToken,
   131  	}
   132  	objMeta.Name = s.namespace + "-" + tag.String()
   133  	clusterrole := &rbacv1.ClusterRole{
   134  		ObjectMeta: objMeta,
   135  		Rules: []rbacv1.PolicyRule{
   136  			{
   137  				Verbs:     []string{"create", "patch"},
   138  				APIGroups: []string{"*"},
   139  				Resources: []string{"secrets"},
   140  			},
   141  			{
   142  				Verbs:     []string{"get", "list"},
   143  				APIGroups: []string{"*"},
   144  				Resources: []string{"namespaces"},
   145  			},
   146  		},
   147  	}
   148  	clusterroleBinding := &rbacv1.ClusterRoleBinding{
   149  		ObjectMeta: objMeta,
   150  		RoleRef: rbacv1.RoleRef{
   151  			APIGroup: "rbac.authorization.k8s.io",
   152  			Kind:     "ClusterRole",
   153  			Name:     clusterrole.Name,
   154  		},
   155  		Subjects: []rbacv1.Subject{
   156  			{
   157  				Kind:      "ServiceAccount",
   158  				Name:      sa.Name,
   159  				Namespace: sa.Namespace,
   160  			},
   161  		},
   162  	}
   163  	expiresInSeconds := int64(60 * 10)
   164  	treq := &authenticationv1.TokenRequest{
   165  		Spec: authenticationv1.TokenRequestSpec{
   166  			ExpirationSeconds: &expiresInSeconds,
   167  		},
   168  	}
   169  	gomock.InOrder(
   170  		s.mockServiceAccounts.EXPECT().Create(gomock.Any(), sa, v1.CreateOptions{}).
   171  			Return(sa, nil),
   172  		s.mockClusterRoles.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(nil, s.k8sNotFoundError()),
   173  		s.mockClusterRoles.EXPECT().Create(gomock.Any(), clusterrole, v1.CreateOptions{}).Return(clusterrole, nil),
   174  		s.mockClusterRoleBindings.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(nil, s.k8sNotFoundError()),
   175  		s.mockClusterRoleBindings.EXPECT().Patch(
   176  			gomock.Any(), "controller-k1-unit-gitlab-0", types.StrategicMergePatchType, gomock.Any(), v1.PatchOptions{FieldManager: "juju"},
   177  		).Return(nil, s.k8sNotFoundError()),
   178  		s.mockClusterRoleBindings.EXPECT().Create(gomock.Any(), clusterroleBinding, v1.CreateOptions{FieldManager: "juju"}).Return(clusterroleBinding, nil),
   179  		s.mockClusterRoleBindings.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(clusterroleBinding, nil),
   180  		s.mockServiceAccounts.EXPECT().CreateToken(gomock.Any(), "unit-gitlab-0", treq, v1.CreateOptions{}).Return(
   181  			&authenticationv1.TokenRequest{Status: authenticationv1.TokenRequestStatus{Token: "token"}}, nil,
   182  		),
   183  	)
   184  
   185  	token, err := s.broker.EnsureSecretAccessToken(tag, nil, nil, nil)
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(token, gc.Equals, "token")
   188  }
   189  
   190  func (s *rbacSuite) TestEnsureSecretAccessTokeUpdate(c *gc.C) {
   191  	ctrl := s.setupController(c)
   192  	defer ctrl.Finish()
   193  
   194  	tag := names.NewUnitTag("gitlab/0")
   195  
   196  	objMeta := v1.ObjectMeta{
   197  		Name:      tag.String(),
   198  		Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "gitlab"},
   199  		Namespace: s.namespace,
   200  	}
   201  	automountServiceAccountToken := true
   202  	sa := &core.ServiceAccount{
   203  		ObjectMeta:                   objMeta,
   204  		AutomountServiceAccountToken: &automountServiceAccountToken,
   205  	}
   206  	role := &rbacv1.Role{
   207  		ObjectMeta: objMeta,
   208  		Rules: []rbacv1.PolicyRule{
   209  			{
   210  				Verbs:     []string{"create", "patch"},
   211  				APIGroups: []string{"*"},
   212  				Resources: []string{"secrets"},
   213  			},
   214  			{
   215  				Verbs:         []string{"get", "list"},
   216  				APIGroups:     []string{"*"},
   217  				Resources:     []string{"namespaces"},
   218  				ResourceNames: []string{"test"},
   219  			},
   220  		},
   221  	}
   222  	roleBinding := &rbacv1.RoleBinding{
   223  		ObjectMeta: objMeta,
   224  		RoleRef: rbacv1.RoleRef{
   225  			APIGroup: "rbac.authorization.k8s.io",
   226  			Kind:     "Role",
   227  			Name:     role.Name,
   228  		},
   229  		Subjects: []rbacv1.Subject{
   230  			{
   231  				Kind:      "ServiceAccount",
   232  				Name:      sa.Name,
   233  				Namespace: sa.Namespace,
   234  			},
   235  		},
   236  	}
   237  	expiresInSeconds := int64(60 * 10)
   238  	treq := &authenticationv1.TokenRequest{
   239  		Spec: authenticationv1.TokenRequestSpec{
   240  			ExpirationSeconds: &expiresInSeconds,
   241  		},
   242  	}
   243  	gomock.InOrder(
   244  		s.mockServiceAccounts.EXPECT().Create(gomock.Any(), sa, v1.CreateOptions{}).
   245  			Return(nil, s.k8sAlreadyExistsError()),
   246  		s.mockServiceAccounts.EXPECT().List(gomock.Any(), v1.ListOptions{
   247  			LabelSelector: "app.kubernetes.io/managed-by=juju,app.kubernetes.io/name=gitlab",
   248  		}).Return(&core.ServiceAccountList{Items: []core.ServiceAccount{*sa}}, nil),
   249  		s.mockServiceAccounts.EXPECT().Update(gomock.Any(), sa, v1.UpdateOptions{}).
   250  			Return(sa, nil),
   251  		s.mockRoles.EXPECT().Get(gomock.Any(), "unit-gitlab-0", v1.GetOptions{}).Return(role, nil),
   252  		s.mockRoles.EXPECT().Update(gomock.Any(), role, v1.UpdateOptions{}).Return(role, nil),
   253  		s.mockRoleBindings.EXPECT().Patch(
   254  			gomock.Any(), "unit-gitlab-0", types.StrategicMergePatchType, gomock.Any(), v1.PatchOptions{FieldManager: "juju"},
   255  		).Return(roleBinding, nil),
   256  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "unit-gitlab-0", v1.GetOptions{}).Return(roleBinding, nil),
   257  		s.mockServiceAccounts.EXPECT().CreateToken(gomock.Any(), "unit-gitlab-0", treq, v1.CreateOptions{}).Return(
   258  			&authenticationv1.TokenRequest{Status: authenticationv1.TokenRequestStatus{Token: "token"}}, nil,
   259  		),
   260  	)
   261  
   262  	token, err := s.broker.EnsureSecretAccessToken(tag, nil, nil, nil)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	c.Assert(token, gc.Equals, "token")
   265  }
   266  
   267  func (s *rbacSuite) TestEnsureSecretAccessTokeControllerModelUpdate(c *gc.C) {
   268  	s.switchToControllerModel(c)
   269  	ctrl := s.setupController(c)
   270  	defer ctrl.Finish()
   271  
   272  	tag := names.NewUnitTag("gitlab/0")
   273  
   274  	objMeta := v1.ObjectMeta{
   275  		Name:      tag.String(),
   276  		Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "gitlab"},
   277  		Namespace: s.namespace,
   278  	}
   279  	automountServiceAccountToken := true
   280  	sa := &core.ServiceAccount{
   281  		ObjectMeta:                   objMeta,
   282  		AutomountServiceAccountToken: &automountServiceAccountToken,
   283  	}
   284  	objMeta.Name = s.namespace + "-" + tag.String()
   285  	clusterrole := &rbacv1.ClusterRole{
   286  		ObjectMeta: objMeta,
   287  		Rules: []rbacv1.PolicyRule{
   288  			{
   289  				Verbs:     []string{"create", "patch"},
   290  				APIGroups: []string{"*"},
   291  				Resources: []string{"secrets"},
   292  			},
   293  			{
   294  				Verbs:     []string{"get", "list"},
   295  				APIGroups: []string{"*"},
   296  				Resources: []string{"namespaces"},
   297  			},
   298  		},
   299  	}
   300  	clusterroleBinding := &rbacv1.ClusterRoleBinding{
   301  		ObjectMeta: objMeta,
   302  		RoleRef: rbacv1.RoleRef{
   303  			APIGroup: "rbac.authorization.k8s.io",
   304  			Kind:     "ClusterRole",
   305  			Name:     clusterrole.Name,
   306  		},
   307  		Subjects: []rbacv1.Subject{
   308  			{
   309  				Kind:      "ServiceAccount",
   310  				Name:      sa.Name,
   311  				Namespace: sa.Namespace,
   312  			},
   313  		},
   314  	}
   315  	expiresInSeconds := int64(60 * 10)
   316  	treq := &authenticationv1.TokenRequest{
   317  		Spec: authenticationv1.TokenRequestSpec{
   318  			ExpirationSeconds: &expiresInSeconds,
   319  		},
   320  	}
   321  	gomock.InOrder(
   322  		s.mockServiceAccounts.EXPECT().Create(gomock.Any(), sa, v1.CreateOptions{}).
   323  			Return(nil, s.k8sAlreadyExistsError()),
   324  		s.mockServiceAccounts.EXPECT().List(gomock.Any(), v1.ListOptions{
   325  			LabelSelector: "app.kubernetes.io/managed-by=juju,app.kubernetes.io/name=gitlab",
   326  		}).Return(&core.ServiceAccountList{Items: []core.ServiceAccount{*sa}}, nil),
   327  		s.mockServiceAccounts.EXPECT().Update(gomock.Any(), sa, v1.UpdateOptions{}).
   328  			Return(sa, nil),
   329  		s.mockClusterRoles.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(clusterrole, nil),
   330  		s.mockClusterRoles.EXPECT().Update(gomock.Any(), clusterrole, v1.UpdateOptions{}).Return(clusterrole, nil),
   331  		s.mockClusterRoleBindings.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(clusterroleBinding, nil),
   332  		s.mockClusterRoleBindings.EXPECT().Update(gomock.Any(), clusterroleBinding, v1.UpdateOptions{FieldManager: "juju"}).Return(clusterroleBinding, nil),
   333  		s.mockClusterRoleBindings.EXPECT().Get(gomock.Any(), "controller-k1-unit-gitlab-0", v1.GetOptions{}).Return(clusterroleBinding, nil),
   334  		s.mockServiceAccounts.EXPECT().CreateToken(gomock.Any(), "unit-gitlab-0", treq, v1.CreateOptions{}).Return(
   335  			&authenticationv1.TokenRequest{Status: authenticationv1.TokenRequestStatus{Token: "token"}}, nil,
   336  		),
   337  	)
   338  
   339  	token, err := s.broker.EnsureSecretAccessToken(tag, nil, nil, nil)
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	c.Assert(token, gc.Equals, "token")
   342  }
   343  
   344  func (s *rbacSuite) TestRulesForSecretAccessNew(c *gc.C) {
   345  	owned := []string{"owned-secret-1"}
   346  	read := []string{"read-secret-1"}
   347  	newPolicies := provider.RulesForSecretAccess("test", false, nil, owned, read, nil)
   348  	c.Assert(newPolicies, gc.DeepEquals, []rbacv1.PolicyRule{
   349  		{
   350  			Verbs:     []string{"create", "patch"},
   351  			APIGroups: []string{"*"},
   352  			Resources: []string{"secrets"},
   353  		},
   354  		{
   355  			Verbs:         []string{"get", "list"},
   356  			APIGroups:     []string{"*"},
   357  			Resources:     []string{"namespaces"},
   358  			ResourceNames: []string{"test"},
   359  		},
   360  		{
   361  			Verbs:         []string{"*"},
   362  			APIGroups:     []string{"*"},
   363  			Resources:     []string{"secrets"},
   364  			ResourceNames: []string{"owned-secret-1"},
   365  		},
   366  		{
   367  			Verbs:         []string{"get"},
   368  			APIGroups:     []string{"*"},
   369  			Resources:     []string{"secrets"},
   370  			ResourceNames: []string{"read-secret-1"},
   371  		},
   372  	})
   373  }
   374  
   375  func (s *rbacSuite) TestRulesForSecretAccessControllerModelNew(c *gc.C) {
   376  	owned := []string{"owned-secret-1"}
   377  	read := []string{"read-secret-1"}
   378  	newPolicies := provider.RulesForSecretAccess("test", true, nil, owned, read, nil)
   379  	c.Assert(newPolicies, gc.DeepEquals, []rbacv1.PolicyRule{
   380  		{
   381  			Verbs:     []string{"create", "patch"},
   382  			APIGroups: []string{"*"},
   383  			Resources: []string{"secrets"},
   384  		},
   385  		{
   386  			Verbs:     []string{"get", "list"},
   387  			APIGroups: []string{"*"},
   388  			Resources: []string{"namespaces"},
   389  		},
   390  		{
   391  			Verbs:         []string{"*"},
   392  			APIGroups:     []string{"*"},
   393  			Resources:     []string{"secrets"},
   394  			ResourceNames: []string{"owned-secret-1"},
   395  		},
   396  		{
   397  			Verbs:         []string{"get"},
   398  			APIGroups:     []string{"*"},
   399  			Resources:     []string{"secrets"},
   400  			ResourceNames: []string{"read-secret-1"},
   401  		},
   402  	})
   403  }
   404  
   405  func (s *rbacSuite) TestRulesForSecretAccessUpdate(c *gc.C) {
   406  	existing := []rbacv1.PolicyRule{
   407  		{
   408  			Verbs:     []string{"create", "patch"},
   409  			APIGroups: []string{"*"},
   410  			Resources: []string{"secrets"},
   411  		},
   412  		{
   413  			Verbs:         []string{"get", "list"},
   414  			APIGroups:     []string{"*"},
   415  			Resources:     []string{"namespaces"},
   416  			ResourceNames: []string{"test"},
   417  		},
   418  		{
   419  			Verbs:         []string{"*"},
   420  			APIGroups:     []string{"*"},
   421  			Resources:     []string{"secrets"},
   422  			ResourceNames: []string{"owned-secret-1"},
   423  		},
   424  		{
   425  			Verbs:         []string{"*"},
   426  			APIGroups:     []string{"*"},
   427  			Resources:     []string{"secrets"},
   428  			ResourceNames: []string{"removed-owned-secret"},
   429  		},
   430  		{
   431  			Verbs:         []string{"get"},
   432  			APIGroups:     []string{"*"},
   433  			Resources:     []string{"secrets"},
   434  			ResourceNames: []string{"read-secret-1"},
   435  		},
   436  		{
   437  			Verbs:         []string{"get"},
   438  			APIGroups:     []string{"*"},
   439  			Resources:     []string{"secrets"},
   440  			ResourceNames: []string{"removed-read-secret"},
   441  		},
   442  	}
   443  
   444  	owned := []string{"owned-secret-1", "owned-secret-2"}
   445  	read := []string{"read-secret-1", "read-secret-2"}
   446  	removed := []string{"removed-owned-secret", "removed-read-secret"}
   447  
   448  	newPolicies := provider.RulesForSecretAccess("test", false, existing, owned, read, removed)
   449  	c.Assert(newPolicies, gc.DeepEquals, []rbacv1.PolicyRule{
   450  		{
   451  			Verbs:     []string{"create", "patch"},
   452  			APIGroups: []string{"*"},
   453  			Resources: []string{"secrets"},
   454  		},
   455  		{
   456  			Verbs:         []string{"get", "list"},
   457  			APIGroups:     []string{"*"},
   458  			Resources:     []string{"namespaces"},
   459  			ResourceNames: []string{"test"},
   460  		},
   461  		{
   462  			Verbs:         []string{"*"},
   463  			APIGroups:     []string{"*"},
   464  			Resources:     []string{"secrets"},
   465  			ResourceNames: []string{"owned-secret-1"},
   466  		},
   467  		{
   468  			Verbs:         []string{"*"},
   469  			APIGroups:     []string{"*"},
   470  			Resources:     []string{"secrets"},
   471  			ResourceNames: []string{"owned-secret-2"},
   472  		},
   473  		{
   474  			Verbs:         []string{"get"},
   475  			APIGroups:     []string{"*"},
   476  			Resources:     []string{"secrets"},
   477  			ResourceNames: []string{"read-secret-1"},
   478  		},
   479  		{
   480  			Verbs:         []string{"get"},
   481  			APIGroups:     []string{"*"},
   482  			Resources:     []string{"secrets"},
   483  			ResourceNames: []string{"read-secret-2"},
   484  		},
   485  	})
   486  }
   487  
   488  func (s *rbacSuite) TestRulesForSecretAccessControllerModelUpdate(c *gc.C) {
   489  	existing := []rbacv1.PolicyRule{
   490  		{
   491  			Verbs:     []string{"create", "patch"},
   492  			APIGroups: []string{"*"},
   493  			Resources: []string{"secrets"},
   494  		},
   495  		{
   496  			Verbs:     []string{"get", "list"},
   497  			APIGroups: []string{"*"},
   498  			Resources: []string{"namespaces"},
   499  		},
   500  		{
   501  			Verbs:         []string{"*"},
   502  			APIGroups:     []string{"*"},
   503  			Resources:     []string{"secrets"},
   504  			ResourceNames: []string{"owned-secret-1"},
   505  		},
   506  		{
   507  			Verbs:         []string{"*"},
   508  			APIGroups:     []string{"*"},
   509  			Resources:     []string{"secrets"},
   510  			ResourceNames: []string{"removed-owned-secret"},
   511  		},
   512  		{
   513  			Verbs:         []string{"get"},
   514  			APIGroups:     []string{"*"},
   515  			Resources:     []string{"secrets"},
   516  			ResourceNames: []string{"read-secret-1"},
   517  		},
   518  		{
   519  			Verbs:         []string{"get"},
   520  			APIGroups:     []string{"*"},
   521  			Resources:     []string{"secrets"},
   522  			ResourceNames: []string{"removed-read-secret"},
   523  		},
   524  	}
   525  
   526  	owned := []string{"owned-secret-1", "owned-secret-2"}
   527  	read := []string{"read-secret-1", "read-secret-2"}
   528  	removed := []string{"removed-owned-secret", "removed-read-secret"}
   529  
   530  	newPolicies := provider.RulesForSecretAccess("test", true, existing, owned, read, removed)
   531  	c.Assert(newPolicies, gc.DeepEquals, []rbacv1.PolicyRule{
   532  		{
   533  			Verbs:     []string{"create", "patch"},
   534  			APIGroups: []string{"*"},
   535  			Resources: []string{"secrets"},
   536  		},
   537  		{
   538  			Verbs:     []string{"get", "list"},
   539  			APIGroups: []string{"*"},
   540  			Resources: []string{"namespaces"},
   541  		},
   542  		{
   543  			Verbs:         []string{"*"},
   544  			APIGroups:     []string{"*"},
   545  			Resources:     []string{"secrets"},
   546  			ResourceNames: []string{"owned-secret-1"},
   547  		},
   548  		{
   549  			Verbs:         []string{"*"},
   550  			APIGroups:     []string{"*"},
   551  			Resources:     []string{"secrets"},
   552  			ResourceNames: []string{"owned-secret-2"},
   553  		},
   554  		{
   555  			Verbs:         []string{"get"},
   556  			APIGroups:     []string{"*"},
   557  			Resources:     []string{"secrets"},
   558  			ResourceNames: []string{"read-secret-1"},
   559  		},
   560  		{
   561  			Verbs:         []string{"get"},
   562  			APIGroups:     []string{"*"},
   563  			Resources:     []string{"secrets"},
   564  			ResourceNames: []string{"read-secret-2"},
   565  		},
   566  	})
   567  }
   568  
   569  func (s *rbacSuite) TestEnsureRoleBinding(c *gc.C) {
   570  	ctrl := s.setupController(c)
   571  	defer ctrl.Finish()
   572  
   573  	rb1 := &rbacv1.RoleBinding{
   574  		ObjectMeta: v1.ObjectMeta{
   575  			Name:      "rb-name",
   576  			Namespace: "test",
   577  			Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"},
   578  			Annotations: map[string]string{
   579  				"fred":                  "mary",
   580  				"controller.juju.is/id": testing.ControllerTag.Id(),
   581  			},
   582  		},
   583  		RoleRef: rbacv1.RoleRef{
   584  			Name: "role-name",
   585  			Kind: "Role",
   586  		},
   587  		Subjects: []rbacv1.Subject{
   588  			{
   589  				Kind:      rbacv1.ServiceAccountKind,
   590  				Name:      "sa1",
   591  				Namespace: "test",
   592  			},
   593  			{
   594  				Kind:      rbacv1.ServiceAccountKind,
   595  				Name:      "sa2",
   596  				Namespace: "test",
   597  			},
   598  		},
   599  	}
   600  	rb1SubjectsInDifferentOrder := &rbacv1.RoleBinding{
   601  		ObjectMeta: v1.ObjectMeta{
   602  			Name:      "rb-name",
   603  			Namespace: "test",
   604  			Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"},
   605  			Annotations: map[string]string{
   606  				"fred":                  "mary",
   607  				"controller.juju.is/id": testing.ControllerTag.Id(),
   608  			},
   609  		},
   610  		RoleRef: rbacv1.RoleRef{
   611  			Name: "role-name",
   612  			Kind: "Role",
   613  		},
   614  		Subjects: []rbacv1.Subject{
   615  			{
   616  				Kind:      rbacv1.ServiceAccountKind,
   617  				Name:      "sa2",
   618  				Namespace: "test",
   619  			},
   620  			{
   621  				Kind:      rbacv1.ServiceAccountKind,
   622  				Name:      "sa1",
   623  				Namespace: "test",
   624  			},
   625  		},
   626  	}
   627  	rb2 := rbacv1.RoleBinding{
   628  		ObjectMeta: v1.ObjectMeta{
   629  			Name:      "rb-name",
   630  			Namespace: "test",
   631  			Labels:    map[string]string{"app.kubernetes.io/managed-by": "juju", "app.kubernetes.io/name": "app-name"},
   632  			Annotations: map[string]string{
   633  				"fred":                  "mary",
   634  				"controller.juju.is/id": testing.ControllerTag.Id(),
   635  			},
   636  		},
   637  		RoleRef: rbacv1.RoleRef{
   638  			Name: "role-name",
   639  			Kind: "Role",
   640  		},
   641  		Subjects: []rbacv1.Subject{
   642  			{
   643  				Kind:      rbacv1.ServiceAccountKind,
   644  				Name:      "sa2",
   645  				Namespace: "test",
   646  			},
   647  		},
   648  	}
   649  	rb2DifferentSubjects := rb2
   650  	rb2DifferentSubjects.Subjects = []rbacv1.Subject{
   651  		{
   652  			Kind:      rbacv1.ServiceAccountKind,
   653  			Name:      "sa3",
   654  			Namespace: "test",
   655  		},
   656  	}
   657  	rbUID := rb2DifferentSubjects.GetUID()
   658  	gomock.InOrder(
   659  		// Already exists, no change.
   660  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "rb-name", v1.GetOptions{}).
   661  			Return(rb1, nil),
   662  
   663  		// Already exists, but with same subjects in different order.
   664  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "rb-name", v1.GetOptions{}).
   665  			Return(rb1SubjectsInDifferentOrder, nil),
   666  
   667  		// No existing role binding, create one.
   668  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "rb-name", v1.GetOptions{}).
   669  			Return(nil, s.k8sNotFoundError()),
   670  		s.mockRoleBindings.EXPECT().Create(gomock.Any(), &rb2, v1.CreateOptions{}).Return(&rb2, nil),
   671  
   672  		// Already exists, but with different subjects, delete and create.
   673  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "rb-name", v1.GetOptions{}).
   674  			Return(&rb2DifferentSubjects, nil),
   675  		s.mockRoleBindings.EXPECT().Delete(gomock.Any(), "rb-name", s.deleteOptions(v1.DeletePropagationForeground, rbUID)).Return(nil),
   676  		s.mockRoleBindings.EXPECT().Get(gomock.Any(), "rb-name", v1.GetOptions{}).Return(nil, s.k8sNotFoundError()),
   677  		s.mockRoleBindings.EXPECT().Create(gomock.Any(), &rb2, v1.CreateOptions{}).Return(&rb2, nil),
   678  	)
   679  
   680  	_, _, err := s.broker.EnsureRoleBinding(rb1)
   681  	c.Assert(err, jc.ErrorIsNil)
   682  
   683  	_, _, err = s.broker.EnsureRoleBinding(rb1)
   684  	c.Assert(err, jc.ErrorIsNil)
   685  
   686  	_, _, err = s.broker.EnsureRoleBinding(&rb2)
   687  	c.Assert(err, jc.ErrorIsNil)
   688  
   689  	_, _, err = s.broker.EnsureRoleBinding(&rb2)
   690  	c.Assert(err, jc.ErrorIsNil)
   691  }