mirror of https://github.com/k3s-io/k3s
Reconcile bootstrap clusterroles on server start
parent
436fa5c9d1
commit
26b42d350d
|
@ -34,6 +34,9 @@ const (
|
|||
GroupKind = "Group"
|
||||
ServiceAccountKind = "ServiceAccount"
|
||||
UserKind = "User"
|
||||
|
||||
// AutoUpdateAnnotationKey is the name of an annotation which prevents reconciliation if set to "false"
|
||||
AutoUpdateAnnotationKey = "rbac.authorization.kubernetes.io/autoupdate"
|
||||
)
|
||||
|
||||
// PolicyRule holds information that describes a policy rule, but does not contain information
|
||||
|
|
|
@ -34,6 +34,9 @@ const (
|
|||
GroupKind = "Group"
|
||||
ServiceAccountKind = "ServiceAccount"
|
||||
UserKind = "User"
|
||||
|
||||
// AutoUpdateAnnotationKey is the name of an annotation which prevents reconciliation if set to "false"
|
||||
AutoUpdateAnnotationKey = "rbac.authorization.kubernetes.io/autoupdate"
|
||||
)
|
||||
|
||||
// Authorization is calculated against
|
||||
|
|
|
@ -34,6 +34,9 @@ const (
|
|||
GroupKind = "Group"
|
||||
ServiceAccountKind = "ServiceAccount"
|
||||
UserKind = "User"
|
||||
|
||||
// AutoUpdateAnnotationKey is the name of an annotation which prevents reconciliation if set to "false"
|
||||
AutoUpdateAnnotationKey = "rbac.authorization.kubernetes.io/autoupdate"
|
||||
)
|
||||
|
||||
// Authorization is calculated against
|
||||
|
|
|
@ -33,6 +33,7 @@ filegroup(
|
|||
":package-srcs",
|
||||
"//pkg/registry/rbac/clusterrole:all-srcs",
|
||||
"//pkg/registry/rbac/clusterrolebinding:all-srcs",
|
||||
"//pkg/registry/rbac/reconciliation:all-srcs",
|
||||
"//pkg/registry/rbac/rest:all-srcs",
|
||||
"//pkg/registry/rbac/role:all-srcs",
|
||||
"//pkg/registry/rbac/rolebinding:all-srcs",
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"reconcile_clusterrole_test.go",
|
||||
"reconcile_clusterrolebindings_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"reconcile_clusterrole.go",
|
||||
"reconcile_clusterrolebindings.go",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/rbac:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//vendor:k8s.io/apimachinery/pkg/api/errors",
|
||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
|
@ -0,0 +1,200 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/validation"
|
||||
)
|
||||
|
||||
type ReconcileOperation string
|
||||
|
||||
var (
|
||||
ReconcileCreate ReconcileOperation = "create"
|
||||
ReconcileUpdate ReconcileOperation = "update"
|
||||
ReconcileRecreate ReconcileOperation = "recreate"
|
||||
ReconcileNone ReconcileOperation = "none"
|
||||
)
|
||||
|
||||
type ReconcileClusterRoleOptions struct {
|
||||
// Role is the expected role that will be reconciled
|
||||
Role *rbac.ClusterRole
|
||||
// Confirm indicates writes should be performed. When false, results are returned as a dry-run.
|
||||
Confirm bool
|
||||
// RemoveExtraPermissions indicates reconciliation should remove extra permissions from an existing role
|
||||
RemoveExtraPermissions bool
|
||||
// Client is used to look up existing roles, and create/update the role when Confirm=true
|
||||
Client internalversion.ClusterRoleInterface
|
||||
}
|
||||
|
||||
type ReconcileClusterRoleResult struct {
|
||||
// Role is the reconciled role from the reconciliation operation.
|
||||
// If the reconcile was performed as a dry-run, or the existing role was protected, the reconciled role is not persisted.
|
||||
Role *rbac.ClusterRole
|
||||
|
||||
// MissingRules contains expected rules that were missing from the currently persisted role
|
||||
MissingRules []rbac.PolicyRule
|
||||
// ExtraRules contains extra permissions the currently persisted role had
|
||||
ExtraRules []rbac.PolicyRule
|
||||
|
||||
// Operation is the API operation required to reconcile.
|
||||
// If no reconciliation was needed, it is set to ReconcileNone.
|
||||
// If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
|
||||
// If result.Protected == true, the role opted out of reconciliation, so the operation was not performed.
|
||||
// Otherwise, the operation was performed.
|
||||
Operation ReconcileOperation
|
||||
// Protected indicates an existing role prevented reconciliation
|
||||
Protected bool
|
||||
}
|
||||
|
||||
func (o *ReconcileClusterRoleOptions) Run() (*ReconcileClusterRoleResult, error) {
|
||||
return o.run(0)
|
||||
}
|
||||
|
||||
func (o *ReconcileClusterRoleOptions) run(attempts int) (*ReconcileClusterRoleResult, error) {
|
||||
// This keeps us from retrying forever if a role keeps appearing and disappearing as we reconcile.
|
||||
// Conflict errors on update are handled at a higher level.
|
||||
if attempts > 2 {
|
||||
return nil, fmt.Errorf("exceeded maximum attempts")
|
||||
}
|
||||
|
||||
var result *ReconcileClusterRoleResult
|
||||
|
||||
existing, err := o.Client.Get(o.Role.Name, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
result = &ReconcileClusterRoleResult{
|
||||
Role: o.Role,
|
||||
MissingRules: o.Role.Rules,
|
||||
Operation: ReconcileCreate,
|
||||
}
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
||||
default:
|
||||
result, err = computeReconciledRole(existing, o.Role, o.RemoveExtraPermissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If reconcile-protected, short-circuit
|
||||
if result.Protected {
|
||||
return result, nil
|
||||
}
|
||||
// If we're in dry-run mode, short-circuit
|
||||
if !o.Confirm {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case ReconcileCreate:
|
||||
created, err := o.Client.Create(result.Role)
|
||||
// If created since we started this reconcile, re-run
|
||||
if errors.IsAlreadyExists(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Role = created
|
||||
|
||||
case ReconcileUpdate:
|
||||
updated, err := o.Client.Update(result.Role)
|
||||
// If deleted since we started this reconcile, re-run
|
||||
if errors.IsNotFound(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Role = updated
|
||||
|
||||
case ReconcileNone:
|
||||
// no-op
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid operation: %v", result.Operation)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// computeReconciledRole returns the role that must be created and/or updated to make the
|
||||
// existing role's permissions match the expected role's permissions
|
||||
func computeReconciledRole(existing, expected *rbac.ClusterRole, removeExtraPermissions bool) (*ReconcileClusterRoleResult, error) {
|
||||
result := &ReconcileClusterRoleResult{Operation: ReconcileNone}
|
||||
|
||||
result.Protected = (existing.Annotations[rbac.AutoUpdateAnnotationKey] == "false")
|
||||
|
||||
// Start with a copy of the existing object
|
||||
changedObj, err := api.Scheme.DeepCopy(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.Role = changedObj.(*rbac.ClusterRole)
|
||||
|
||||
// Merge expected annotations and labels
|
||||
result.Role.Annotations = merge(expected.Annotations, result.Role.Annotations)
|
||||
if !reflect.DeepEqual(result.Role.Annotations, existing.Annotations) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
result.Role.Labels = merge(expected.Labels, result.Role.Labels)
|
||||
if !reflect.DeepEqual(result.Role.Labels, existing.Labels) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
// Compute extra and missing rules
|
||||
_, result.ExtraRules = validation.Covers(expected.Rules, existing.Rules)
|
||||
_, result.MissingRules = validation.Covers(existing.Rules, expected.Rules)
|
||||
|
||||
switch {
|
||||
case !removeExtraPermissions && len(result.MissingRules) > 0:
|
||||
// add missing rules in the union case
|
||||
result.Role.Rules = append(result.Role.Rules, result.MissingRules...)
|
||||
result.Operation = ReconcileUpdate
|
||||
|
||||
case removeExtraPermissions && (len(result.MissingRules) > 0 || len(result.ExtraRules) > 0):
|
||||
// stomp to expected rules in the non-union case
|
||||
result.Role.Rules = expected.Rules
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// merge combines the given maps with the later annotations having higher precedence
|
||||
func merge(maps ...map[string]string) map[string]string {
|
||||
var output map[string]string = nil
|
||||
for _, m := range maps {
|
||||
if m != nil && output == nil {
|
||||
output = map[string]string{}
|
||||
}
|
||||
for k, v := range m {
|
||||
output[k] = v
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func role(rules []rbac.PolicyRule, labels map[string]string, annotations map[string]string) *rbac.ClusterRole {
|
||||
return &rbac.ClusterRole{Rules: rules, ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: annotations}}
|
||||
}
|
||||
|
||||
func rules(resources ...string) []rbac.PolicyRule {
|
||||
r := []rbac.PolicyRule{}
|
||||
for _, resource := range resources {
|
||||
r = append(r, rbac.PolicyRule{APIGroups: []string{""}, Verbs: []string{"get"}, Resources: []string{resource}})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type ss map[string]string
|
||||
|
||||
func TestComputeReconciledRole(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
expectedRole *rbac.ClusterRole
|
||||
actualRole *rbac.ClusterRole
|
||||
removeExtraPermissions bool
|
||||
|
||||
expectedReconciledRole *rbac.ClusterRole
|
||||
expectedReconciliationNeeded bool
|
||||
}{
|
||||
"empty": {
|
||||
expectedRole: role(rules(), nil, nil),
|
||||
actualRole: role(rules(), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match without union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("a"), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match with union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("a"), nil, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different rules without union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("b"), nil, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different rules with union": {
|
||||
expectedRole: role(rules("a"), nil, nil),
|
||||
actualRole: role(rules("b"), nil, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"match labels without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match labels with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different labels without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("a"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different labels and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"2": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"1": "a", "2": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"1": "b"}, nil),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"1": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"1": "a"}, nil),
|
||||
actualRole: role(rules("b"), ss{"1": "b"}, nil),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"1": "b"}, nil),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"match annotations without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"match annotations with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: nil,
|
||||
expectedReconciliationNeeded: false,
|
||||
},
|
||||
"different annotations without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("a"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"different annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"2": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, ss{"1": "a", "2": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"1": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), nil, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), nil, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), nil, ss{"1": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), nil, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels/annotations and rules without union": {
|
||||
expectedRole: role(rules("a"), ss{"3": "d"}, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), ss{"4": "e"}, ss{"1": "b"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(rules("a"), ss{"3": "d", "4": "e"}, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"conflicting labels/annotations and rules with union": {
|
||||
expectedRole: role(rules("a"), ss{"3": "d"}, ss{"1": "a"}),
|
||||
actualRole: role(rules("b"), ss{"4": "e"}, ss{"1": "b"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(rules("b", "a"), ss{"3": "d", "4": "e"}, ss{"1": "b"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"complex labels/annotations and rules without union": {
|
||||
expectedRole: role(rules("pods", "nodes", "secrets"), ss{"env": "prod", "color": "blue"}, ss{"description": "fancy", "system": "true"}),
|
||||
actualRole: role(rules("nodes", "images", "projects"), ss{"color": "red", "team": "pm"}, ss{"system": "false", "owner": "admin", "vip": "yes"}),
|
||||
removeExtraPermissions: true,
|
||||
|
||||
expectedReconciledRole: role(
|
||||
rules("pods", "nodes", "secrets"),
|
||||
ss{"env": "prod", "color": "red", "team": "pm"},
|
||||
ss{"description": "fancy", "system": "false", "owner": "admin", "vip": "yes"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
"complex labels/annotations and rules with union": {
|
||||
expectedRole: role(rules("pods", "nodes", "secrets"), ss{"env": "prod", "color": "blue", "manager": "randy"}, ss{"description": "fancy", "system": "true", "up": "true"}),
|
||||
actualRole: role(rules("nodes", "images", "projects"), ss{"color": "red", "team": "pm"}, ss{"system": "false", "owner": "admin", "vip": "yes", "rate": "down"}),
|
||||
removeExtraPermissions: false,
|
||||
|
||||
expectedReconciledRole: role(
|
||||
rules("nodes", "images", "projects", "pods", "secrets"),
|
||||
ss{"env": "prod", "manager": "randy", "color": "red", "team": "pm"},
|
||||
ss{"description": "fancy", "system": "false", "owner": "admin", "vip": "yes", "rate": "down", "up": "true"}),
|
||||
expectedReconciliationNeeded: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
result, err := computeReconciledRole(tc.actualRole, tc.expectedRole, tc.removeExtraPermissions)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
reconciliationNeeded := result.Operation != ReconcileNone
|
||||
if reconciliationNeeded != tc.expectedReconciliationNeeded {
|
||||
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v", k, tc.expectedReconciliationNeeded, reconciliationNeeded)
|
||||
continue
|
||||
}
|
||||
if reconciliationNeeded && !api.Semantic.DeepEqual(result.Role, tc.expectedReconciledRole) {
|
||||
t.Errorf("%s: Expected\n\t%#v\ngot\n\t%#v", k, tc.expectedReconciledRole, result.Role)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,234 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
)
|
||||
|
||||
// ReconcileClusterRoleBindingOptions holds options for running a role binding reconciliation
|
||||
type ReconcileClusterRoleBindingOptions struct {
|
||||
// RoleBinding is the expected rolebinding that will be reconciled
|
||||
RoleBinding *rbac.ClusterRoleBinding
|
||||
// Confirm indicates writes should be performed. When false, results are returned as a dry-run.
|
||||
Confirm bool
|
||||
// RemoveExtraSubjects indicates reconciliation should remove extra subjects from an existing role binding
|
||||
RemoveExtraSubjects bool
|
||||
// Client is used to look up existing rolebindings, and create/update the rolebinding when Confirm=true
|
||||
Client internalversion.ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
// ReconcileClusterRoleBindingResult holds the result of a reconciliation operation.
|
||||
type ReconcileClusterRoleBindingResult struct {
|
||||
// RoleBinding is the reconciled rolebinding from the reconciliation operation.
|
||||
// If the reconcile was performed as a dry-run, or the existing rolebinding was protected, the reconciled rolebinding is not persisted.
|
||||
RoleBinding *rbac.ClusterRoleBinding
|
||||
|
||||
// MissingSubjects contains expected subjects that were missing from the currently persisted rolebinding
|
||||
MissingSubjects []rbac.Subject
|
||||
// ExtraSubjects contains extra subjects the currently persisted rolebinding had
|
||||
ExtraSubjects []rbac.Subject
|
||||
|
||||
// Operation is the API operation required to reconcile.
|
||||
// If no reconciliation was needed, it is set to ReconcileNone.
|
||||
// If options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
|
||||
// If result.Protected == true, the rolebinding opted out of reconciliation, so the operation was not performed.
|
||||
// Otherwise, the operation was performed.
|
||||
Operation ReconcileOperation
|
||||
// Protected indicates an existing role prevented reconciliation
|
||||
Protected bool
|
||||
}
|
||||
|
||||
func (o *ReconcileClusterRoleBindingOptions) Run() (*ReconcileClusterRoleBindingResult, error) {
|
||||
return o.run(0)
|
||||
}
|
||||
|
||||
func (o *ReconcileClusterRoleBindingOptions) run(attempts int) (*ReconcileClusterRoleBindingResult, error) {
|
||||
// This keeps us from retrying forever if a rolebinding keeps appearing and disappearing as we reconcile.
|
||||
// Conflict errors on update are handled at a higher level.
|
||||
if attempts > 3 {
|
||||
return nil, fmt.Errorf("exceeded maximum attempts")
|
||||
}
|
||||
|
||||
var result *ReconcileClusterRoleBindingResult
|
||||
|
||||
existingBinding, err := o.Client.Get(o.RoleBinding.Name, metav1.GetOptions{})
|
||||
switch {
|
||||
case errors.IsNotFound(err):
|
||||
result = &ReconcileClusterRoleBindingResult{
|
||||
RoleBinding: o.RoleBinding,
|
||||
MissingSubjects: o.RoleBinding.Subjects,
|
||||
Operation: ReconcileCreate,
|
||||
}
|
||||
|
||||
case err != nil:
|
||||
return nil, err
|
||||
|
||||
default:
|
||||
result, err = computeReconciledRoleBinding(existingBinding, o.RoleBinding, o.RemoveExtraSubjects)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If reconcile-protected, short-circuit
|
||||
if result.Protected {
|
||||
return result, nil
|
||||
}
|
||||
// If we're in dry-run mode, short-circuit
|
||||
if !o.Confirm {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
switch result.Operation {
|
||||
case ReconcileRecreate:
|
||||
// Try deleting
|
||||
err := o.Client.Delete(
|
||||
existingBinding.Name,
|
||||
&metav1.DeleteOptions{Preconditions: &metav1.Preconditions{UID: &existingBinding.UID}},
|
||||
)
|
||||
switch {
|
||||
case err == nil, errors.IsNotFound(err):
|
||||
// object no longer exists, as desired
|
||||
case errors.IsConflict(err):
|
||||
// delete failed because our UID precondition conflicted
|
||||
// this could mean another object exists with a different UID, re-run
|
||||
return o.run(attempts + 1)
|
||||
default:
|
||||
// return other errors
|
||||
return nil, err
|
||||
}
|
||||
// continue to create
|
||||
fallthrough
|
||||
case ReconcileCreate:
|
||||
created, err := o.Client.Create(result.RoleBinding)
|
||||
// If created since we started this reconcile, re-run
|
||||
if errors.IsAlreadyExists(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.RoleBinding = created
|
||||
|
||||
case ReconcileUpdate:
|
||||
updated, err := o.Client.Update(result.RoleBinding)
|
||||
// If deleted since we started this reconcile, re-run
|
||||
if errors.IsNotFound(err) {
|
||||
return o.run(attempts + 1)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.RoleBinding = updated
|
||||
|
||||
case ReconcileNone:
|
||||
// no-op
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid operation: %v", result.Operation)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// computeReconciledRoleBinding returns the rolebinding that must be created and/or updated to make the
|
||||
// existing rolebinding's subjects, roleref, labels, and annotations match the expected rolebinding
|
||||
func computeReconciledRoleBinding(existing, expected *rbac.ClusterRoleBinding, removeExtraSubjects bool) (*ReconcileClusterRoleBindingResult, error) {
|
||||
result := &ReconcileClusterRoleBindingResult{Operation: ReconcileNone}
|
||||
|
||||
result.Protected = (existing.Annotations[rbac.AutoUpdateAnnotationKey] == "false")
|
||||
|
||||
// Reset the binding completely if the roleRef is different
|
||||
if expected.RoleRef != existing.RoleRef {
|
||||
result.RoleBinding = expected
|
||||
result.Operation = ReconcileRecreate
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Start with a copy of the existing object
|
||||
changedObj, err := api.Scheme.DeepCopy(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result.RoleBinding = changedObj.(*rbac.ClusterRoleBinding)
|
||||
|
||||
// Merge expected annotations and labels
|
||||
result.RoleBinding.Annotations = merge(expected.Annotations, result.RoleBinding.Annotations)
|
||||
if !reflect.DeepEqual(result.RoleBinding.Annotations, existing.Annotations) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
result.RoleBinding.Labels = merge(expected.Labels, result.RoleBinding.Labels)
|
||||
if !reflect.DeepEqual(result.RoleBinding.Labels, existing.Labels) {
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
// Compute extra and missing subjects
|
||||
result.MissingSubjects, result.ExtraSubjects = diffSubjectLists(expected.Subjects, existing.Subjects)
|
||||
|
||||
switch {
|
||||
case !removeExtraSubjects && len(result.MissingSubjects) > 0:
|
||||
// add missing subjects in the union case
|
||||
result.RoleBinding.Subjects = append(result.RoleBinding.Subjects, result.MissingSubjects...)
|
||||
result.Operation = ReconcileUpdate
|
||||
|
||||
case removeExtraSubjects && (len(result.MissingSubjects) > 0 || len(result.ExtraSubjects) > 0):
|
||||
// stomp to expected subjects in the non-union case
|
||||
result.RoleBinding.Subjects = expected.Subjects
|
||||
result.Operation = ReconcileUpdate
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func contains(list []rbac.Subject, item rbac.Subject) bool {
|
||||
for _, listItem := range list {
|
||||
if listItem == item {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// diffSubjectLists returns lists containing the items unique to each provided list:
|
||||
// list1Only = list1 - list2
|
||||
// list2Only = list2 - list1
|
||||
// if both returned lists are empty, the provided lists are equal
|
||||
func diffSubjectLists(list1 []rbac.Subject, list2 []rbac.Subject) (list1Only []rbac.Subject, list2Only []rbac.Subject) {
|
||||
for _, list1Item := range list1 {
|
||||
if !contains(list2, list1Item) {
|
||||
if !contains(list1Only, list1Item) {
|
||||
list1Only = append(list1Only, list1Item)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, list2Item := range list2 {
|
||||
if !contains(list1, list2Item) {
|
||||
if !contains(list2Only, list2Item) {
|
||||
list2Only = append(list2Only, list2Item)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
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 (
|
||||
"testing"
|
||||
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
func binding(roleRef rbac.RoleRef, subjects []rbac.Subject) *rbac.ClusterRoleBinding {
|
||||
return &rbac.ClusterRoleBinding{RoleRef: roleRef, Subjects: subjects}
|
||||
}
|
||||
|
||||
func ref(name string) rbac.RoleRef {
|
||||
return rbac.RoleRef{Name: name}
|
||||
}
|
||||
|
||||
func subject(name string) rbac.Subject {
|
||||
return rbac.Subject{Name: name}
|
||||
}
|
||||
|
||||
func subjects(names ...string) []rbac.Subject {
|
||||
r := []rbac.Subject{}
|
||||
for _, name := range names {
|
||||
r = append(r, subject(name))
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func TestDiffObjectReferenceLists(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
A []rbac.Subject
|
||||
B []rbac.Subject
|
||||
ExpectedOnlyA []rbac.Subject
|
||||
ExpectedOnlyB []rbac.Subject
|
||||
}{
|
||||
"empty": {},
|
||||
|
||||
"matching, order-independent": {
|
||||
A: subjects("foo", "bar"),
|
||||
B: subjects("bar", "foo"),
|
||||
},
|
||||
|
||||
"partial match": {
|
||||
A: subjects("foo", "bar"),
|
||||
B: subjects("foo", "baz"),
|
||||
ExpectedOnlyA: subjects("bar"),
|
||||
ExpectedOnlyB: subjects("baz"),
|
||||
},
|
||||
|
||||
"missing": {
|
||||
A: subjects("foo"),
|
||||
B: subjects("bar"),
|
||||
ExpectedOnlyA: subjects("foo"),
|
||||
ExpectedOnlyB: subjects("bar"),
|
||||
},
|
||||
|
||||
"remove duplicates": {
|
||||
A: subjects("foo", "foo"),
|
||||
B: subjects("bar", "bar"),
|
||||
ExpectedOnlyA: subjects("foo"),
|
||||
ExpectedOnlyB: subjects("bar"),
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
onlyA, onlyB := diffSubjectLists(tc.A, tc.B)
|
||||
if !api.Semantic.DeepEqual(onlyA, tc.ExpectedOnlyA) {
|
||||
t.Errorf("%s: Expected %#v, got %#v", k, tc.ExpectedOnlyA, onlyA)
|
||||
}
|
||||
if !api.Semantic.DeepEqual(onlyB, tc.ExpectedOnlyB) {
|
||||
t.Errorf("%s: Expected %#v, got %#v", k, tc.ExpectedOnlyB, onlyB)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeUpdate(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
ExpectedBinding *rbac.ClusterRoleBinding
|
||||
ActualBinding *rbac.ClusterRoleBinding
|
||||
RemoveExtraSubjects bool
|
||||
|
||||
ExpectedUpdatedBinding *rbac.ClusterRoleBinding
|
||||
ExpectedUpdateNeeded bool
|
||||
}{
|
||||
"match without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
"match with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
|
||||
"different roleref with identical subjects": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("differentRole"), subjects("a")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
|
||||
"extra subjects without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
"extra subjects with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: nil,
|
||||
ExpectedUpdateNeeded: false,
|
||||
},
|
||||
|
||||
"missing subjects without union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: true,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
"missing subjects with union": {
|
||||
ExpectedBinding: binding(ref("role"), subjects("a", "c")),
|
||||
ActualBinding: binding(ref("role"), subjects("a", "b")),
|
||||
RemoveExtraSubjects: false,
|
||||
|
||||
ExpectedUpdatedBinding: binding(ref("role"), subjects("a", "b", "c")),
|
||||
ExpectedUpdateNeeded: true,
|
||||
},
|
||||
}
|
||||
|
||||
for k, tc := range tests {
|
||||
result, err := computeReconciledRoleBinding(tc.ActualBinding, tc.ExpectedBinding, tc.RemoveExtraSubjects)
|
||||
if err != nil {
|
||||
t.Errorf("%s: %v", k, err)
|
||||
continue
|
||||
}
|
||||
updateNeeded := result.Operation != ReconcileNone
|
||||
updatedBinding := result.RoleBinding
|
||||
if updateNeeded != tc.ExpectedUpdateNeeded {
|
||||
t.Errorf("%s: Expected\n\t%v\ngot\n\t%v (%v)", k, tc.ExpectedUpdateNeeded, updateNeeded, result.Operation)
|
||||
continue
|
||||
}
|
||||
if updateNeeded && !api.Semantic.DeepEqual(updatedBinding, tc.ExpectedUpdatedBinding) {
|
||||
t.Errorf("%s: Expected\n\t%v %v\ngot\n\t%v %v", k, tc.ExpectedUpdatedBinding.RoleRef, tc.ExpectedUpdatedBinding.Subjects, updatedBinding.RoleRef, updatedBinding.Subjects)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,12 +17,14 @@ go_library(
|
|||
"//pkg/apis/rbac/v1alpha1:go_default_library",
|
||||
"//pkg/apis/rbac/v1beta1:go_default_library",
|
||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
|
||||
"//pkg/client/retry:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrole/storage:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/clusterrolebinding/storage:go_default_library",
|
||||
"//pkg/registry/rbac/reconciliation:go_default_library",
|
||||
"//pkg/registry/rbac/role:go_default_library",
|
||||
"//pkg/registry/rbac/role/policybased:go_default_library",
|
||||
"//pkg/registry/rbac/role/storage:go_default_library",
|
||||
|
@ -32,7 +34,6 @@ go_library(
|
|||
"//pkg/registry/rbac/validation:go_default_library",
|
||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
||||
"//vendor:github.com/golang/glog",
|
||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
||||
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
|
||||
"github.com/golang/glog"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
@ -36,12 +35,14 @@ import (
|
|||
rbacapiv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
||||
rbacapiv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
|
||||
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
||||
"k8s.io/kubernetes/pkg/client/retry"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
||||
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
|
||||
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
||||
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
|
||||
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
|
||||
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
||||
rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
|
||||
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
|
||||
|
@ -133,37 +134,62 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
existingClusterRoles, err := clientset.ClusterRoles().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
// only initialized on empty etcd
|
||||
if len(existingClusterRoles.Items) == 0 {
|
||||
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
|
||||
if _, err := clientset.ClusterRoles().Create(&clusterRole); err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
|
||||
continue
|
||||
// ensure bootstrap roles are created or reconciled
|
||||
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
|
||||
opts := reconciliation.ReconcileClusterRoleOptions{
|
||||
Role: &clusterRole,
|
||||
Client: clientset.ClusterRoles(),
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Created clusterrole.%s/%s", rbac.GroupName, clusterRole.Name)
|
||||
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
|
||||
})
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
existingClusterRoleBindings, err := clientset.ClusterRoleBindings().List(metav1.ListOptions{})
|
||||
if err != nil {
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
|
||||
return false, nil
|
||||
}
|
||||
// only initialized on empty etcd
|
||||
if len(existingClusterRoleBindings.Items) == 0 {
|
||||
for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) {
|
||||
if _, err := clientset.ClusterRoleBindings().Create(&clusterRoleBinding); err != nil {
|
||||
// don't fail on failures, try to create as many as you can
|
||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
|
||||
continue
|
||||
// ensure bootstrap rolebindings are created or reconciled
|
||||
for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) {
|
||||
|
||||
opts := reconciliation.ReconcileClusterRoleBindingOptions{
|
||||
RoleBinding: &clusterRoleBinding,
|
||||
Client: clientset.ClusterRoleBindings(),
|
||||
Confirm: true,
|
||||
}
|
||||
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||
result, err := opts.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Created clusterrolebinding.%s/%s", rbac.GroupName, clusterRoleBinding.Name)
|
||||
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
|
||||
})
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,8 @@ var (
|
|||
ReadWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
|
||||
Read = []string{"get", "list", "watch"}
|
||||
|
||||
Label = map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}
|
||||
Label = map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}
|
||||
Annotation = map[string]string{rbac.AutoUpdateAnnotationKey: "true"}
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -51,6 +52,13 @@ func addClusterRoleLabel(roles []rbac.ClusterRole) {
|
|||
for k, v := range Label {
|
||||
roles[i].ObjectMeta.Labels[k] = v
|
||||
}
|
||||
|
||||
if roles[i].ObjectMeta.Annotations == nil {
|
||||
roles[i].ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
for k, v := range Annotation {
|
||||
roles[i].ObjectMeta.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -63,6 +71,13 @@ func addClusterRoleBindingLabel(rolebindings []rbac.ClusterRoleBinding) {
|
|||
for k, v := range Label {
|
||||
rolebindings[i].ObjectMeta.Labels[k] = v
|
||||
}
|
||||
|
||||
if rolebindings[i].ObjectMeta.Annotations == nil {
|
||||
rolebindings[i].ObjectMeta.Annotations = make(map[string]string)
|
||||
}
|
||||
for k, v := range Annotation {
|
||||
rolebindings[i].ObjectMeta.Annotations[k] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -18,6 +20,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -36,6 +40,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -54,6 +60,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -69,6 +77,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -84,6 +94,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
|
|
@ -3,6 +3,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -159,6 +161,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -177,6 +181,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -313,6 +319,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -333,6 +341,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -347,6 +357,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -365,6 +377,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -384,6 +398,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -401,6 +417,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -506,6 +524,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -618,6 +638,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -635,6 +657,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -663,6 +687,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -693,6 +719,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -742,6 +770,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
|
|
@ -3,6 +3,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -18,6 +20,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -33,6 +37,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -48,6 +54,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -63,6 +71,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -78,6 +88,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -93,6 +105,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -108,6 +122,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -123,6 +139,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -138,6 +156,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -153,6 +173,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -168,6 +190,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -183,6 +207,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -198,6 +224,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -213,6 +241,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -228,6 +258,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -243,6 +275,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -258,6 +292,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -273,6 +309,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -288,6 +326,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -303,6 +343,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -318,6 +360,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
|
|
@ -3,6 +3,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -49,6 +51,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -80,6 +84,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -129,6 +135,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -181,6 +189,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -233,6 +243,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -295,6 +307,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -336,6 +350,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -363,6 +379,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -430,6 +448,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -470,6 +490,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -503,6 +525,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -547,6 +571,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -634,6 +660,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -656,6 +684,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -697,6 +727,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -738,6 +770,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -767,6 +801,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -796,6 +832,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -818,6 +856,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -855,6 +895,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
@ -908,6 +950,8 @@ items:
|
|||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
annotations:
|
||||
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||
creationTimestamp: null
|
||||
labels:
|
||||
kubernetes.io/bootstrapping: rbac-defaults
|
||||
|
|
Loading…
Reference in New Issue