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 }