Merge pull request #50702 from enj/enj/r/inject_policy_hook

Automatic merge from submit-queue (batch tested with PRs 50694, 50702)

Allow injection of policy in RBAC post start hook

This change allows the RBAC PostStartHook logic to be reused with different policy data when bootstrapping the cluster.  Thus any changes to the bootstrap logic are separated from the policy data.

Signed-off-by: Monis Khan <mkhan@redhat.com>

```release-note
NONE
```

@kubernetes/sig-auth-pr-reviews
pull/6/head
Kubernetes Submit Queue 2017-08-15 14:28:28 -07:00 committed by GitHub
commit f6929fc089
1 changed files with 119 additions and 104 deletions

View File

@ -134,99 +134,54 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
}
func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
return PostStartHookName, PostStartHook, nil
policy := &PolicyData{
ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
Roles: bootstrappolicy.NamespaceRoles(),
RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
}
return PostStartHookName, policy.EnsureRBACPolicy(), nil
}
func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
// intializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server
// starts, the roles don't initialize, and nothing works.
err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
type PolicyData struct {
ClusterRoles []rbac.ClusterRole
ClusterRoleBindings []rbac.ClusterRoleBinding
Roles map[string][]rbac.Role
RoleBindings map[string][]rbac.RoleBinding
}
coreclientset, err := coreclient.NewForConfig(hookContext.LoopbackClientConfig)
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
return false, nil
}
func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
return func(hookContext genericapiserver.PostStartHookContext) error {
// intializing roles is really important. On some e2e runs, we've seen cases where etcd is down when the server
// starts, the roles don't initialize, and nothing works.
err := wait.Poll(1*time.Second, 30*time.Second, func() (done bool, err error) {
clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig)
if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
return false, nil
}
// Make sure etcd is responding before we start reconciling
if _, err := clientset.ClusterRoles().List(metav1.ListOptions{}); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
return false, nil
}
if _, err := clientset.ClusterRoleBindings().List(metav1.ListOptions{}); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
return false, nil
}
// ensure bootstrap roles are created or reconciled
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
opts := reconciliation.ReconcileRoleOptions{
Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: &clusterRole},
Client: reconciliation.ClusterRoleModifier{Client: clientset.ClusterRoles()},
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 clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
}
return nil
})
coreclientset, err := coreclient.NewForConfig(hookContext.LoopbackClientConfig)
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrole.%s/%s: %v", rbac.GroupName, clusterRole.Name, err))
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
return false, nil
}
}
// ensure bootstrap rolebindings are created or reconciled
for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) {
opts := reconciliation.ReconcileRoleBindingOptions{
RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: &clusterRoleBinding},
Client: reconciliation.ClusterRoleBindingClientAdapter{Client: clientset.ClusterRoleBindings()},
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 clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
case result.Operation == reconciliation.ReconcileRecreate:
glog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
}
return nil
})
clientset, err := rbacclient.NewForConfig(hookContext.LoopbackClientConfig)
if err != nil {
// don't fail on failures, try to create as many as you can
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrolebinding.%s/%s: %v", rbac.GroupName, clusterRoleBinding.Name, err))
utilruntime.HandleError(fmt.Errorf("unable to initialize client: %v", err))
return false, nil
}
// Make sure etcd is responding before we start reconciling
if _, err := clientset.ClusterRoles().List(metav1.ListOptions{}); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
return false, nil
}
if _, err := clientset.ClusterRoleBindings().List(metav1.ListOptions{}); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
return false, nil
}
}
// ensure bootstrap namespaced roles are created or reconciled
for namespace, roles := range bootstrappolicy.NamespaceRoles() {
for _, role := range roles {
// ensure bootstrap roles are created or reconciled
for _, clusterRole := range p.ClusterRoles {
opts := reconciliation.ReconcileRoleOptions{
Role: reconciliation.RoleRuleOwner{Role: &role},
Client: reconciliation.RoleModifier{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
Role: reconciliation.ClusterRoleRuleOwner{ClusterRole: &clusterRole},
Client: reconciliation.ClusterRoleModifier{Client: clientset.ClusterRoles()},
Confirm: true,
}
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
@ -236,27 +191,25 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
}
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)
glog.Warningf("skipped reconcile-protected clusterrole.%s/%s with missing permissions: %v", rbac.GroupName, clusterRole.Name, 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)
glog.Infof("updated clusterrole.%s/%s with additional permissions: %v", rbac.GroupName, clusterRole.Name, result.MissingRules)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created role.%s/%s in %v ", rbac.GroupName, role.Name, namespace)
glog.Infof("created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
}
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))
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrole.%s/%s: %v", rbac.GroupName, clusterRole.Name, err))
}
}
}
// ensure bootstrap namespaced rolebindings are created or reconciled
for namespace, roleBindings := range bootstrappolicy.NamespaceRoleBindings() {
for _, roleBinding := range roleBindings {
// ensure bootstrap rolebindings are created or reconciled
for _, clusterRoleBinding := range p.ClusterRoleBindings {
opts := reconciliation.ReconcileRoleBindingOptions{
RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding},
Client: reconciliation.RoleBindingClientAdapter{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
RoleBinding: reconciliation.ClusterRoleBindingAdapter{ClusterRoleBinding: &clusterRoleBinding},
Client: reconciliation.ClusterRoleBindingClientAdapter{Client: clientset.ClusterRoleBindings()},
Confirm: true,
}
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
@ -266,31 +219,93 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
}
switch {
case result.Protected && result.Operation != reconciliation.ReconcileNone:
glog.Warningf("skipped reconcile-protected rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
glog.Warningf("skipped reconcile-protected clusterrolebinding.%s/%s with missing subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
glog.Infof("updated clusterrolebinding.%s/%s with additional subjects: %v", rbac.GroupName, clusterRoleBinding.Name, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
glog.Infof("created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
case result.Operation == reconciliation.ReconcileRecreate:
glog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
glog.Infof("recreated clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
}
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 rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err))
utilruntime.HandleError(fmt.Errorf("unable to reconcile clusterrolebinding.%s/%s: %v", rbac.GroupName, clusterRoleBinding.Name, err))
}
}
// ensure bootstrap namespaced roles are created or reconciled
for namespace, roles := range p.Roles {
for _, role := range roles {
opts := reconciliation.ReconcileRoleOptions{
Role: reconciliation.RoleRuleOwner{Role: &role},
Client: reconciliation.RoleModifier{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
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))
}
}
}
// ensure bootstrap namespaced rolebindings are created or reconciled
for namespace, roleBindings := range p.RoleBindings {
for _, roleBinding := range roleBindings {
opts := reconciliation.ReconcileRoleBindingOptions{
RoleBinding: reconciliation.RoleBindingAdapter{RoleBinding: &roleBinding},
Client: reconciliation.RoleBindingClientAdapter{Client: clientset, NamespaceClient: coreclientset.Namespaces()},
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 rolebinding.%s/%s in %v with missing subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileUpdate:
glog.Infof("updated rolebinding.%s/%s in %v with additional subjects: %v", rbac.GroupName, roleBinding.Name, namespace, result.MissingSubjects)
case result.Operation == reconciliation.ReconcileCreate:
glog.Infof("created rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.Name, namespace)
case result.Operation == reconciliation.ReconcileRecreate:
glog.Infof("recreated rolebinding.%s/%s in %v", rbac.GroupName, roleBinding.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 rolebinding.%s/%s in %v: %v", rbac.GroupName, roleBinding.Name, namespace, err))
}
}
}
return true, nil
})
// if we're never able to make it through intialization, kill the API server
if err != nil {
return fmt.Errorf("unable to initialize roles: %v", err)
}
return true, nil
})
// if we're never able to make it through intialization, kill the API server
if err != nil {
return fmt.Errorf("unable to initialize roles: %v", err)
return nil
}
return nil
}
func (p RESTStorageProvider) GroupName() string {