mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-03 17:30:00 +00:00
Reconcile bootstrap clusterroles on server start
This commit is contained in:
parent
436fa5c9d1
commit
26b42d350d
@ -34,6 +34,9 @@ const (
|
|||||||
GroupKind = "Group"
|
GroupKind = "Group"
|
||||||
ServiceAccountKind = "ServiceAccount"
|
ServiceAccountKind = "ServiceAccount"
|
||||||
UserKind = "User"
|
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
|
// PolicyRule holds information that describes a policy rule, but does not contain information
|
||||||
|
@ -34,6 +34,9 @@ const (
|
|||||||
GroupKind = "Group"
|
GroupKind = "Group"
|
||||||
ServiceAccountKind = "ServiceAccount"
|
ServiceAccountKind = "ServiceAccount"
|
||||||
UserKind = "User"
|
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
|
// Authorization is calculated against
|
||||||
|
@ -34,6 +34,9 @@ const (
|
|||||||
GroupKind = "Group"
|
GroupKind = "Group"
|
||||||
ServiceAccountKind = "ServiceAccount"
|
ServiceAccountKind = "ServiceAccount"
|
||||||
UserKind = "User"
|
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
|
// Authorization is calculated against
|
||||||
|
@ -33,6 +33,7 @@ filegroup(
|
|||||||
":package-srcs",
|
":package-srcs",
|
||||||
"//pkg/registry/rbac/clusterrole:all-srcs",
|
"//pkg/registry/rbac/clusterrole:all-srcs",
|
||||||
"//pkg/registry/rbac/clusterrolebinding:all-srcs",
|
"//pkg/registry/rbac/clusterrolebinding:all-srcs",
|
||||||
|
"//pkg/registry/rbac/reconciliation:all-srcs",
|
||||||
"//pkg/registry/rbac/rest:all-srcs",
|
"//pkg/registry/rbac/rest:all-srcs",
|
||||||
"//pkg/registry/rbac/role:all-srcs",
|
"//pkg/registry/rbac/role:all-srcs",
|
||||||
"//pkg/registry/rbac/rolebinding:all-srcs",
|
"//pkg/registry/rbac/rolebinding:all-srcs",
|
||||||
|
54
pkg/registry/rbac/reconciliation/BUILD
Normal file
54
pkg/registry/rbac/reconciliation/BUILD
Normal file
@ -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"],
|
||||||
|
)
|
200
pkg/registry/rbac/reconciliation/reconcile_clusterrole.go
Normal file
200
pkg/registry/rbac/reconciliation/reconcile_clusterrole.go
Normal file
@ -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
|
||||||
|
}
|
273
pkg/registry/rbac/reconciliation/reconcile_clusterrole_test.go
Normal file
273
pkg/registry/rbac/reconciliation/reconcile_clusterrole_test.go
Normal file
@ -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/v1alpha1:go_default_library",
|
||||||
"//pkg/apis/rbac/v1beta1:go_default_library",
|
"//pkg/apis/rbac/v1beta1:go_default_library",
|
||||||
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion: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:go_default_library",
|
||||||
"//pkg/registry/rbac/clusterrole/policybased:go_default_library",
|
"//pkg/registry/rbac/clusterrole/policybased:go_default_library",
|
||||||
"//pkg/registry/rbac/clusterrole/storage:go_default_library",
|
"//pkg/registry/rbac/clusterrole/storage:go_default_library",
|
||||||
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
"//pkg/registry/rbac/clusterrolebinding:go_default_library",
|
||||||
"//pkg/registry/rbac/clusterrolebinding/policybased:go_default_library",
|
"//pkg/registry/rbac/clusterrolebinding/policybased:go_default_library",
|
||||||
"//pkg/registry/rbac/clusterrolebinding/storage: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:go_default_library",
|
||||||
"//pkg/registry/rbac/role/policybased:go_default_library",
|
"//pkg/registry/rbac/role/policybased:go_default_library",
|
||||||
"//pkg/registry/rbac/role/storage:go_default_library",
|
"//pkg/registry/rbac/role/storage:go_default_library",
|
||||||
@ -32,7 +34,6 @@ go_library(
|
|||||||
"//pkg/registry/rbac/validation:go_default_library",
|
"//pkg/registry/rbac/validation:go_default_library",
|
||||||
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
"//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library",
|
||||||
"//vendor:github.com/golang/glog",
|
"//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/runtime/schema",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
@ -36,12 +35,14 @@ import (
|
|||||||
rbacapiv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
rbacapiv1alpha1 "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
||||||
rbacapiv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
|
rbacapiv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
|
||||||
rbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
|
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"
|
"k8s.io/kubernetes/pkg/registry/rbac/clusterrole"
|
||||||
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
|
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/policybased"
|
||||||
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
|
clusterrolestore "k8s.io/kubernetes/pkg/registry/rbac/clusterrole/storage"
|
||||||
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
"k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding"
|
||||||
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
|
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased"
|
||||||
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
|
clusterrolebindingstore "k8s.io/kubernetes/pkg/registry/rbac/clusterrolebinding/storage"
|
||||||
|
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
|
||||||
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
"k8s.io/kubernetes/pkg/registry/rbac/role"
|
||||||
rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
|
rolepolicybased "k8s.io/kubernetes/pkg/registry/rbac/role/policybased"
|
||||||
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
|
rolestore "k8s.io/kubernetes/pkg/registry/rbac/role/storage"
|
||||||
@ -133,37 +134,62 @@ func PostStartHook(hookContext genericapiserver.PostStartHookContext) error {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
existingClusterRoles, err := clientset.ClusterRoles().List(metav1.ListOptions{})
|
// ensure bootstrap roles are created or reconciled
|
||||||
if err != nil {
|
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
|
||||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
|
opts := reconciliation.ReconcileClusterRoleOptions{
|
||||||
return false, nil
|
Role: &clusterRole,
|
||||||
}
|
Client: clientset.ClusterRoles(),
|
||||||
// only initialized on empty etcd
|
Confirm: true,
|
||||||
if len(existingClusterRoles.Items) == 0 {
|
}
|
||||||
for _, clusterRole := range append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...) {
|
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||||
if _, err := clientset.ClusterRoles().Create(&clusterRole); err != nil {
|
result, err := opts.Run()
|
||||||
// don't fail on failures, try to create as many as you can
|
if err != nil {
|
||||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterroles: %v", err))
|
return err
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
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{})
|
// ensure bootstrap rolebindings are created or reconciled
|
||||||
if err != nil {
|
for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) {
|
||||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
|
|
||||||
return false, nil
|
opts := reconciliation.ReconcileClusterRoleBindingOptions{
|
||||||
}
|
RoleBinding: &clusterRoleBinding,
|
||||||
// only initialized on empty etcd
|
Client: clientset.ClusterRoleBindings(),
|
||||||
if len(existingClusterRoleBindings.Items) == 0 {
|
Confirm: true,
|
||||||
for _, clusterRoleBinding := range append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...) {
|
}
|
||||||
if _, err := clientset.ClusterRoleBindings().Create(&clusterRoleBinding); err != nil {
|
err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
|
||||||
// don't fail on failures, try to create as many as you can
|
result, err := opts.Run()
|
||||||
utilruntime.HandleError(fmt.Errorf("unable to initialize clusterrolebindings: %v", err))
|
if err != nil {
|
||||||
continue
|
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"}
|
ReadWrite = []string{"get", "list", "watch", "create", "update", "patch", "delete", "deletecollection"}
|
||||||
Read = []string{"get", "list", "watch"}
|
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 (
|
const (
|
||||||
@ -51,6 +52,13 @@ func addClusterRoleLabel(roles []rbac.ClusterRole) {
|
|||||||
for k, v := range Label {
|
for k, v := range Label {
|
||||||
roles[i].ObjectMeta.Labels[k] = v
|
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
|
return
|
||||||
}
|
}
|
||||||
@ -63,6 +71,13 @@ func addClusterRoleBindingLabel(rolebindings []rbac.ClusterRoleBinding) {
|
|||||||
for k, v := range Label {
|
for k, v := range Label {
|
||||||
rolebindings[i].ObjectMeta.Labels[k] = v
|
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
|
return
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -18,6 +20,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -36,6 +40,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -54,6 +60,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -69,6 +77,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -84,6 +94,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
|
@ -3,6 +3,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -159,6 +161,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -177,6 +181,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -313,6 +319,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -333,6 +341,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -347,6 +357,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -365,6 +377,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -384,6 +398,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -401,6 +417,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -506,6 +524,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -618,6 +638,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -635,6 +657,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -663,6 +687,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -693,6 +719,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -742,6 +770,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
|
@ -3,6 +3,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -18,6 +20,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -33,6 +37,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -48,6 +54,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -63,6 +71,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -78,6 +88,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -93,6 +105,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -108,6 +122,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -123,6 +139,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -138,6 +156,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -153,6 +173,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -168,6 +190,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -183,6 +207,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -198,6 +224,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -213,6 +241,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -228,6 +258,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -243,6 +275,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -258,6 +292,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -273,6 +309,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -288,6 +326,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -303,6 +343,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -318,6 +360,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
|
@ -3,6 +3,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -49,6 +51,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -80,6 +84,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -129,6 +135,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -181,6 +189,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -233,6 +243,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -295,6 +307,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -336,6 +350,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -363,6 +379,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -430,6 +448,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -470,6 +490,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -503,6 +525,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -547,6 +571,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -634,6 +660,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -656,6 +684,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -697,6 +727,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -738,6 +770,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -767,6 +801,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -796,6 +832,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -818,6 +856,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -855,6 +895,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
@ -908,6 +950,8 @@ items:
|
|||||||
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
- apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
|
annotations:
|
||||||
|
rbac.authorization.kubernetes.io/autoupdate: "true"
|
||||||
creationTimestamp: null
|
creationTimestamp: null
|
||||||
labels:
|
labels:
|
||||||
kubernetes.io/bootstrapping: rbac-defaults
|
kubernetes.io/bootstrapping: rbac-defaults
|
||||||
|
Loading…
Reference in New Issue
Block a user