diff --git a/pkg/registry/rbac/reconciliation/BUILD b/pkg/registry/rbac/reconciliation/BUILD index b2c9a10d5d5..79a1484a3d1 100644 --- a/pkg/registry/rbac/reconciliation/BUILD +++ b/pkg/registry/rbac/reconciliation/BUILD @@ -11,8 +11,8 @@ load( go_test( name = "go_default_test", srcs = [ - "reconcile_clusterrole_test.go", "reconcile_clusterrolebindings_test.go", + "reconcile_role_test.go", ], library = ":go_default_library", tags = ["automanaged"], @@ -26,8 +26,9 @@ go_test( go_library( name = "go_default_library", srcs = [ - "reconcile_clusterrole.go", "reconcile_clusterrolebindings.go", + "reconcile_role.go", + "role_interfaces.go", ], tags = ["automanaged"], deps = [ diff --git a/pkg/registry/rbac/reconciliation/reconcile_clusterrole.go b/pkg/registry/rbac/reconciliation/reconcile_role.go similarity index 100% rename from pkg/registry/rbac/reconciliation/reconcile_clusterrole.go rename to pkg/registry/rbac/reconciliation/reconcile_role.go diff --git a/pkg/registry/rbac/reconciliation/reconcile_clusterrole_test.go b/pkg/registry/rbac/reconciliation/reconcile_role_test.go similarity index 100% rename from pkg/registry/rbac/reconciliation/reconcile_clusterrole_test.go rename to pkg/registry/rbac/reconciliation/reconcile_role_test.go diff --git a/pkg/registry/rbac/reconciliation/role_interfaces.go b/pkg/registry/rbac/reconciliation/role_interfaces.go new file mode 100644 index 00000000000..05887cc0c83 --- /dev/null +++ b/pkg/registry/rbac/reconciliation/role_interfaces.go @@ -0,0 +1,88 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package reconciliation + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion" +) + +type RoleRuleOwner struct { + Role *rbac.Role +} + +func (o RoleRuleOwner) GetNamespace() string { + return o.Role.Namespace +} + +func (o RoleRuleOwner) GetName() string { + return o.Role.Name +} + +func (o RoleRuleOwner) GetLabels() map[string]string { + return o.Role.Labels +} + +func (o RoleRuleOwner) SetLabels(in map[string]string) { + o.Role.Labels = in +} + +func (o RoleRuleOwner) GetAnnotations() map[string]string { + return o.Role.Annotations +} + +func (o RoleRuleOwner) SetAnnotations(in map[string]string) { + o.Role.Annotations = in +} + +func (o RoleRuleOwner) GetRules() []rbac.PolicyRule { + return o.Role.Rules +} + +func (o RoleRuleOwner) SetRules(in []rbac.PolicyRule) { + o.Role.Rules = in +} + +type RoleModifier struct { + Client internalversion.RolesGetter +} + +func (c RoleModifier) Get(namespace, name string) (RuleOwner, error) { + ret, err := c.Client.Roles(namespace).Get(name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + return RoleRuleOwner{Role: ret}, err +} + +func (c RoleModifier) Create(in RuleOwner) (RuleOwner, error) { + ret, err := c.Client.Roles(in.GetNamespace()).Create(in.(RoleRuleOwner).Role) + if err != nil { + return nil, err + } + return RoleRuleOwner{Role: ret}, err +} + +func (c RoleModifier) Update(in RuleOwner) (RuleOwner, error) { + ret, err := c.Client.Roles(in.GetNamespace()).Create(in.(RoleRuleOwner).Role) + if err != nil { + return nil, err + } + return RoleRuleOwner{Role: ret}, err + +} diff --git a/pkg/registry/rbac/rest/storage_rbac.go b/pkg/registry/rbac/rest/storage_rbac.go index f4a3d9c06eb..867011296ea 100644 --- a/pkg/registry/rbac/rest/storage_rbac.go +++ b/pkg/registry/rbac/rest/storage_rbac.go @@ -165,7 +165,6 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { // ensure bootstrap rolebindings are created or reconciled for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) { - opts := reconciliation.ReconcileClusterRoleBindingOptions{ RoleBinding: &clusterRoleBinding, Client: clientset.ClusterRoleBindings(), @@ -194,6 +193,36 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error { } } + // ensure bootstrap namespaced roles are created or reconciled + for namespace, roles := range bootstrappolicy.NamespaceRoles() { + for _, role := range roles { + opts := reconciliation.ReconcileClusterRoleOptions{ + Role: reconciliation.RoleRuleOwner{Role: &role}, + Client: reconciliation.RoleModifier{Client: clientset}, + Confirm: true, + } + err := retry.RetryOnConflict(retry.DefaultBackoff, func() error { + result, err := opts.Run() + if err != nil { + return err + } + switch { + case result.Protected && result.Operation != reconciliation.ReconcileNone: + glog.Warningf("skipped reconcile-protected role.%s/%s in %v with missing permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules) + case result.Operation == reconciliation.ReconcileUpdate: + glog.Infof("updated role.%s/%s in %v with additional permissions: %v", rbac.GroupName, role.Name, namespace, result.MissingRules) + case result.Operation == reconciliation.ReconcileCreate: + glog.Infof("created role.%s/%s in %v ", rbac.GroupName, role.Name, namespace) + } + return nil + }) + if err != nil { + // don't fail on failures, try to create as many as you can + utilruntime.HandleError(fmt.Errorf("unable to reconcile role.%s/%s in %v: %v", rbac.GroupName, role.Name, namespace, err)) + } + } + } + return true, nil }) // if we're never able to make it through intialization, kill the API server