github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/caas/kubernetes/provider/modeloperator.go (about) 1 // Copyright 2020 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provider 5 6 import ( 7 "context" 8 "fmt" 9 "path/filepath" 10 "strconv" 11 12 jujuclock "github.com/juju/clock" 13 "github.com/juju/errors" 14 "github.com/juju/names/v5" 15 apps "k8s.io/api/apps/v1" 16 core "k8s.io/api/core/v1" 17 rbac "k8s.io/api/rbac/v1" 18 k8serrors "k8s.io/apimachinery/pkg/api/errors" 19 meta "k8s.io/apimachinery/pkg/apis/meta/v1" 20 "k8s.io/apimachinery/pkg/labels" 21 "k8s.io/apimachinery/pkg/util/intstr" 22 "k8s.io/client-go/kubernetes" 23 "k8s.io/utils/pointer" 24 25 "github.com/juju/juju/agent" 26 "github.com/juju/juju/caas" 27 "github.com/juju/juju/caas/kubernetes/provider/constants" 28 "github.com/juju/juju/caas/kubernetes/provider/proxy" 29 "github.com/juju/juju/caas/kubernetes/provider/resources" 30 "github.com/juju/juju/caas/kubernetes/provider/utils" 31 "github.com/juju/juju/core/paths" 32 coreresources "github.com/juju/juju/core/resources" 33 ) 34 35 // ModelOperatorBroker defines a broker for Executing Kubernetes ensure 36 // commands. This interfaces is scoped down to the exact components needed by 37 // the ensure model operator routines. 38 type ModelOperatorBroker interface { 39 // Client returns the Kubernetes client to use for model operator actions. 40 Client() kubernetes.Interface 41 42 // EnsureConfigMap ensures the supplied kubernetes config map exists in the 43 // targeted cluster. Error returned if this action is not able to be 44 // performed. 45 EnsureConfigMap(*core.ConfigMap) ([]func(), error) 46 47 // EnsureDeployment ensures the supplied kubernetes deployment object exists 48 // in the targeted cluster. Error returned if this action is not able to be 49 // performed. 50 EnsureDeployment(*apps.Deployment) ([]func(), error) 51 52 // EnsureRole ensures the supplied kubernetes role object exists in the 53 // targeted clusters namespace 54 EnsureRole(*rbac.Role) ([]func(), error) 55 56 // EnsureRoleBinding ensures the supplied kubernetes role binding object 57 // exists in the targetes clusters namespace 58 EnsureRoleBinding(*rbac.RoleBinding) ([]func(), error) 59 60 // EnsureService ensures the spplied kubernetes service object exists in the 61 // targeted cluster. Error returned if the action is not able to be 62 // performed. 63 EnsureService(*core.Service) ([]func(), error) 64 65 // EnsureServiceAccount ensures the supplied the kubernetes service account 66 // exists in the targets cluster. 67 EnsureServiceAccount(*core.ServiceAccount) ([]func(), error) 68 69 // Model returns the name of the current model being deployed to for the 70 // broker 71 Model() string 72 73 // Namespace returns the current default namespace targeted by this broker. 74 Namespace() string 75 76 // IsLegacyLabels indicates if this provider is operating on a legacy label schema 77 IsLegacyLabels() bool 78 } 79 80 // modelOperatorBrokerBridge provides a pluggable struct of funcs to implement 81 // the ModelOperatorBroker interface 82 type modelOperatorBrokerBridge struct { 83 client kubernetes.Interface 84 ensureConfigMap func(*core.ConfigMap) ([]func(), error) 85 ensureDeployment func(*apps.Deployment) ([]func(), error) 86 ensureRole func(*rbac.Role) ([]func(), error) 87 ensureRoleBinding func(*rbac.RoleBinding) ([]func(), error) 88 ensureService func(*core.Service) ([]func(), error) 89 ensureServiceAccount func(*core.ServiceAccount) ([]func(), error) 90 model func() string 91 namespace func() string 92 isLegacyLabels func() bool 93 } 94 95 const ( 96 modelOperatorPortLabel = "api" 97 98 EnvModelAgentCAASServiceName = "SERVICE_NAME" 99 EnvModelAgentCAASServiceNamespace = "SERVICE_NAMESPACE" 100 EnvModelAgentHTTPPort = "HTTP_PORT" 101 102 OperatorModelTarget = "model" 103 ) 104 105 var ( 106 // modelOperatorName is the model operator stack name used for deployment, service, RBAC resources. 107 modelOperatorName = "modeloperator" 108 109 // ExecRBACResourceName is the model's exec RBAC resource name. 110 ExecRBACResourceName = "model-exec" 111 ) 112 113 // Client implements ModelOperatorBroker 114 func (m *modelOperatorBrokerBridge) Client() kubernetes.Interface { 115 return m.client 116 } 117 118 // EnsureConfigMap implements ModelOperatorBroker 119 func (m *modelOperatorBrokerBridge) EnsureConfigMap(c *core.ConfigMap) ([]func(), error) { 120 if m.ensureConfigMap == nil { 121 return []func(){}, errors.NotImplementedf("ensure config map bridge") 122 } 123 return m.ensureConfigMap(c) 124 } 125 126 // EnsureDeployment implements ModelOperatorBroker 127 func (m *modelOperatorBrokerBridge) EnsureDeployment(d *apps.Deployment) ([]func(), error) { 128 if m.ensureDeployment == nil { 129 return []func(){}, errors.NotImplementedf("ensure deployment bridge") 130 } 131 return m.ensureDeployment(d) 132 } 133 134 // EnsureRole implements ModelOperatorBroker 135 func (m *modelOperatorBrokerBridge) EnsureRole(r *rbac.Role) ([]func(), error) { 136 if m.ensureRole == nil { 137 return []func(){}, errors.NotImplementedf("ensure role bridge") 138 } 139 return m.ensureRole(r) 140 } 141 142 // EnsureRoleBinding implements ModelOperatorBroker 143 func (m *modelOperatorBrokerBridge) EnsureRoleBinding(r *rbac.RoleBinding) ([]func(), error) { 144 if m.ensureRoleBinding == nil { 145 return []func(){}, errors.NotImplementedf("ensure role binding bridge") 146 } 147 return m.ensureRoleBinding(r) 148 } 149 150 // EnsureService implements ModelOperatorBroker 151 func (m *modelOperatorBrokerBridge) EnsureService(s *core.Service) ([]func(), error) { 152 if m.ensureService == nil { 153 return []func(){}, errors.NotImplementedf("ensure service bridge") 154 } 155 return m.ensureService(s) 156 } 157 158 // EnsureServiceAccount implements ModelOperatorBroker 159 func (m *modelOperatorBrokerBridge) EnsureServiceAccount(s *core.ServiceAccount) ([]func(), error) { 160 if m.ensureServiceAccount == nil { 161 return []func(){}, errors.NotImplementedf("ensure service account bridge") 162 } 163 return m.ensureServiceAccount(s) 164 } 165 166 // Model implements ModelOperatorBroker 167 func (m *modelOperatorBrokerBridge) Model() string { 168 if m.model == nil { 169 return "" 170 } 171 return m.model() 172 } 173 174 // Namespace implements ModelOperatorBroker 175 func (m *modelOperatorBrokerBridge) Namespace() string { 176 if m.namespace == nil { 177 return "" 178 } 179 return m.namespace() 180 } 181 182 func (m *modelOperatorBrokerBridge) IsLegacyLabels() bool { 183 if m.isLegacyLabels == nil { 184 return true 185 } 186 return m.isLegacyLabels() 187 } 188 189 func ensureModelOperator( 190 modelUUID, 191 agentPath string, 192 clock jujuclock.Clock, 193 config *caas.ModelOperatorConfig, 194 broker ModelOperatorBroker, 195 ) (err error) { 196 197 ctx := context.TODO() 198 operatorName := modelOperatorName 199 modelTag := names.NewModelTag(modelUUID) 200 201 selectorLabels := modelOperatorLabels(operatorName, broker.IsLegacyLabels()) 202 labels := selectorLabels 203 if !broker.IsLegacyLabels() { 204 labels = utils.LabelsMerge(labels, utils.LabelsJuju) 205 } 206 207 cleanUpFuncs := []func(){} 208 defer func() { 209 if err != nil { 210 utils.RunCleanUps(cleanUpFuncs) 211 } 212 }() 213 214 configMap := modelOperatorConfigMap( 215 broker.Namespace(), 216 operatorName, 217 labels, 218 config.AgentConf) 219 220 c, err := broker.EnsureConfigMap(configMap) 221 cleanUpFuncs = append(cleanUpFuncs, c...) 222 if err != nil { 223 return errors.Annotate(err, "ensuring model operator config map") 224 } 225 226 volumes := []core.Volume{{ 227 Name: configMap.Name, 228 VolumeSource: core.VolumeSource{ 229 ConfigMap: &core.ConfigMapVolumeSource{ 230 LocalObjectReference: core.LocalObjectReference{ 231 Name: configMap.Name, 232 }, 233 Items: []core.KeyToPath{ 234 { 235 Key: modelOperatorConfigMapAgentConfKey(modelOperatorName), 236 Path: constants.TemplateFileNameAgentConf, 237 }, 238 }, 239 }, 240 }, 241 }} 242 243 volumeMounts := []core.VolumeMount{ 244 { 245 Name: configMap.Name, 246 MountPath: filepath.Join(agent.Dir(agentPath, modelTag), constants.TemplateFileNameAgentConf), 247 SubPath: constants.TemplateFileNameAgentConf, 248 }, 249 } 250 251 saName, c, err := ensureModelOperatorRBAC( 252 ctx, 253 broker, 254 clock, 255 operatorName, 256 labels, 257 ) 258 cleanUpFuncs = append(cleanUpFuncs, c...) 259 if err != nil { 260 return errors.Trace(err) 261 } 262 263 service := modelOperatorService( 264 operatorName, broker.Namespace(), labels, selectorLabels, config.Port) 265 c, err = broker.EnsureService(service) 266 cleanUpFuncs = append(cleanUpFuncs, c...) 267 if err != nil { 268 return errors.Annotate(err, "ensuring model operater service") 269 } 270 271 deployment, err := modelOperatorDeployment( 272 operatorName, 273 broker.Namespace(), 274 labels, 275 selectorLabels, 276 config.ImageDetails, 277 config.Port, 278 modelUUID, 279 service.Name, 280 saName, 281 volumes, 282 volumeMounts) 283 if err != nil { 284 return errors.Annotate(err, "building juju model operator deployment") 285 } 286 287 c, err = broker.EnsureDeployment(deployment) 288 cleanUpFuncs = append(cleanUpFuncs, c...) 289 if err != nil { 290 return errors.Annotate(err, "ensuring juju model operator deployment") 291 } 292 293 return nil 294 } 295 296 // EnsureModelOperator implements caas broker's interface. Function ensures that 297 // a model operator for this broker's namespace exists within Kubernetes. 298 func (k *kubernetesClient) EnsureModelOperator( 299 modelUUID, 300 agentPath string, 301 config *caas.ModelOperatorConfig, 302 ) error { 303 if k.client() == nil { 304 return errors.New("kubernetes client cannot be nil") 305 } 306 307 bridge := &modelOperatorBrokerBridge{ 308 client: k.client(), 309 ensureConfigMap: func(c *core.ConfigMap) ([]func(), error) { 310 cleanUp, err := k.ensureConfigMap(c) 311 return []func(){cleanUp}, err 312 }, 313 ensureDeployment: func(d *apps.Deployment) ([]func(), error) { 314 return []func(){}, k.ensureDeployment(d) 315 }, 316 ensureRole: func(r *rbac.Role) ([]func(), error) { 317 _, c, err := k.ensureRole(r) 318 return c, err 319 }, 320 ensureRoleBinding: func(rb *rbac.RoleBinding) ([]func(), error) { 321 _, c, err := k.ensureRoleBinding(rb) 322 return c, err 323 }, 324 ensureService: func(svc *core.Service) ([]func(), error) { 325 c, err := k.ensureK8sService(svc) 326 return []func(){c}, err 327 }, 328 ensureServiceAccount: func(sa *core.ServiceAccount) ([]func(), error) { 329 _, c, err := k.ensureServiceAccount(sa) 330 return c, err 331 }, 332 namespace: func() string { return k.namespace }, 333 model: func() string { return k.CurrentModel() }, 334 isLegacyLabels: k.IsLegacyLabels, 335 } 336 337 return ensureModelOperator(modelUUID, agentPath, k.clock, config, bridge) 338 } 339 340 // ModelOperator return the model operator config used to create the current 341 // model operator for this broker 342 func (k *kubernetesClient) ModelOperator() (*caas.ModelOperatorConfig, error) { 343 if k.namespace == "" { 344 return nil, errNoNamespace 345 } 346 operatorName := modelOperatorName 347 exists, err := k.ModelOperatorExists() 348 if err != nil { 349 return nil, errors.Trace(err) 350 } 351 if !exists { 352 return nil, errors.NotFoundf("model operator %s", operatorName) 353 } 354 355 modelOperatorCfg := caas.ModelOperatorConfig{} 356 cm, err := k.client().CoreV1().ConfigMaps(k.namespace). 357 Get(context.TODO(), operatorName, meta.GetOptions{}) 358 if err != nil && !k8serrors.IsNotFound(err) { 359 return nil, errors.Trace(err) 360 } 361 if cm != nil { 362 if agentConf, ok := cm.Data[modelOperatorConfigMapAgentConfKey(operatorName)]; ok { 363 modelOperatorCfg.AgentConf = []byte(agentConf) 364 } 365 } 366 367 return &modelOperatorCfg, nil 368 } 369 370 func modelOperatorConfigMap( 371 namespace, 372 operatorName string, 373 labels map[string]string, 374 agentConf []byte, 375 ) *core.ConfigMap { 376 377 return &core.ConfigMap{ 378 ObjectMeta: meta.ObjectMeta{ 379 Name: operatorName, 380 Namespace: namespace, 381 Labels: labels, 382 }, 383 Data: map[string]string{ 384 modelOperatorConfigMapAgentConfKey(operatorName): string(agentConf), 385 }, 386 } 387 } 388 389 func modelOperatorDeployment( 390 operatorName, 391 namespace string, 392 labels, 393 selectorLabels map[string]string, 394 operatorImageDetails coreresources.DockerImageDetails, 395 port int32, 396 modelUUID, 397 serviceName, 398 serviceAccountName string, 399 volumes []core.Volume, 400 volumeMounts []core.VolumeMount, 401 ) (o *apps.Deployment, err error) { 402 jujudCmd := fmt.Sprintf("exec $JUJU_TOOLS_DIR/jujud model --model-uuid=%s", modelUUID) 403 jujuDataDir := paths.DataDir(paths.OSUnixLike) 404 405 o = &apps.Deployment{ 406 ObjectMeta: meta.ObjectMeta{ 407 Name: operatorName, 408 Namespace: namespace, 409 Labels: utils.LabelsMerge( 410 labels, 411 utils.LabelsJujuModelOperatorDisableWebhook, 412 ), 413 }, 414 Spec: apps.DeploymentSpec{ 415 Replicas: pointer.Int32Ptr(1), 416 Selector: &meta.LabelSelector{ 417 MatchLabels: selectorLabels, 418 }, 419 Template: core.PodTemplateSpec{ 420 ObjectMeta: meta.ObjectMeta{ 421 Labels: utils.LabelsMerge( 422 selectorLabels, 423 utils.LabelsJujuModelOperatorDisableWebhook, 424 ), 425 }, 426 Spec: core.PodSpec{ 427 Containers: []core.Container{{ 428 Image: operatorImageDetails.RegistryPath, 429 ImagePullPolicy: core.PullIfNotPresent, 430 Name: operatorContainerName, 431 WorkingDir: jujuDataDir, 432 Command: []string{ 433 "/bin/sh", 434 }, 435 Args: []string{ 436 "-c", 437 fmt.Sprintf( 438 caas.JujudStartUpSh, 439 jujuDataDir, 440 "tools", 441 jujudCmd, 442 ), 443 }, 444 Env: []core.EnvVar{ 445 { 446 Name: EnvModelAgentHTTPPort, 447 Value: strconv.Itoa(int(port)), 448 }, 449 { 450 Name: EnvModelAgentCAASServiceName, 451 Value: serviceName, 452 }, 453 { 454 Name: EnvModelAgentCAASServiceNamespace, 455 Value: namespace, 456 }, 457 }, 458 Ports: []core.ContainerPort{ 459 { 460 ContainerPort: port, 461 Name: modelOperatorPortLabel, 462 Protocol: core.ProtocolTCP, 463 }, 464 }, 465 VolumeMounts: volumeMounts, 466 }}, 467 ServiceAccountName: serviceAccountName, 468 AutomountServiceAccountToken: boolPtr(true), 469 Volumes: volumes, 470 }, 471 }, 472 }, 473 } 474 if operatorImageDetails.IsPrivate() { 475 o.Spec.Template.Spec.ImagePullSecrets = []core.LocalObjectReference{ 476 {Name: constants.CAASImageRepoSecretName}, 477 } 478 } 479 return o, nil 480 } 481 482 // ModelOperatorExists indicates if the model operator for the given broker 483 // exists 484 func (k *kubernetesClient) ModelOperatorExists() (bool, error) { 485 operatorName := modelOperatorName 486 exists, err := k.modelOperatorDeploymentExists(operatorName) 487 if err != nil { 488 return false, errors.Trace(err) 489 } 490 return exists, nil 491 } 492 493 func (k *kubernetesClient) modelOperatorDeploymentExists(operatorName string) (bool, error) { 494 if k.namespace == "" { 495 return false, errNoNamespace 496 } 497 _, err := k.client().AppsV1().Deployments(k.namespace). 498 Get(context.TODO(), operatorName, meta.GetOptions{}) 499 500 if k8serrors.IsNotFound(err) { 501 return false, nil 502 } 503 if err != nil { 504 return false, errors.Trace(err) 505 } 506 return true, nil 507 } 508 509 func modelOperatorLabels(operatorName string, legacy bool) labels.Set { 510 if legacy { 511 return utils.LabelForKeyValue(constants.LegacyLabelModelOperator, operatorName) 512 } 513 return utils.LabelsForOperator(operatorName, OperatorModelTarget, legacy) 514 } 515 516 func modelOperatorService( 517 operatorName, 518 namespace string, 519 labels, 520 selectorLabels map[string]string, 521 port int32, 522 ) *core.Service { 523 return &core.Service{ 524 ObjectMeta: meta.ObjectMeta{ 525 Name: operatorName, 526 Namespace: namespace, 527 Labels: labels, 528 }, 529 Spec: core.ServiceSpec{ 530 Selector: selectorLabels, 531 Type: core.ServiceTypeClusterIP, 532 Ports: []core.ServicePort{ 533 { 534 Protocol: core.ProtocolTCP, 535 Port: port, 536 TargetPort: intstr.FromString(modelOperatorPortLabel), 537 }, 538 }, 539 }, 540 } 541 } 542 543 func modelOperatorGlobalScopedName(model, operatorName string) string { 544 if model == "" { 545 return operatorName 546 } 547 return fmt.Sprintf("%s-%s", model, operatorName) 548 } 549 550 func ensureModelOperatorRBAC( 551 ctx context.Context, 552 broker ModelOperatorBroker, 553 clock jujuclock.Clock, 554 operatorName string, 555 labels map[string]string, 556 ) (string, []func(), error) { 557 cleanUpFuncs := []func(){} 558 559 objMetaGlobal := meta.ObjectMeta{ 560 Name: modelOperatorGlobalScopedName(broker.Model(), operatorName), 561 Labels: labels, 562 } 563 objMetaNamespaced := meta.ObjectMeta{ 564 Name: operatorName, 565 Labels: labels, 566 Namespace: broker.Namespace(), 567 } 568 569 sa := &core.ServiceAccount{ 570 ObjectMeta: objMetaNamespaced, 571 AutomountServiceAccountToken: boolPtr(true), 572 } 573 574 c, err := broker.EnsureServiceAccount(sa) 575 cleanUpFuncs = append(cleanUpFuncs, c...) 576 if err != nil { 577 return sa.Name, cleanUpFuncs, errors.Annotate(err, "ensuring service account") 578 } 579 580 clusterRole := resources.NewClusterRole(objMetaGlobal.GetName(), &rbac.ClusterRole{ 581 ObjectMeta: objMetaGlobal, 582 Rules: []rbac.PolicyRule{ 583 { 584 APIGroups: []string{""}, 585 Resources: []string{"namespaces"}, 586 Verbs: []string{"get", "list"}, 587 }, 588 { 589 APIGroups: []string{"admissionregistration.k8s.io"}, 590 Resources: []string{"mutatingwebhookconfigurations"}, 591 Verbs: []string{ 592 "create", 593 "delete", 594 "get", 595 "list", 596 "update", 597 }, 598 }, 599 }, 600 }) 601 602 c, err = clusterRole.Ensure( 603 ctx, 604 broker.Client(), 605 resources.ClaimJujuOwnership, 606 ) 607 cleanUpFuncs = append(cleanUpFuncs, c...) 608 if err != nil { 609 return sa.Name, cleanUpFuncs, errors.Annotate(err, "ensuring cluster role") 610 } 611 612 clusterRoleBinding := resources.NewClusterRoleBinding(objMetaGlobal.GetName(), &rbac.ClusterRoleBinding{ 613 ObjectMeta: objMetaGlobal, 614 RoleRef: rbac.RoleRef{ 615 APIGroup: "rbac.authorization.k8s.io", 616 Kind: "ClusterRole", 617 Name: clusterRole.Name, 618 }, 619 Subjects: []rbac.Subject{ 620 { 621 Kind: "ServiceAccount", 622 Name: sa.Name, 623 Namespace: sa.Namespace, 624 }, 625 }, 626 }) 627 628 c, err = clusterRoleBinding.Ensure(ctx, broker.Client(), resources.ClaimJujuOwnership) 629 cleanUpFuncs = append(cleanUpFuncs, c...) 630 if err != nil { 631 return sa.Name, cleanUpFuncs, errors.Annotate(err, "ensuring cluster role binding") 632 } 633 634 role := &rbac.Role{ 635 ObjectMeta: objMetaNamespaced, 636 Rules: []rbac.PolicyRule{ 637 { 638 APIGroups: []string{""}, 639 Resources: []string{"serviceaccounts"}, 640 Verbs: []string{ 641 "get", 642 "list", 643 "watch", 644 }, 645 }, 646 }, 647 } 648 649 c, err = broker.EnsureRole(role) 650 cleanUpFuncs = append(cleanUpFuncs, c...) 651 if err != nil { 652 return sa.Name, cleanUpFuncs, errors.Annotate(err, "ensuring role") 653 } 654 655 roleBinding := &rbac.RoleBinding{ 656 ObjectMeta: objMetaNamespaced, 657 RoleRef: rbac.RoleRef{ 658 APIGroup: "rbac.authorization.k8s.io", 659 Kind: "Role", 660 Name: role.Name, 661 }, 662 Subjects: []rbac.Subject{ 663 { 664 Kind: "ServiceAccount", 665 Name: sa.Name, 666 Namespace: sa.Namespace, 667 }, 668 }, 669 } 670 671 c, err = broker.EnsureRoleBinding(roleBinding) 672 cleanUpFuncs = append(cleanUpFuncs, c...) 673 if err != nil { 674 return sa.Name, cleanUpFuncs, errors.Annotate(err, "ensuring role binding") 675 } 676 677 err = ensureExecRBACResources(objMetaNamespaced, clock, broker) 678 return sa.Name, cleanUpFuncs, errors.Trace(err) 679 } 680 681 func ensureExecRBACResources(objMeta meta.ObjectMeta, clock jujuclock.Clock, broker ModelOperatorBroker) error { 682 objMeta.SetName(ExecRBACResourceName) 683 684 sa := &core.ServiceAccount{ 685 ObjectMeta: objMeta, 686 AutomountServiceAccountToken: boolPtr(true), 687 } 688 _, err := broker.EnsureServiceAccount(sa) 689 if err != nil { 690 return errors.Annotatef(err, "ensuring service account %q", sa.GetName()) 691 } 692 693 role := &rbac.Role{ 694 ObjectMeta: objMeta, 695 Rules: []rbac.PolicyRule{ 696 { 697 APIGroups: []string{""}, 698 Resources: []string{"namespaces"}, 699 Verbs: []string{ 700 "get", 701 "list", 702 }, 703 ResourceNames: []string{ 704 objMeta.Namespace, 705 }, 706 }, 707 { 708 APIGroups: []string{""}, 709 Resources: []string{"pods"}, 710 Verbs: []string{ 711 "get", 712 "list", 713 }, 714 }, 715 { 716 APIGroups: []string{""}, 717 Resources: []string{"pods/exec"}, 718 Verbs: []string{ 719 "create", 720 }, 721 }, 722 }, 723 } 724 _, err = broker.EnsureRole(role) 725 if err != nil { 726 return errors.Annotatef(err, "ensuring role %q", role.GetName()) 727 } 728 729 roleBinding := &rbac.RoleBinding{ 730 ObjectMeta: objMeta, 731 RoleRef: rbac.RoleRef{ 732 APIGroup: "rbac.authorization.k8s.io", 733 Kind: "Role", 734 Name: role.Name, 735 }, 736 Subjects: []rbac.Subject{ 737 { 738 Kind: "ServiceAccount", 739 Name: sa.Name, 740 Namespace: sa.Namespace, 741 }, 742 }, 743 } 744 745 _, err = broker.EnsureRoleBinding(roleBinding) 746 if err != nil { 747 return errors.Annotatef(err, "ensuring role binding %q", roleBinding.Name) 748 } 749 750 _, err = proxy.EnsureSecretForServiceAccount( 751 sa.GetName(), objMeta, clock, 752 broker.Client().CoreV1().Secrets(objMeta.GetNamespace()), 753 broker.Client().CoreV1().ServiceAccounts(objMeta.GetNamespace()), 754 ) 755 return errors.Trace(err) 756 } 757 758 func modelOperatorConfigMapAgentConfKey(operatorName string) string { 759 return operatorName + "-agent.conf" 760 }