Unverified Commit f575c555 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #54005 from deads2k/rbac-02-aggregation

Automatic merge from submit-queue (batch tested with PRs 54005, 55127, 53850, 55486, 53440). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. aggregate cluster roles xref https://github.com/kubernetes/community/pull/1219 https://github.com/kubernetes/features/issues/502 This is a pull with API types, a controller, and a demonstration of how to move admin, edit, and view. Once we agree on the shape, I'll I added ```yaml aggregationRule: clusterRoleSelectors: - matchLabels: rbac.authorization.k8s.io/aggregate-to-admin: true ``` to the `ClusterRole`. A controller then goes and gathers all the matching ClusterRoles and sets the `rules` to the union of matching cluster roles. @kubernetes/sig-auth-pr-reviews ```release-note RBAC ClusterRoles can now select other roles to aggregate ```
parents beefab8a f34fb9b0
...@@ -78754,12 +78754,28 @@ ...@@ -78754,12 +78754,28 @@
} }
} }
}, },
"io.k8s.api.rbac.v1.AggregationRule": {
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
}
}
}
},
"io.k8s.api.rbac.v1.ClusterRole": { "io.k8s.api.rbac.v1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"required": [ "required": [
"rules" "rules"
], ],
"properties": { "properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
"$ref": "#/definitions/io.k8s.api.rbac.v1.AggregationRule"
},
"apiVersion": { "apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
"type": "string" "type": "string"
...@@ -79126,12 +79142,28 @@ ...@@ -79126,12 +79142,28 @@
} }
} }
}, },
"io.k8s.api.rbac.v1alpha1.AggregationRule": {
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
}
}
}
},
"io.k8s.api.rbac.v1alpha1.ClusterRole": { "io.k8s.api.rbac.v1alpha1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"required": [ "required": [
"rules" "rules"
], ],
"properties": { "properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
"$ref": "#/definitions/io.k8s.api.rbac.v1alpha1.AggregationRule"
},
"apiVersion": { "apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
"type": "string" "type": "string"
...@@ -79498,12 +79530,28 @@ ...@@ -79498,12 +79530,28 @@
} }
} }
}, },
"io.k8s.api.rbac.v1beta1.AggregationRule": {
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
"type": "array",
"items": {
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
}
}
}
},
"io.k8s.api.rbac.v1beta1.ClusterRole": { "io.k8s.api.rbac.v1beta1.ClusterRole": {
"description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "description": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"required": [ "required": [
"rules" "rules"
], ],
"properties": { "properties": {
"aggregationRule": {
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
"$ref": "#/definitions/io.k8s.api.rbac.v1beta1.AggregationRule"
},
"apiVersion": { "apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources", "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#resources",
"type": "string" "type": "string"
...@@ -3789,6 +3789,10 @@ ...@@ -3789,6 +3789,10 @@
"$ref": "v1.PolicyRule" "$ref": "v1.PolicyRule"
}, },
"description": "Rules holds all the PolicyRules for this ClusterRole" "description": "Rules holds all the PolicyRules for this ClusterRole"
},
"aggregationRule": {
"$ref": "v1.AggregationRule",
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller."
} }
} }
}, },
...@@ -3836,6 +3840,61 @@ ...@@ -3836,6 +3840,61 @@
} }
} }
}, },
"v1.AggregationRule": {
"id": "v1.AggregationRule",
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"type": "array",
"items": {
"$ref": "v1.LabelSelector"
},
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added"
}
}
},
"v1.LabelSelector": {
"id": "v1.LabelSelector",
"description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
"properties": {
"matchLabels": {
"type": "object",
"description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
},
"matchExpressions": {
"type": "array",
"items": {
"$ref": "v1.LabelSelectorRequirement"
},
"description": "matchExpressions is a list of label selector requirements. The requirements are ANDed."
}
}
},
"v1.LabelSelectorRequirement": {
"id": "v1.LabelSelectorRequirement",
"description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"required": [
"key",
"operator"
],
"properties": {
"key": {
"type": "string",
"description": "key is the label key that the selector applies to."
},
"operator": {
"type": "string",
"description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
},
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
}
}
},
"v1.RoleBindingList": { "v1.RoleBindingList": {
"id": "v1.RoleBindingList", "id": "v1.RoleBindingList",
"description": "RoleBindingList is a collection of RoleBindings", "description": "RoleBindingList is a collection of RoleBindings",
......
...@@ -3789,6 +3789,10 @@ ...@@ -3789,6 +3789,10 @@
"$ref": "v1alpha1.PolicyRule" "$ref": "v1alpha1.PolicyRule"
}, },
"description": "Rules holds all the PolicyRules for this ClusterRole" "description": "Rules holds all the PolicyRules for this ClusterRole"
},
"aggregationRule": {
"$ref": "v1alpha1.AggregationRule",
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller."
} }
} }
}, },
...@@ -3836,6 +3840,61 @@ ...@@ -3836,6 +3840,61 @@
} }
} }
}, },
"v1alpha1.AggregationRule": {
"id": "v1alpha1.AggregationRule",
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"type": "array",
"items": {
"$ref": "v1.LabelSelector"
},
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added"
}
}
},
"v1.LabelSelector": {
"id": "v1.LabelSelector",
"description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
"properties": {
"matchLabels": {
"type": "object",
"description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
},
"matchExpressions": {
"type": "array",
"items": {
"$ref": "v1.LabelSelectorRequirement"
},
"description": "matchExpressions is a list of label selector requirements. The requirements are ANDed."
}
}
},
"v1.LabelSelectorRequirement": {
"id": "v1.LabelSelectorRequirement",
"description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"required": [
"key",
"operator"
],
"properties": {
"key": {
"type": "string",
"description": "key is the label key that the selector applies to."
},
"operator": {
"type": "string",
"description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
},
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
}
}
},
"v1alpha1.RoleBindingList": { "v1alpha1.RoleBindingList": {
"id": "v1alpha1.RoleBindingList", "id": "v1alpha1.RoleBindingList",
"description": "RoleBindingList is a collection of RoleBindings", "description": "RoleBindingList is a collection of RoleBindings",
......
...@@ -3789,6 +3789,10 @@ ...@@ -3789,6 +3789,10 @@
"$ref": "v1beta1.PolicyRule" "$ref": "v1beta1.PolicyRule"
}, },
"description": "Rules holds all the PolicyRules for this ClusterRole" "description": "Rules holds all the PolicyRules for this ClusterRole"
},
"aggregationRule": {
"$ref": "v1beta1.AggregationRule",
"description": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller."
} }
} }
}, },
...@@ -3836,6 +3840,61 @@ ...@@ -3836,6 +3840,61 @@
} }
} }
}, },
"v1beta1.AggregationRule": {
"id": "v1beta1.AggregationRule",
"description": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"properties": {
"clusterRoleSelectors": {
"type": "array",
"items": {
"$ref": "v1.LabelSelector"
},
"description": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added"
}
}
},
"v1.LabelSelector": {
"id": "v1.LabelSelector",
"description": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
"properties": {
"matchLabels": {
"type": "object",
"description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed."
},
"matchExpressions": {
"type": "array",
"items": {
"$ref": "v1.LabelSelectorRequirement"
},
"description": "matchExpressions is a list of label selector requirements. The requirements are ANDed."
}
}
},
"v1.LabelSelectorRequirement": {
"id": "v1.LabelSelectorRequirement",
"description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"required": [
"key",
"operator"
],
"properties": {
"key": {
"type": "string",
"description": "key is the label key that the selector applies to."
},
"operator": {
"type": "string",
"description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist."
},
"values": {
"type": "array",
"items": {
"type": "string"
},
"description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch."
}
}
},
"v1beta1.RoleBindingList": { "v1beta1.RoleBindingList": {
"id": "v1beta1.RoleBindingList", "id": "v1beta1.RoleBindingList",
"description": "RoleBindingList is a collection of RoleBindings", "description": "RoleBindingList is a collection of RoleBindings",
......
...@@ -20,6 +20,7 @@ go_library( ...@@ -20,6 +20,7 @@ go_library(
"import_known_versions.go", "import_known_versions.go",
"plugins.go", "plugins.go",
"policy.go", "policy.go",
"rbac.go",
], ],
importpath = "k8s.io/kubernetes/cmd/kube-controller-manager/app", importpath = "k8s.io/kubernetes/cmd/kube-controller-manager/app",
deps = [ deps = [
...@@ -52,6 +53,7 @@ go_library( ...@@ -52,6 +53,7 @@ go_library(
"//pkg/controller/certificates/approver:go_default_library", "//pkg/controller/certificates/approver:go_default_library",
"//pkg/controller/certificates/cleaner:go_default_library", "//pkg/controller/certificates/cleaner:go_default_library",
"//pkg/controller/certificates/signer:go_default_library", "//pkg/controller/certificates/signer:go_default_library",
"//pkg/controller/clusterroleaggregation:go_default_library",
"//pkg/controller/cronjob:go_default_library", "//pkg/controller/cronjob:go_default_library",
"//pkg/controller/daemon:go_default_library", "//pkg/controller/daemon:go_default_library",
"//pkg/controller/deployment:go_default_library", "//pkg/controller/deployment:go_default_library",
......
...@@ -358,6 +358,7 @@ func NewControllerInitializers() map[string]InitFunc { ...@@ -358,6 +358,7 @@ func NewControllerInitializers() map[string]InitFunc {
controllers["persistentvolume-binder"] = startPersistentVolumeBinderController controllers["persistentvolume-binder"] = startPersistentVolumeBinderController
controllers["attachdetach"] = startAttachDetachController controllers["attachdetach"] = startAttachDetachController
controllers["persistentvolume-expander"] = startVolumeExpandController controllers["persistentvolume-expander"] = startVolumeExpandController
controllers["clusterrole-aggregation"] = startClusterRoleAggregrationController
return controllers return controllers
} }
......
/*
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 app
import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/controller/clusterroleaggregation"
)
func startClusterRoleAggregrationController(ctx ControllerContext) (bool, error) {
if !ctx.AvailableResources[schema.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "v1", Resource: "clusterroles"}] {
return false, nil
}
go clusterroleaggregation.NewClusterRoleAggregation(
ctx.InformerFactory.Rbac().V1().ClusterRoles(),
ctx.ClientBuilder.ClientOrDie("clusterrole-aggregation-controller").RbacV1(),
).Run(5, ctx.Stop)
return true, nil
}
...@@ -662,6 +662,40 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } ...@@ -662,6 +662,40 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_v1alpha1_aggregationrule">v1alpha1.AggregationRule</h3>
<div class="paragraph">
<p>AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">clusterRoleSelectors</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole&#8217;s permissions will be added</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_labelselector">v1.LabelSelector</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_watchevent">v1.WatchEvent</h3> <h3 id="_v1_watchevent">v1.WatchEvent</h3>
<table class="tableblock frame-all grid-all" style="width:100%; "> <table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup> <colgroup>
...@@ -755,6 +789,95 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } ...@@ -755,6 +789,95 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
</div> </div>
<div class="sect2"> <div class="sect2">
<h3 id="_v1_labelselector">v1.LabelSelector</h3>
<div class="paragraph">
<p>A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">matchLabels</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">object</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">matchExpressions</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">matchExpressions is a list of label selector requirements. The requirements are ANDed.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_labelselectorrequirement">v1.LabelSelectorRequirement</a> array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1_labelselectorrequirement">v1.LabelSelectorRequirement</h3>
<div class="paragraph">
<p>A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.</p>
</div>
<table class="tableblock frame-all grid-all" style="width:100%; ">
<colgroup>
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
<col style="width:20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">Name</th>
<th class="tableblock halign-left valign-top">Description</th>
<th class="tableblock halign-left valign-top">Required</th>
<th class="tableblock halign-left valign-top">Schema</th>
<th class="tableblock halign-left valign-top">Default</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">key</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">key is the label key that the selector applies to.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">operator</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">operator represents a key&#8217;s relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">true</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">values</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">string array</p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody>
</table>
</div>
<div class="sect2">
<h3 id="_v1alpha1_rolebinding">v1alpha1.RoleBinding</h3> <h3 id="_v1alpha1_rolebinding">v1alpha1.RoleBinding</h3>
<div class="paragraph"> <div class="paragraph">
<p>RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.</p> <p>RoleBinding references a role, but does not contain it. It can reference a Role in the same namespace or a ClusterRole in the global namespace. It adds who information via Subjects and namespace information by which namespace it exists in. RoleBindings in a given namespace only have effect in that namespace.</p>
...@@ -1386,6 +1509,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; } ...@@ -1386,6 +1509,13 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1alpha1_policyrule">v1alpha1.PolicyRule</a> array</p></td> <td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1alpha1_policyrule">v1alpha1.PolicyRule</a> array</p></td>
<td class="tableblock halign-left valign-top"></td> <td class="tableblock halign-left valign-top"></td>
</tr> </tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">aggregationRule</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1alpha1_aggregationrule">v1alpha1.AggregationRule</a></p></td>
<td class="tableblock halign-left valign-top"></td>
</tr>
</tbody> </tbody>
</table> </table>
......
...@@ -96,6 +96,7 @@ pkg/controller/certificates ...@@ -96,6 +96,7 @@ pkg/controller/certificates
pkg/controller/certificates/approver pkg/controller/certificates/approver
pkg/controller/certificates/signer pkg/controller/certificates/signer
pkg/controller/cloud pkg/controller/cloud
pkg/controller/clusterroleaggregation
pkg/controller/cronjob pkg/controller/cronjob
pkg/controller/daemon pkg/controller/daemon
pkg/controller/daemon/util pkg/controller/daemon/util
......
...@@ -155,6 +155,18 @@ type ClusterRole struct { ...@@ -155,6 +155,18 @@ type ClusterRole struct {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
Rules []PolicyRule Rules []PolicyRule
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
AggregationRule *AggregationRule
}
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
type AggregationRule struct {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
ClusterRoleSelectors []metav1.LabelSelector
} }
// +genclient // +genclient
......
...@@ -22,6 +22,7 @@ package v1 ...@@ -22,6 +22,7 @@ package v1
import ( import (
v1 "k8s.io/api/rbac/v1" v1 "k8s.io/api/rbac/v1"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
rbac "k8s.io/kubernetes/pkg/apis/rbac" rbac "k8s.io/kubernetes/pkg/apis/rbac"
...@@ -36,6 +37,8 @@ func init() { ...@@ -36,6 +37,8 @@ func init() {
// Public to allow building arbitrary schemes. // Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error { func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs( return scheme.AddGeneratedConversionFuncs(
Convert_v1_AggregationRule_To_rbac_AggregationRule,
Convert_rbac_AggregationRule_To_v1_AggregationRule,
Convert_v1_ClusterRole_To_rbac_ClusterRole, Convert_v1_ClusterRole_To_rbac_ClusterRole,
Convert_rbac_ClusterRole_To_v1_ClusterRole, Convert_rbac_ClusterRole_To_v1_ClusterRole,
Convert_v1_ClusterRoleBinding_To_rbac_ClusterRoleBinding, Convert_v1_ClusterRoleBinding_To_rbac_ClusterRoleBinding,
...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error {
) )
} }
func autoConvert_v1_AggregationRule_To_rbac_AggregationRule(in *v1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]meta_v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_v1_AggregationRule_To_rbac_AggregationRule is an autogenerated conversion function.
func Convert_v1_AggregationRule_To_rbac_AggregationRule(in *v1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
return autoConvert_v1_AggregationRule_To_rbac_AggregationRule(in, out, s)
}
func autoConvert_rbac_AggregationRule_To_v1_AggregationRule(in *rbac.AggregationRule, out *v1.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]meta_v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_rbac_AggregationRule_To_v1_AggregationRule is an autogenerated conversion function.
func Convert_rbac_AggregationRule_To_v1_AggregationRule(in *rbac.AggregationRule, out *v1.AggregationRule, s conversion.Scope) error {
return autoConvert_rbac_AggregationRule_To_v1_AggregationRule(in, out, s)
}
func autoConvert_v1_ClusterRole_To_rbac_ClusterRole(in *v1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error { func autoConvert_v1_ClusterRole_To_rbac_ClusterRole(in *v1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*rbac.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
...@@ -75,6 +99,7 @@ func Convert_v1_ClusterRole_To_rbac_ClusterRole(in *v1.ClusterRole, out *rbac.Cl ...@@ -75,6 +99,7 @@ func Convert_v1_ClusterRole_To_rbac_ClusterRole(in *v1.ClusterRole, out *rbac.Cl
func autoConvert_rbac_ClusterRole_To_v1_ClusterRole(in *rbac.ClusterRole, out *v1.ClusterRole, s conversion.Scope) error { func autoConvert_rbac_ClusterRole_To_v1_ClusterRole(in *rbac.ClusterRole, out *v1.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]v1.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]v1.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*v1.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
......
...@@ -22,6 +22,7 @@ package v1alpha1 ...@@ -22,6 +22,7 @@ package v1alpha1
import ( import (
v1alpha1 "k8s.io/api/rbac/v1alpha1" v1alpha1 "k8s.io/api/rbac/v1alpha1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
rbac "k8s.io/kubernetes/pkg/apis/rbac" rbac "k8s.io/kubernetes/pkg/apis/rbac"
...@@ -36,6 +37,8 @@ func init() { ...@@ -36,6 +37,8 @@ func init() {
// Public to allow building arbitrary schemes. // Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error { func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs( return scheme.AddGeneratedConversionFuncs(
Convert_v1alpha1_AggregationRule_To_rbac_AggregationRule,
Convert_rbac_AggregationRule_To_v1alpha1_AggregationRule,
Convert_v1alpha1_ClusterRole_To_rbac_ClusterRole, Convert_v1alpha1_ClusterRole_To_rbac_ClusterRole,
Convert_rbac_ClusterRole_To_v1alpha1_ClusterRole, Convert_rbac_ClusterRole_To_v1alpha1_ClusterRole,
Convert_v1alpha1_ClusterRoleBinding_To_rbac_ClusterRoleBinding, Convert_v1alpha1_ClusterRoleBinding_To_rbac_ClusterRoleBinding,
...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error {
) )
} }
func autoConvert_v1alpha1_AggregationRule_To_rbac_AggregationRule(in *v1alpha1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_v1alpha1_AggregationRule_To_rbac_AggregationRule is an autogenerated conversion function.
func Convert_v1alpha1_AggregationRule_To_rbac_AggregationRule(in *v1alpha1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
return autoConvert_v1alpha1_AggregationRule_To_rbac_AggregationRule(in, out, s)
}
func autoConvert_rbac_AggregationRule_To_v1alpha1_AggregationRule(in *rbac.AggregationRule, out *v1alpha1.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_rbac_AggregationRule_To_v1alpha1_AggregationRule is an autogenerated conversion function.
func Convert_rbac_AggregationRule_To_v1alpha1_AggregationRule(in *rbac.AggregationRule, out *v1alpha1.AggregationRule, s conversion.Scope) error {
return autoConvert_rbac_AggregationRule_To_v1alpha1_AggregationRule(in, out, s)
}
func autoConvert_v1alpha1_ClusterRole_To_rbac_ClusterRole(in *v1alpha1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error { func autoConvert_v1alpha1_ClusterRole_To_rbac_ClusterRole(in *v1alpha1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*rbac.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
...@@ -75,6 +99,7 @@ func Convert_v1alpha1_ClusterRole_To_rbac_ClusterRole(in *v1alpha1.ClusterRole, ...@@ -75,6 +99,7 @@ func Convert_v1alpha1_ClusterRole_To_rbac_ClusterRole(in *v1alpha1.ClusterRole,
func autoConvert_rbac_ClusterRole_To_v1alpha1_ClusterRole(in *rbac.ClusterRole, out *v1alpha1.ClusterRole, s conversion.Scope) error { func autoConvert_rbac_ClusterRole_To_v1alpha1_ClusterRole(in *rbac.ClusterRole, out *v1alpha1.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]v1alpha1.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]v1alpha1.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*v1alpha1.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
......
...@@ -22,6 +22,7 @@ package v1beta1 ...@@ -22,6 +22,7 @@ package v1beta1
import ( import (
v1beta1 "k8s.io/api/rbac/v1beta1" v1beta1 "k8s.io/api/rbac/v1beta1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
conversion "k8s.io/apimachinery/pkg/conversion" conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
rbac "k8s.io/kubernetes/pkg/apis/rbac" rbac "k8s.io/kubernetes/pkg/apis/rbac"
...@@ -36,6 +37,8 @@ func init() { ...@@ -36,6 +37,8 @@ func init() {
// Public to allow building arbitrary schemes. // Public to allow building arbitrary schemes.
func RegisterConversions(scheme *runtime.Scheme) error { func RegisterConversions(scheme *runtime.Scheme) error {
return scheme.AddGeneratedConversionFuncs( return scheme.AddGeneratedConversionFuncs(
Convert_v1beta1_AggregationRule_To_rbac_AggregationRule,
Convert_rbac_AggregationRule_To_v1beta1_AggregationRule,
Convert_v1beta1_ClusterRole_To_rbac_ClusterRole, Convert_v1beta1_ClusterRole_To_rbac_ClusterRole,
Convert_rbac_ClusterRole_To_v1beta1_ClusterRole, Convert_rbac_ClusterRole_To_v1beta1_ClusterRole,
Convert_v1beta1_ClusterRoleBinding_To_rbac_ClusterRoleBinding, Convert_v1beta1_ClusterRoleBinding_To_rbac_ClusterRoleBinding,
...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error { ...@@ -61,9 +64,30 @@ func RegisterConversions(scheme *runtime.Scheme) error {
) )
} }
func autoConvert_v1beta1_AggregationRule_To_rbac_AggregationRule(in *v1beta1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_v1beta1_AggregationRule_To_rbac_AggregationRule is an autogenerated conversion function.
func Convert_v1beta1_AggregationRule_To_rbac_AggregationRule(in *v1beta1.AggregationRule, out *rbac.AggregationRule, s conversion.Scope) error {
return autoConvert_v1beta1_AggregationRule_To_rbac_AggregationRule(in, out, s)
}
func autoConvert_rbac_AggregationRule_To_v1beta1_AggregationRule(in *rbac.AggregationRule, out *v1beta1.AggregationRule, s conversion.Scope) error {
out.ClusterRoleSelectors = *(*[]v1.LabelSelector)(unsafe.Pointer(&in.ClusterRoleSelectors))
return nil
}
// Convert_rbac_AggregationRule_To_v1beta1_AggregationRule is an autogenerated conversion function.
func Convert_rbac_AggregationRule_To_v1beta1_AggregationRule(in *rbac.AggregationRule, out *v1beta1.AggregationRule, s conversion.Scope) error {
return autoConvert_rbac_AggregationRule_To_v1beta1_AggregationRule(in, out, s)
}
func autoConvert_v1beta1_ClusterRole_To_rbac_ClusterRole(in *v1beta1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error { func autoConvert_v1beta1_ClusterRole_To_rbac_ClusterRole(in *v1beta1.ClusterRole, out *rbac.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]rbac.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*rbac.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
...@@ -75,6 +99,7 @@ func Convert_v1beta1_ClusterRole_To_rbac_ClusterRole(in *v1beta1.ClusterRole, ou ...@@ -75,6 +99,7 @@ func Convert_v1beta1_ClusterRole_To_rbac_ClusterRole(in *v1beta1.ClusterRole, ou
func autoConvert_rbac_ClusterRole_To_v1beta1_ClusterRole(in *rbac.ClusterRole, out *v1beta1.ClusterRole, s conversion.Scope) error { func autoConvert_rbac_ClusterRole_To_v1beta1_ClusterRole(in *rbac.ClusterRole, out *v1beta1.ClusterRole, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta out.ObjectMeta = in.ObjectMeta
out.Rules = *(*[]v1beta1.PolicyRule)(unsafe.Pointer(&in.Rules)) out.Rules = *(*[]v1beta1.PolicyRule)(unsafe.Pointer(&in.Rules))
out.AggregationRule = (*v1beta1.AggregationRule)(unsafe.Pointer(in.AggregationRule))
return nil return nil
} }
......
...@@ -14,6 +14,8 @@ go_library( ...@@ -14,6 +14,8 @@ go_library(
"//pkg/apis/core/validation:go_default_library", "//pkg/apis/core/validation:go_default_library",
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/validation/path:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/validation/path:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
], ],
) )
......
...@@ -18,6 +18,8 @@ package validation ...@@ -18,6 +18,8 @@ package validation
import ( import (
"k8s.io/apimachinery/pkg/api/validation/path" "k8s.io/apimachinery/pkg/api/validation/path"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unversionedvalidation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
"k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/apis/core/validation" "k8s.io/kubernetes/pkg/apis/core/validation"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
...@@ -61,6 +63,22 @@ func ValidateClusterRole(role *rbac.ClusterRole) field.ErrorList { ...@@ -61,6 +63,22 @@ func ValidateClusterRole(role *rbac.ClusterRole) field.ErrorList {
allErrs = append(allErrs, err...) allErrs = append(allErrs, err...)
} }
} }
if role.AggregationRule != nil {
if len(role.AggregationRule.ClusterRoleSelectors) == 0 {
allErrs = append(allErrs, field.Required(field.NewPath("aggregationRule", "clusterRoleSelectors"), "at least one clusterRoleSelector required if aggregationRule is non-nil"))
}
for i, selector := range role.AggregationRule.ClusterRoleSelectors {
fieldPath := field.NewPath("aggregationRule", "clusterRoleSelectors").Index(i)
allErrs = append(allErrs, unversionedvalidation.ValidateLabelSelector(&selector, fieldPath)...)
selector, err := metav1.LabelSelectorAsSelector(&selector)
if err != nil {
allErrs = append(allErrs, field.Invalid(fieldPath, selector, "invalid label selector."))
}
}
}
if len(allErrs) != 0 { if len(allErrs) != 0 {
return allErrs return allErrs
} }
......
...@@ -21,10 +21,34 @@ limitations under the License. ...@@ -21,10 +21,34 @@ limitations under the License.
package rbac package rbac
import ( import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AggregationRule) DeepCopyInto(out *AggregationRule) {
*out = *in
if in.ClusterRoleSelectors != nil {
in, out := &in.ClusterRoleSelectors, &out.ClusterRoleSelectors
*out = make([]v1.LabelSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule.
func (in *AggregationRule) DeepCopy() *AggregationRule {
if in == nil {
return nil
}
out := new(AggregationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { ...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.AggregationRule != nil {
in, out := &in.AggregationRule, &out.AggregationRule
if *in == nil {
*out = nil
} else {
*out = new(AggregationRule)
(*in).DeepCopyInto(*out)
}
}
return return
} }
......
...@@ -107,6 +107,7 @@ filegroup( ...@@ -107,6 +107,7 @@ filegroup(
"//pkg/controller/bootstrap:all-srcs", "//pkg/controller/bootstrap:all-srcs",
"//pkg/controller/certificates:all-srcs", "//pkg/controller/certificates:all-srcs",
"//pkg/controller/cloud:all-srcs", "//pkg/controller/cloud:all-srcs",
"//pkg/controller/clusterroleaggregation:all-srcs",
"//pkg/controller/cronjob:all-srcs", "//pkg/controller/cronjob:all-srcs",
"//pkg/controller/daemon:all-srcs", "//pkg/controller/daemon:all-srcs",
"//pkg/controller/deployment:all-srcs", "//pkg/controller/deployment:all-srcs",
......
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["clusterroleaggregation_controller.go"],
importpath = "k8s.io/kubernetes/pkg/controller/clusterroleaggregation",
visibility = ["//visibility:public"],
deps = [
"//pkg/controller:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//vendor/k8s.io/client-go/informers/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/typed/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/listers/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
"//vendor/k8s.io/client-go/util/workqueue:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["clusterroleaggregation_controller_test.go"],
importpath = "k8s.io/kubernetes/pkg/controller/clusterroleaggregation",
library = ":go_default_library",
deps = [
"//pkg/controller:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
"//vendor/k8s.io/client-go/listers/rbac/v1:go_default_library",
"//vendor/k8s.io/client-go/testing:go_default_library",
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
],
)
/*
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 clusterroleaggregation
import (
"fmt"
"sort"
"time"
"github.com/golang/glog"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
rbacinformers "k8s.io/client-go/informers/rbac/v1"
rbacclient "k8s.io/client-go/kubernetes/typed/rbac/v1"
rbaclisters "k8s.io/client-go/listers/rbac/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
"k8s.io/kubernetes/pkg/controller"
)
// ClusterRoleAggregationController is a controller to combine cluster roles
type ClusterRoleAggregationController struct {
clusterRoleClient rbacclient.ClusterRolesGetter
clusterRoleLister rbaclisters.ClusterRoleLister
clusterRolesSynced cache.InformerSynced
syncHandler func(key string) error
queue workqueue.RateLimitingInterface
}
// NewClusterRoleAggregation creates a new controller
func NewClusterRoleAggregation(clusterRoleInformer rbacinformers.ClusterRoleInformer, clusterRoleClient rbacclient.ClusterRolesGetter) *ClusterRoleAggregationController {
c := &ClusterRoleAggregationController{
clusterRoleClient: clusterRoleClient,
clusterRoleLister: clusterRoleInformer.Lister(),
clusterRolesSynced: clusterRoleInformer.Informer().HasSynced,
queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ClusterRoleAggregator"),
}
c.syncHandler = c.syncClusterRole
clusterRoleInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
AddFunc: func(obj interface{}) {
c.enqueue()
},
UpdateFunc: func(old, cur interface{}) {
c.enqueue()
},
DeleteFunc: func(uncast interface{}) {
c.enqueue()
},
})
return c
}
func (c *ClusterRoleAggregationController) syncClusterRole(key string) error {
_, name, err := cache.SplitMetaNamespaceKey(key)
if err != nil {
return err
}
sharedClusterRole, err := c.clusterRoleLister.Get(name)
if errors.IsNotFound(err) {
return nil
}
if err != nil {
return err
}
if sharedClusterRole.AggregationRule == nil {
return nil
}
newPolicyRules := []rbacv1.PolicyRule{}
for i := range sharedClusterRole.AggregationRule.ClusterRoleSelectors {
selector := sharedClusterRole.AggregationRule.ClusterRoleSelectors[i]
runtimeLabelSelector, err := metav1.LabelSelectorAsSelector(&selector)
if err != nil {
return err
}
clusterRoles, err := c.clusterRoleLister.List(runtimeLabelSelector)
if err != nil {
return err
}
sort.Sort(byName(clusterRoles))
for i := range clusterRoles {
if clusterRoles[i].Name == sharedClusterRole.Name {
continue
}
for j := range clusterRoles[i].Rules {
currRule := clusterRoles[i].Rules[j]
if !ruleExists(newPolicyRules, currRule) {
newPolicyRules = append(newPolicyRules, currRule)
}
}
}
}
if equality.Semantic.DeepEqual(newPolicyRules, sharedClusterRole.Rules) {
return nil
}
// we need to update
clusterRole := sharedClusterRole.DeepCopy()
clusterRole.Rules = nil
for _, rule := range newPolicyRules {
clusterRole.Rules = append(clusterRole.Rules, *rule.DeepCopy())
}
_, err = c.clusterRoleClient.ClusterRoles().Update(clusterRole)
return err
}
func ruleExists(haystack []rbacv1.PolicyRule, needle rbacv1.PolicyRule) bool {
for _, curr := range haystack {
if equality.Semantic.DeepEqual(curr, needle) {
return true
}
}
return false
}
// Run starts the controller and blocks until stopCh is closed.
func (c *ClusterRoleAggregationController) Run(workers int, stopCh <-chan struct{}) {
defer utilruntime.HandleCrash()
defer c.queue.ShutDown()
glog.Infof("Starting ClusterRoleAggregator")
defer glog.Infof("Shutting down ClusterRoleAggregator")
if !controller.WaitForCacheSync("ClusterRoleAggregator", stopCh, c.clusterRolesSynced) {
return
}
for i := 0; i < workers; i++ {
go wait.Until(c.runWorker, time.Second, stopCh)
}
<-stopCh
}
func (c *ClusterRoleAggregationController) runWorker() {
for c.processNextWorkItem() {
}
}
func (c *ClusterRoleAggregationController) processNextWorkItem() bool {
dsKey, quit := c.queue.Get()
if quit {
return false
}
defer c.queue.Done(dsKey)
err := c.syncHandler(dsKey.(string))
if err == nil {
c.queue.Forget(dsKey)
return true
}
utilruntime.HandleError(fmt.Errorf("%v failed with : %v", dsKey, err))
c.queue.AddRateLimited(dsKey)
return true
}
func (c *ClusterRoleAggregationController) enqueue() {
// this is unusual, but since the set of all clusterroles is small and we don't know the dependency
// graph, just queue up every thing each time. This allows errors to be selectively retried if there
// is a problem updating a single role
allClusterRoles, err := c.clusterRoleLister.List(labels.Everything())
if err != nil {
utilruntime.HandleError(fmt.Errorf("Couldn't list all objects %v", err))
return
}
for _, clusterRole := range allClusterRoles {
// only queue ones that we may need to aggregate
if clusterRole.AggregationRule == nil {
continue
}
key, err := controller.KeyFunc(clusterRole)
if err != nil {
utilruntime.HandleError(fmt.Errorf("Couldn't get key for object %#v: %v", clusterRole, err))
return
}
c.queue.Add(key)
}
}
type byName []*rbacv1.ClusterRole
func (a byName) Len() int { return len(a) }
func (a byName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byName) Less(i, j int) bool { return a[i].Name < a[j].Name }
/*
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 clusterroleaggregation
import (
"testing"
rbacv1 "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
fakeclient "k8s.io/client-go/kubernetes/fake"
rbaclisters "k8s.io/client-go/listers/rbac/v1"
clienttesting "k8s.io/client-go/testing"
"k8s.io/client-go/tools/cache"
"k8s.io/kubernetes/pkg/controller"
)
func TestSyncClusterRole(t *testing.T) {
hammerRules := func() []rbacv1.PolicyRule {
return []rbacv1.PolicyRule{
{Verbs: []string{"hammer"}, Resources: []string{"nails"}},
{Verbs: []string{"hammer"}, Resources: []string{"wedges"}},
}
}
chiselRules := func() []rbacv1.PolicyRule {
return []rbacv1.PolicyRule{
{Verbs: []string{"chisel"}, Resources: []string{"mortises"}},
}
}
sawRules := func() []rbacv1.PolicyRule {
return []rbacv1.PolicyRule{
{Verbs: []string{"saw"}, Resources: []string{"boards"}},
}
}
role := func(name string, labels map[string]string, rules []rbacv1.PolicyRule) *rbacv1.ClusterRole {
return &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: name, Labels: labels},
Rules: rules,
}
}
combinedRole := func(selectors []map[string]string, rules ...[]rbacv1.PolicyRule) *rbacv1.ClusterRole {
ret := &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: "combined"},
AggregationRule: &rbacv1.AggregationRule{},
}
for _, selector := range selectors {
ret.AggregationRule.ClusterRoleSelectors = append(ret.AggregationRule.ClusterRoleSelectors,
metav1.LabelSelector{MatchLabels: selector})
}
for _, currRules := range rules {
ret.Rules = append(ret.Rules, currRules...)
}
return ret
}
tests := []struct {
name string
startingClusterRoles []*rbacv1.ClusterRole
clusterRoleToSync string
expectedClusterRole *rbacv1.ClusterRole
}{
{
name: "remove dead rules",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
combinedRole([]map[string]string{{"foo": "bar"}}, sawRules()),
},
clusterRoleToSync: "combined",
expectedClusterRole: combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
},
{
name: "strip rules",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "not-bar"}, hammerRules()),
combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
},
clusterRoleToSync: "combined",
expectedClusterRole: combinedRole([]map[string]string{{"foo": "bar"}}),
},
{
name: "select properly and put in order",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
role("saw", map[string]string{"foo": "not-bar"}, sawRules()),
combinedRole([]map[string]string{{"foo": "bar"}}),
},
clusterRoleToSync: "combined",
expectedClusterRole: combinedRole([]map[string]string{{"foo": "bar"}}, chiselRules(), hammerRules()),
},
{
name: "select properly with multiple selectors",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
role("saw", map[string]string{"foo": "not-bar"}, sawRules()),
combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}),
},
clusterRoleToSync: "combined",
expectedClusterRole: combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
},
{
name: "select properly remove duplicates",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
role("chisel", map[string]string{"foo": "bar"}, chiselRules()),
role("saw", map[string]string{"foo": "bar"}, sawRules()),
role("other-saw", map[string]string{"foo": "not-bar"}, sawRules()),
combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}),
},
clusterRoleToSync: "combined",
expectedClusterRole: combinedRole([]map[string]string{{"foo": "bar"}, {"foo": "not-bar"}}, chiselRules(), hammerRules(), sawRules()),
},
{
name: "no diff skip",
startingClusterRoles: []*rbacv1.ClusterRole{
role("hammer", map[string]string{"foo": "bar"}, hammerRules()),
combinedRole([]map[string]string{{"foo": "bar"}}, hammerRules()),
},
clusterRoleToSync: "combined",
expectedClusterRole: nil,
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
indexer := cache.NewIndexer(controller.KeyFunc, cache.Indexers{})
objs := []runtime.Object{}
for _, obj := range test.startingClusterRoles {
objs = append(objs, obj)
indexer.Add(obj)
}
fakeClient := fakeclient.NewSimpleClientset(objs...)
c := ClusterRoleAggregationController{
clusterRoleClient: fakeClient.RbacV1(),
clusterRoleLister: rbaclisters.NewClusterRoleLister(indexer),
}
err := c.syncClusterRole(test.clusterRoleToSync)
if err != nil {
t.Fatal(err)
}
if test.expectedClusterRole == nil {
if len(fakeClient.Actions()) != 0 {
t.Fatalf("unexpected actions %#v", fakeClient.Actions())
}
return
}
if len(fakeClient.Actions()) != 1 {
t.Fatalf("unexpected actions %#v", fakeClient.Actions())
}
action := fakeClient.Actions()[0]
if !action.Matches("update", "clusterroles") {
t.Fatalf("unexpected action %#v", action)
}
updateAction, ok := action.(clienttesting.UpdateAction)
if !ok {
t.Fatalf("unexpected action %#v", action)
}
if !equality.Semantic.DeepEqual(updateAction.GetObject().(*rbacv1.ClusterRole), test.expectedClusterRole) {
t.Fatalf("%v", diff.ObjectDiff(test.expectedClusterRole, updateAction.GetObject().(*rbacv1.ClusterRole)))
}
})
}
}
...@@ -18,7 +18,9 @@ limitations under the License. ...@@ -18,7 +18,9 @@ limitations under the License.
package policybased package policybased
import ( import (
"k8s.io/apimachinery/pkg/api/errors" "errors"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request" genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
...@@ -40,6 +42,11 @@ func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.Auth ...@@ -40,6 +42,11 @@ func NewStorage(s rest.StandardStorage, ruleResolver rbacregistryvalidation.Auth
return &Storage{s, ruleResolver} return &Storage{s, ruleResolver}
} }
var fullAuthority = []rbac.PolicyRule{
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
rbac.NewRule("*").URLs("*").RuleOrDie(),
}
func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) { func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, createValidatingAdmission rest.ValidateObjectFunc, includeUninitialized bool) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) { if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized) return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
...@@ -48,8 +55,15 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, crea ...@@ -48,8 +55,15 @@ func (s *Storage) Create(ctx genericapirequest.Context, obj runtime.Object, crea
clusterRole := obj.(*rbac.ClusterRole) clusterRole := obj.(*rbac.ClusterRole)
rules := clusterRole.Rules rules := clusterRole.Rules
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err) return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
}
// to set the aggregation rule, since it can gather anything, requires * on *.*
if hasAggregationRule(clusterRole) {
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, fullAuthority); err != nil {
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
}
} }
return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized) return s.StandardStorage.Create(ctx, obj, createValidatingAdmission, includeUninitialized)
} }
...@@ -60,6 +74,7 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up ...@@ -60,6 +74,7 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up
nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) { nonEscalatingInfo := rest.WrapUpdatedObjectInfo(obj, func(ctx genericapirequest.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
clusterRole := obj.(*rbac.ClusterRole) clusterRole := obj.(*rbac.ClusterRole)
oldClusterRole := oldObj.(*rbac.ClusterRole)
// if we're only mutating fields needed for the GC to eventually delete this obj, return // if we're only mutating fields needed for the GC to eventually delete this obj, return
if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) { if rbacregistry.IsOnlyMutatingGCFields(obj, oldObj, kapihelper.Semantic) {
...@@ -68,10 +83,21 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up ...@@ -68,10 +83,21 @@ func (s *Storage) Update(ctx genericapirequest.Context, name string, obj rest.Up
rules := clusterRole.Rules rules := clusterRole.Rules
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil { if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err) return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, err)
}
// to change the aggregation rule, since it can gather anything and prevent tightening, requires * on *.*
if hasAggregationRule(clusterRole) || hasAggregationRule(oldClusterRole) {
if err := rbacregistryvalidation.ConfirmNoEscalation(ctx, s.ruleResolver, fullAuthority); err != nil {
return nil, apierrors.NewForbidden(groupResource, clusterRole.Name, errors.New("must have cluster-admin privileges to use the aggregationRule"))
}
} }
return obj, nil return obj, nil
}) })
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation) return s.StandardStorage.Update(ctx, name, nonEscalatingInfo, createValidation, updateValidation)
} }
func hasAggregationRule(clusterRole *rbac.ClusterRole) bool {
return clusterRole.AggregationRule != nil && len(clusterRole.AggregationRule.ClusterRoleSelectors) > 0
}
...@@ -18,6 +18,7 @@ go_test( ...@@ -18,6 +18,7 @@ go_test(
"//pkg/apis/core/helper:go_default_library", "//pkg/apis/core/helper:go_default_library",
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
], ],
) )
...@@ -39,6 +40,7 @@ go_library( ...@@ -39,6 +40,7 @@ go_library(
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library", "//pkg/client/clientset_generated/internalclientset/typed/core/internalversion: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/registry/rbac/validation:go_default_library", "//pkg/registry/rbac/validation:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
......
...@@ -66,6 +66,14 @@ func (o ClusterRoleRuleOwner) SetRules(in []rbac.PolicyRule) { ...@@ -66,6 +66,14 @@ func (o ClusterRoleRuleOwner) SetRules(in []rbac.PolicyRule) {
o.ClusterRole.Rules = in o.ClusterRole.Rules = in
} }
func (o ClusterRoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
return o.ClusterRole.AggregationRule
}
func (o ClusterRoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
o.ClusterRole.AggregationRule = in
}
type ClusterRoleModifier struct { type ClusterRoleModifier struct {
Client internalversion.ClusterRoleInterface Client internalversion.ClusterRoleInterface
} }
......
...@@ -20,7 +20,9 @@ import ( ...@@ -20,7 +20,9 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/registry/rbac/validation" "k8s.io/kubernetes/pkg/registry/rbac/validation"
...@@ -51,6 +53,8 @@ type RuleOwner interface { ...@@ -51,6 +53,8 @@ type RuleOwner interface {
SetAnnotations(map[string]string) SetAnnotations(map[string]string)
GetRules() []rbac.PolicyRule GetRules() []rbac.PolicyRule
SetRules([]rbac.PolicyRule) SetRules([]rbac.PolicyRule)
GetAggregationRule() *rbac.AggregationRule
SetAggregationRule(*rbac.AggregationRule)
DeepCopyRuleOwner() RuleOwner DeepCopyRuleOwner() RuleOwner
} }
...@@ -75,6 +79,11 @@ type ReconcileClusterRoleResult struct { ...@@ -75,6 +79,11 @@ type ReconcileClusterRoleResult struct {
// ExtraRules contains extra permissions the currently persisted role had // ExtraRules contains extra permissions the currently persisted role had
ExtraRules []rbac.PolicyRule ExtraRules []rbac.PolicyRule
// MissingAggregationRuleSelectors contains expected selectors that were missing from the currently persisted role
MissingAggregationRuleSelectors []metav1.LabelSelector
// ExtraAggregationRuleSelectors contains extra selectors the currently persisted role had
ExtraAggregationRuleSelectors []metav1.LabelSelector
// Operation is the API operation required to reconcile. // Operation is the API operation required to reconcile.
// If no reconciliation was needed, it is set to ReconcileNone. // 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 options.Confirm == false, the reconcile was in dry-run mode, so the operation was not performed.
...@@ -101,10 +110,15 @@ func (o *ReconcileRoleOptions) run(attempts int) (*ReconcileClusterRoleResult, e ...@@ -101,10 +110,15 @@ func (o *ReconcileRoleOptions) run(attempts int) (*ReconcileClusterRoleResult, e
existing, err := o.Client.Get(o.Role.GetNamespace(), o.Role.GetName()) existing, err := o.Client.Get(o.Role.GetNamespace(), o.Role.GetName())
switch { switch {
case errors.IsNotFound(err): case errors.IsNotFound(err):
aggregationRule := o.Role.GetAggregationRule()
if aggregationRule == nil {
aggregationRule = &rbac.AggregationRule{}
}
result = &ReconcileClusterRoleResult{ result = &ReconcileClusterRoleResult{
Role: o.Role, Role: o.Role,
MissingRules: o.Role.GetRules(), MissingRules: o.Role.GetRules(),
Operation: ReconcileCreate, MissingAggregationRuleSelectors: aggregationRule.ClusterRoleSelectors,
Operation: ReconcileCreate,
} }
case err != nil: case err != nil:
...@@ -195,6 +209,26 @@ func computeReconciledRole(existing, expected RuleOwner, removeExtraPermissions ...@@ -195,6 +209,26 @@ func computeReconciledRole(existing, expected RuleOwner, removeExtraPermissions
result.Operation = ReconcileUpdate result.Operation = ReconcileUpdate
} }
// Compute extra and missing rules
_, result.ExtraAggregationRuleSelectors = aggregationRuleCovers(expected.GetAggregationRule(), existing.GetAggregationRule())
_, result.MissingAggregationRuleSelectors = aggregationRuleCovers(existing.GetAggregationRule(), expected.GetAggregationRule())
switch {
case !removeExtraPermissions && len(result.MissingAggregationRuleSelectors) > 0:
// add missing rules in the union case
aggregationRule := result.Role.GetAggregationRule()
if aggregationRule == nil {
aggregationRule = &rbac.AggregationRule{}
}
aggregationRule.ClusterRoleSelectors = append(aggregationRule.ClusterRoleSelectors, result.MissingAggregationRuleSelectors...)
result.Role.SetAggregationRule(aggregationRule)
result.Operation = ReconcileUpdate
case removeExtraPermissions && (len(result.MissingAggregationRuleSelectors) > 0 || len(result.ExtraAggregationRuleSelectors) > 0):
result.Role.SetAggregationRule(expected.GetAggregationRule())
result.Operation = ReconcileUpdate
}
return result, nil return result, nil
} }
...@@ -211,3 +245,37 @@ func merge(maps ...map[string]string) map[string]string { ...@@ -211,3 +245,37 @@ func merge(maps ...map[string]string) map[string]string {
} }
return output return output
} }
// aggregationRuleCovers determines whether or not the ownerSelectors cover the servantSelectors in terms of semantically
// equal label selectors.
// It returns whether or not the ownerSelectors cover and a list of the rules that the ownerSelectors do not cover.
func aggregationRuleCovers(ownerRule, servantRule *rbac.AggregationRule) (bool, []metav1.LabelSelector) {
switch {
case ownerRule == nil && servantRule == nil:
return true, []metav1.LabelSelector{}
case ownerRule == nil && servantRule != nil:
return false, servantRule.ClusterRoleSelectors
case ownerRule != nil && servantRule == nil:
return true, []metav1.LabelSelector{}
}
ownerSelectors := ownerRule.ClusterRoleSelectors
servantSelectors := servantRule.ClusterRoleSelectors
uncoveredSelectors := []metav1.LabelSelector{}
for _, servantSelector := range servantSelectors {
covered := false
for _, ownerSelector := range ownerSelectors {
if equality.Semantic.DeepEqual(ownerSelector, servantSelector) {
covered = true
break
}
}
if !covered {
uncoveredSelectors = append(uncoveredSelectors, servantSelector)
}
}
return (len(uncoveredSelectors) == 0), uncoveredSelectors
}
...@@ -20,12 +20,16 @@ import ( ...@@ -20,12 +20,16 @@ import (
"testing" "testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/kubernetes/pkg/apis/core/helper" "k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
) )
func role(rules []rbac.PolicyRule, labels map[string]string, annotations map[string]string) *rbac.ClusterRole { 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}} return &rbac.ClusterRole{
Rules: rules,
ObjectMeta: metav1.ObjectMeta{Labels: labels, Annotations: annotations},
}
} }
func rules(resources ...string) []rbac.PolicyRule { func rules(resources ...string) []rbac.PolicyRule {
...@@ -38,7 +42,7 @@ func rules(resources ...string) []rbac.PolicyRule { ...@@ -38,7 +42,7 @@ func rules(resources ...string) []rbac.PolicyRule {
type ss map[string]string type ss map[string]string
func TestComputeReconciledRole(t *testing.T) { func TestComputeReconciledRoleRules(t *testing.T) {
tests := map[string]struct { tests := map[string]struct {
expectedRole *rbac.ClusterRole expectedRole *rbac.ClusterRole
actualRole *rbac.ClusterRole actualRole *rbac.ClusterRole
...@@ -273,3 +277,96 @@ func TestComputeReconciledRole(t *testing.T) { ...@@ -273,3 +277,96 @@ func TestComputeReconciledRole(t *testing.T) {
} }
} }
} }
func aggregatedRole(aggregationRule *rbac.AggregationRule) *rbac.ClusterRole {
return &rbac.ClusterRole{
AggregationRule: aggregationRule,
}
}
func aggregationrule(selectors []map[string]string) *rbac.AggregationRule {
ret := &rbac.AggregationRule{}
for _, selector := range selectors {
ret.ClusterRoleSelectors = append(ret.ClusterRoleSelectors,
metav1.LabelSelector{MatchLabels: selector})
}
return ret
}
func TestComputeReconciledRoleAggregationRules(t *testing.T) {
tests := map[string]struct {
expectedRole *rbac.ClusterRole
actualRole *rbac.ClusterRole
removeExtraPermissions bool
expectedReconciledRole *rbac.ClusterRole
expectedReconciliationNeeded bool
}{
"empty": {
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
actualRole: aggregatedRole(nil),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"empty-2": {
expectedRole: aggregatedRole(&rbac.AggregationRule{}),
actualRole: aggregatedRole(&rbac.AggregationRule{}),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"match without union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
removeExtraPermissions: true,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"match with union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
removeExtraPermissions: false,
expectedReconciledRole: nil,
expectedReconciliationNeeded: false,
},
"different rules without union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
removeExtraPermissions: true,
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
expectedReconciliationNeeded: true,
},
"different rules with union": {
expectedRole: aggregatedRole(aggregationrule([]map[string]string{{"foo": "bar"}})),
actualRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}})),
removeExtraPermissions: false,
expectedReconciledRole: aggregatedRole(aggregationrule([]map[string]string{{"alpha": "bravo"}, {"foo": "bar"}})),
expectedReconciliationNeeded: true,
},
}
for k, tc := range tests {
actualRole := ClusterRoleRuleOwner{ClusterRole: tc.actualRole}
expectedRole := ClusterRoleRuleOwner{ClusterRole: tc.expectedRole}
result, err := computeReconciledRole(actualRole, 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 && !helper.Semantic.DeepEqual(result.Role.(ClusterRoleRuleOwner).ClusterRole, tc.expectedReconciledRole) {
t.Errorf("%s: %v", k, diff.ObjectDiff(tc.expectedReconciledRole, result.Role.(ClusterRoleRuleOwner).ClusterRole))
}
}
}
...@@ -69,6 +69,13 @@ func (o RoleRuleOwner) SetRules(in []rbac.PolicyRule) { ...@@ -69,6 +69,13 @@ func (o RoleRuleOwner) SetRules(in []rbac.PolicyRule) {
o.Role.Rules = in o.Role.Rules = in
} }
func (o RoleRuleOwner) GetAggregationRule() *rbac.AggregationRule {
return nil
}
func (o RoleRuleOwner) SetAggregationRule(in *rbac.AggregationRule) {
}
type RoleModifier struct { type RoleModifier struct {
Client internalversion.RolesGetter Client internalversion.RolesGetter
NamespaceClient core.NamespaceInterface NamespaceClient core.NamespaceInterface
......
...@@ -33,6 +33,7 @@ go_library( ...@@ -33,6 +33,7 @@ go_library(
"//vendor/k8s.io/api/rbac/v1:go_default_library", "//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/api/rbac/v1alpha1:go_default_library", "//vendor/k8s.io/api/rbac/v1alpha1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library", "//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
rbacapiv1 "k8s.io/api/rbac/v1" rbacapiv1 "k8s.io/api/rbac/v1"
rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1"
rbacapiv1beta1 "k8s.io/api/rbac/v1beta1" rbacapiv1beta1 "k8s.io/api/rbac/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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"
...@@ -134,10 +135,11 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon ...@@ -134,10 +135,11 @@ func (p RESTStorageProvider) storage(version schema.GroupVersion, apiResourceCon
func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) { func (p RESTStorageProvider) PostStartHook() (string, genericapiserver.PostStartHookFunc, error) {
policy := &PolicyData{ policy := &PolicyData{
ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...), ClusterRoles: append(bootstrappolicy.ClusterRoles(), bootstrappolicy.ControllerRoles()...),
ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...), ClusterRoleBindings: append(bootstrappolicy.ClusterRoleBindings(), bootstrappolicy.ControllerRoleBindings()...),
Roles: bootstrappolicy.NamespaceRoles(), Roles: bootstrappolicy.NamespaceRoles(),
RoleBindings: bootstrappolicy.NamespaceRoleBindings(), RoleBindings: bootstrappolicy.NamespaceRoleBindings(),
ClusterRolesToAggregate: bootstrappolicy.ClusterRolesToAggregate(),
} }
return PostStartHookName, policy.EnsureRBACPolicy(), nil return PostStartHookName, policy.EnsureRBACPolicy(), nil
} }
...@@ -147,6 +149,8 @@ type PolicyData struct { ...@@ -147,6 +149,8 @@ type PolicyData struct {
ClusterRoleBindings []rbac.ClusterRoleBinding ClusterRoleBindings []rbac.ClusterRoleBinding
Roles map[string][]rbac.Role Roles map[string][]rbac.Role
RoleBindings map[string][]rbac.RoleBinding RoleBindings map[string][]rbac.RoleBinding
// ClusterRolesToAggregate maps from previous clusterrole name to the new clusterrole name
ClusterRolesToAggregate map[string]string
} }
func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
...@@ -176,6 +180,13 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -176,6 +180,13 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
return false, nil return false, nil
} }
// if the new cluster roles to aggregate do not yet exist, then we need to copy the old roles if they don't exist
// in new locations
if err := primeAggregatedClusterRoles(p.ClusterRolesToAggregate, clientset); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to prime aggregated clusterroles: %v", err))
return false, nil
}
// ensure bootstrap roles are created or reconciled // ensure bootstrap roles are created or reconciled
for _, clusterRole := range p.ClusterRoles { for _, clusterRole := range p.ClusterRoles {
opts := reconciliation.ReconcileRoleOptions{ opts := reconciliation.ReconcileRoleOptions{
...@@ -310,3 +321,32 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc { ...@@ -310,3 +321,32 @@ func (p *PolicyData) EnsureRBACPolicy() genericapiserver.PostStartHookFunc {
func (p RESTStorageProvider) GroupName() string { func (p RESTStorageProvider) GroupName() string {
return rbac.GroupName return rbac.GroupName
} }
// primeAggregatedClusterRoles copies roles that have transitioned to aggregated roles and may need to pick up changes
// that were done to the legacy roles.
func primeAggregatedClusterRoles(clusterRolesToAggregate map[string]string, clusterRoleClient rbacclient.ClusterRolesGetter) error {
for oldName, newName := range clusterRolesToAggregate {
_, err := clusterRoleClient.ClusterRoles().Get(newName, metav1.GetOptions{})
if err == nil {
continue
}
if !apierrors.IsNotFound(err) {
return err
}
existingRole, err := clusterRoleClient.ClusterRoles().Get(oldName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
continue
}
if err != nil {
return err
}
glog.V(1).Infof("migrating %v to %v", existingRole.Name, newName)
existingRole.Name = newName
if _, err := clusterRoleClient.ClusterRoles().Create(existingRole); err != nil && !apierrors.IsAlreadyExists(err) {
return err
}
}
return nil
}
...@@ -69,6 +69,14 @@ func buildControllerRoles() ([]rbac.ClusterRole, []rbac.ClusterRoleBinding) { ...@@ -69,6 +69,14 @@ func buildControllerRoles() ([]rbac.ClusterRole, []rbac.ClusterRoleBinding) {
}, },
}) })
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{ addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "clusterrole-aggregation-controller"},
Rules: []rbac.PolicyRule{
// this controller must have full permissions to allow it to mutate any role in any way
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
rbac.NewRule("*").URLs("*").RuleOrDie(),
},
})
addControllerRole(&controllerRoles, &controllerRoleBindings, rbac.ClusterRole{
ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cronjob-controller"}, ObjectMeta: metav1.ObjectMeta{Name: saRolePrefix + "cronjob-controller"},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("cronjobs").RuleOrDie(), rbac.NewRule("get", "list", "watch", "update").Groups(batchGroup).Resources("cronjobs").RuleOrDie(),
......
...@@ -32,6 +32,7 @@ var rolesWithAllowStar = sets.NewString( ...@@ -32,6 +32,7 @@ var rolesWithAllowStar = sets.NewString(
saRolePrefix+"generic-garbage-collector", saRolePrefix+"generic-garbage-collector",
saRolePrefix+"resourcequota-controller", saRolePrefix+"resourcequota-controller",
saRolePrefix+"horizontal-pod-autoscaler", saRolePrefix+"horizontal-pod-autoscaler",
saRolePrefix+"clusterrole-aggregation-controller",
) )
// TestNoStarsForControllers confirms that no controller role has star verbs, groups, // TestNoStarsForControllers confirms that no controller role has star verbs, groups,
......
...@@ -176,6 +176,30 @@ func ClusterRoles() []rbac.ClusterRole { ...@@ -176,6 +176,30 @@ func ClusterRoles() []rbac.ClusterRole {
{ {
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users. // a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
ObjectMeta: metav1.ObjectMeta{Name: "admin"}, ObjectMeta: metav1.ObjectMeta{Name: "admin"},
AggregationRule: &rbac.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}}},
},
},
{
// a role for a namespace level editor. It grants access to all user level actions in a namespace.
// It does not grant powers for "privileged" resources which are domain of the system: `/status`
// subresources or `quota`/`limits` which are used to control namespaces
ObjectMeta: metav1.ObjectMeta{Name: "edit"},
AggregationRule: &rbac.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}}},
},
},
{
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in
// a namespace.
ObjectMeta: metav1.ObjectMeta{Name: "view"},
AggregationRule: &rbac.AggregationRule{
ClusterRoleSelectors: []metav1.LabelSelector{{MatchLabels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}}},
},
},
{
// a role for a namespace level admin. It is `edit` plus the power to grant permissions to other users.
ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-admin", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-admin": "true"}},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
...@@ -211,7 +235,7 @@ func ClusterRoles() []rbac.ClusterRole { ...@@ -211,7 +235,7 @@ func ClusterRoles() []rbac.ClusterRole {
// a role for a namespace level editor. It grants access to all user level actions in a namespace. // a role for a namespace level editor. It grants access to all user level actions in a namespace.
// It does not grant powers for "privileged" resources which are domain of the system: `/status` // It does not grant powers for "privileged" resources which are domain of the system: `/status`
// subresources or `quota`/`limits` which are used to control namespaces // subresources or `quota`/`limits` which are used to control namespaces
ObjectMeta: metav1.ObjectMeta{Name: "edit"}, ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-edit", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-edit": "true"}},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(), rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("pods", "pods/attach", "pods/proxy", "pods/exec", "pods/portforward").RuleOrDie(),
rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(ReadWrite...).Groups(legacyGroup).Resources("replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
...@@ -242,7 +266,7 @@ func ClusterRoles() []rbac.ClusterRole { ...@@ -242,7 +266,7 @@ func ClusterRoles() []rbac.ClusterRole {
{ {
// a role for namespace level viewing. It grants Read-only access to non-escalating resources in // a role for namespace level viewing. It grants Read-only access to non-escalating resources in
// a namespace. // a namespace.
ObjectMeta: metav1.ObjectMeta{Name: "view"}, ObjectMeta: metav1.ObjectMeta{Name: "system:aggregate-to-view", Labels: map[string]string{"rbac.authorization.k8s.io/aggregate-to-view": "true"}},
Rules: []rbac.PolicyRule{ Rules: []rbac.PolicyRule{
rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts", rbac.NewRule(Read...).Groups(legacyGroup).Resources("pods", "replicationcontrollers", "replicationcontrollers/scale", "serviceaccounts",
"services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(), "services", "endpoints", "persistentvolumeclaims", "configmaps").RuleOrDie(),
...@@ -444,3 +468,11 @@ func ClusterRoleBindings() []rbac.ClusterRoleBinding { ...@@ -444,3 +468,11 @@ func ClusterRoleBindings() []rbac.ClusterRoleBinding {
return rolebindings return rolebindings
} }
func ClusterRolesToAggregate() map[string]string {
return map[string]string{
"admin": "system:aggregate-to-admin",
"edit": "system:aggregate-to-edit",
"view": "system:aggregate-to-view",
}
}
...@@ -53,11 +53,11 @@ func getSemanticRoles(roles []rbac.ClusterRole) semanticRoles { ...@@ -53,11 +53,11 @@ func getSemanticRoles(roles []rbac.ClusterRole) semanticRoles {
for i := range roles { for i := range roles {
role := roles[i] role := roles[i]
switch role.Name { switch role.Name {
case "admin": case "system:aggregate-to-admin":
ret.admin = &role ret.admin = &role
case "edit": case "system:aggregate-to-edit":
ret.edit = &role ret.edit = &role
case "view": case "system:aggregate-to-view":
ret.view = &role ret.view = &role
} }
} }
...@@ -319,8 +319,9 @@ func TestClusterRoleLabel(t *testing.T) { ...@@ -319,8 +319,9 @@ func TestClusterRoleLabel(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if got, want := accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"}; !reflect.DeepEqual(got, want) {
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), got, want) if accessor.GetLabels()["kubernetes.io/bootstrapping"] != "rbac-defaults" {
t.Errorf("ClusterRole: %s GetLabels() = %s, want %s", accessor.GetName(), accessor.GetLabels(), map[string]string{"kubernetes.io/bootstrapping": "rbac-defaults"})
} }
} }
......
apiVersion: v1 apiVersion: v1
items: items:
- apiVersion: rbac.authorization.k8s.io/v1 - aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-admin: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
annotations: annotations:
...@@ -9,6 +13,51 @@ items: ...@@ -9,6 +13,51 @@ items:
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: admin name: admin
rules: null
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-edit: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: edit
rules: null
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
rbac.authorization.k8s.io/aggregate-to-admin: "true"
name: system:aggregate-to-admin
rules: rules:
- apiGroups: - apiGroups:
- "" - ""
...@@ -185,27 +234,8 @@ items: ...@@ -185,27 +234,8 @@ items:
creationTimestamp: null creationTimestamp: null
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin rbac.authorization.k8s.io/aggregate-to-edit: "true"
rules: name: system:aggregate-to-edit
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: edit
rules: rules:
- apiGroups: - apiGroups:
- "" - ""
...@@ -362,6 +392,108 @@ items: ...@@ -362,6 +392,108 @@ items:
creationTimestamp: null creationTimestamp: null
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
rbac.authorization.k8s.io/aggregate-to-view: "true"
name: system:aggregate-to-view
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- pods
- replicationcontrollers
- replicationcontrollers/scale
- serviceaccounts
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- bindings
- events
- limitranges
- namespaces/status
- pods/log
- pods/status
- replicationcontrollers/status
- resourcequotas
- resourcequotas/status
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
verbs:
- get
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/scale
- ingresses
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- get
- list
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- get
- list
- watch
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:auth-delegator name: system:auth-delegator
rules: rules:
- apiGroups: - apiGroups:
...@@ -935,7 +1067,11 @@ items: ...@@ -935,7 +1067,11 @@ items:
- create - create
- patch - patch
- update - update
- apiVersion: rbac.authorization.k8s.io/v1 - aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.authorization.k8s.io/aggregate-to-view: "true"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: ClusterRole
metadata: metadata:
annotations: annotations:
...@@ -944,97 +1080,6 @@ items: ...@@ -944,97 +1080,6 @@ items:
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: view name: view
rules: rules: null
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- persistentvolumeclaims
- pods
- replicationcontrollers
- replicationcontrollers/scale
- serviceaccounts
- services
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- bindings
- events
- limitranges
- namespaces/status
- pods/log
- pods/status
- replicationcontrollers/status
- resourcequotas
- resourcequotas/status
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- namespaces
verbs:
- get
- list
- watch
- apiGroups:
- apps
resources:
- daemonsets
- deployments
- deployments/scale
- replicasets
- replicasets/scale
- statefulsets
verbs:
- get
- list
- watch
- apiGroups:
- autoscaling
resources:
- horizontalpodautoscalers
verbs:
- get
- list
- watch
- apiGroups:
- batch
resources:
- cronjobs
- jobs
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- daemonsets
- deployments
- deployments/scale
- ingresses
- replicasets
- replicasets/scale
- replicationcontrollers/scale
verbs:
- get
- list
- watch
- apiGroups:
- policy
resources:
- poddisruptionbudgets
verbs:
- get
- list
- watch
kind: List kind: List
metadata: {} metadata: {}
...@@ -42,6 +42,23 @@ items: ...@@ -42,6 +42,23 @@ items:
creationTimestamp: null creationTimestamp: null
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:clusterrole-aggregation-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:controller:clusterrole-aggregation-controller
subjects:
- kind: ServiceAccount
name: clusterrole-aggregation-controller
namespace: kube-system
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:cronjob-controller name: system:controller:cronjob-controller
roleRef: roleRef:
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
......
...@@ -95,6 +95,26 @@ items: ...@@ -95,6 +95,26 @@ items:
creationTimestamp: null creationTimestamp: null
labels: labels:
kubernetes.io/bootstrapping: rbac-defaults kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:clusterrole-aggregation-controller
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
- apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: null
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:controller:cronjob-controller name: system:controller:cronjob-controller
rules: rules:
- apiGroups: - apiGroups:
......
...@@ -21,6 +21,7 @@ syntax = 'proto2'; ...@@ -21,6 +21,7 @@ syntax = 'proto2';
package k8s.io.api.rbac.v1; package k8s.io.api.rbac.v1;
import "k8s.io/api/rbac/v1alpha1/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
...@@ -29,6 +30,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto"; ...@@ -29,6 +30,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated". // Package-wide variables from generator "generated".
option go_package = "v1"; option go_package = "v1";
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
message AggregationRule {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector clusterRoleSelectors = 1;
}
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. // ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
message ClusterRole { message ClusterRole {
// Standard object's metadata. // Standard object's metadata.
...@@ -37,6 +46,12 @@ message ClusterRole { ...@@ -37,6 +46,12 @@ message ClusterRole {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
repeated PolicyRule rules = 2; repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
optional AggregationRule aggregationRule = 3;
} }
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, // ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
......
...@@ -170,6 +170,20 @@ type ClusterRole struct { ...@@ -170,6 +170,20 @@ type ClusterRole struct {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"`
}
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
type AggregationRule struct {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
ClusterRoleSelectors []metav1.LabelSelector `json:"clusterRoleSelectors,omitempty" protobuf:"bytes,1,rep,name=clusterRoleSelectors"`
} }
// +genclient // +genclient
......
...@@ -27,10 +27,20 @@ package v1 ...@@ -27,10 +27,20 @@ package v1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE // AUTO-GENERATED FUNCTIONS START HERE
var map_AggregationRule = map[string]string{
"": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"clusterRoleSelectors": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
}
func (AggregationRule) SwaggerDoc() map[string]string {
return map_AggregationRule
}
var map_ClusterRole = map[string]string{ var map_ClusterRole = map[string]string{
"": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"metadata": "Standard object's metadata.", "metadata": "Standard object's metadata.",
"rules": "Rules holds all the PolicyRules for this ClusterRole", "rules": "Rules holds all the PolicyRules for this ClusterRole",
"aggregationRule": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
} }
func (ClusterRole) SwaggerDoc() map[string]string { func (ClusterRole) SwaggerDoc() map[string]string {
......
...@@ -21,10 +21,34 @@ limitations under the License. ...@@ -21,10 +21,34 @@ limitations under the License.
package v1 package v1
import ( import (
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AggregationRule) DeepCopyInto(out *AggregationRule) {
*out = *in
if in.ClusterRoleSelectors != nil {
in, out := &in.ClusterRoleSelectors, &out.ClusterRoleSelectors
*out = make([]meta_v1.LabelSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule.
func (in *AggregationRule) DeepCopy() *AggregationRule {
if in == nil {
return nil
}
out := new(AggregationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { ...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.AggregationRule != nil {
in, out := &in.AggregationRule, &out.AggregationRule
if *in == nil {
*out = nil
} else {
*out = new(AggregationRule)
(*in).DeepCopyInto(*out)
}
}
return return
} }
......
...@@ -29,6 +29,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto"; ...@@ -29,6 +29,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated". // Package-wide variables from generator "generated".
option go_package = "v1alpha1"; option go_package = "v1alpha1";
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
message AggregationRule {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector clusterRoleSelectors = 1;
}
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. // ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
message ClusterRole { message ClusterRole {
// Standard object's metadata. // Standard object's metadata.
...@@ -37,6 +45,12 @@ message ClusterRole { ...@@ -37,6 +45,12 @@ message ClusterRole {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
repeated PolicyRule rules = 2; repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
optional AggregationRule aggregationRule = 3;
} }
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, // ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
......
...@@ -172,6 +172,20 @@ type ClusterRole struct { ...@@ -172,6 +172,20 @@ type ClusterRole struct {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"`
}
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
type AggregationRule struct {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
ClusterRoleSelectors []metav1.LabelSelector `json:"clusterRoleSelectors,omitempty" protobuf:"bytes,1,rep,name=clusterRoleSelectors"`
} }
// +genclient // +genclient
......
...@@ -27,10 +27,20 @@ package v1alpha1 ...@@ -27,10 +27,20 @@ package v1alpha1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE // AUTO-GENERATED FUNCTIONS START HERE
var map_AggregationRule = map[string]string{
"": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"clusterRoleSelectors": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
}
func (AggregationRule) SwaggerDoc() map[string]string {
return map_AggregationRule
}
var map_ClusterRole = map[string]string{ var map_ClusterRole = map[string]string{
"": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"metadata": "Standard object's metadata.", "metadata": "Standard object's metadata.",
"rules": "Rules holds all the PolicyRules for this ClusterRole", "rules": "Rules holds all the PolicyRules for this ClusterRole",
"aggregationRule": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
} }
func (ClusterRole) SwaggerDoc() map[string]string { func (ClusterRole) SwaggerDoc() map[string]string {
......
...@@ -21,10 +21,34 @@ limitations under the License. ...@@ -21,10 +21,34 @@ limitations under the License.
package v1alpha1 package v1alpha1
import ( import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AggregationRule) DeepCopyInto(out *AggregationRule) {
*out = *in
if in.ClusterRoleSelectors != nil {
in, out := &in.ClusterRoleSelectors, &out.ClusterRoleSelectors
*out = make([]v1.LabelSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule.
func (in *AggregationRule) DeepCopy() *AggregationRule {
if in == nil {
return nil
}
out := new(AggregationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { ...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.AggregationRule != nil {
in, out := &in.AggregationRule, &out.AggregationRule
if *in == nil {
*out = nil
} else {
*out = new(AggregationRule)
(*in).DeepCopyInto(*out)
}
}
return return
} }
......
...@@ -21,6 +21,7 @@ syntax = 'proto2'; ...@@ -21,6 +21,7 @@ syntax = 'proto2';
package k8s.io.api.rbac.v1beta1; package k8s.io.api.rbac.v1beta1;
import "k8s.io/api/rbac/v1alpha1/generated.proto";
import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto"; import "k8s.io/apimachinery/pkg/apis/meta/v1/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto"; import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
...@@ -29,6 +30,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto"; ...@@ -29,6 +30,14 @@ import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated". // Package-wide variables from generator "generated".
option go_package = "v1beta1"; option go_package = "v1beta1";
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
message AggregationRule {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
repeated k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector clusterRoleSelectors = 1;
}
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. // ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
message ClusterRole { message ClusterRole {
// Standard object's metadata. // Standard object's metadata.
...@@ -37,6 +46,12 @@ message ClusterRole { ...@@ -37,6 +46,12 @@ message ClusterRole {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
repeated PolicyRule rules = 2; repeated PolicyRule rules = 2;
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
optional AggregationRule aggregationRule = 3;
} }
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace, // ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
......
...@@ -171,6 +171,19 @@ type ClusterRole struct { ...@@ -171,6 +171,19 @@ type ClusterRole struct {
// Rules holds all the PolicyRules for this ClusterRole // Rules holds all the PolicyRules for this ClusterRole
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
// AggregationRule is an optional field that describes how to build the Rules for this ClusterRole.
// If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be
// stomped by the controller.
// +optional
AggregationRule *AggregationRule `json:"aggregationRule,omitempty" protobuf:"bytes,3,opt,name=aggregationRule"`
}
// AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole
type AggregationRule struct {
// ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.
// If any of the selectors match, then the ClusterRole's permissions will be added
// +optional
ClusterRoleSelectors []metav1.LabelSelector `json:"clusterRoleSelectors,omitempty" protobuf:"bytes,1,rep,name=clusterRoleSelectors"`
} }
// +genclient // +genclient
......
...@@ -27,10 +27,20 @@ package v1beta1 ...@@ -27,10 +27,20 @@ package v1beta1
// Those methods can be generated by using hack/update-generated-swagger-docs.sh // Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE // AUTO-GENERATED FUNCTIONS START HERE
var map_AggregationRule = map[string]string{
"": "AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole",
"clusterRoleSelectors": "ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules. If any of the selectors match, then the ClusterRole's permissions will be added",
}
func (AggregationRule) SwaggerDoc() map[string]string {
return map_AggregationRule
}
var map_ClusterRole = map[string]string{ var map_ClusterRole = map[string]string{
"": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.", "": "ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.",
"metadata": "Standard object's metadata.", "metadata": "Standard object's metadata.",
"rules": "Rules holds all the PolicyRules for this ClusterRole", "rules": "Rules holds all the PolicyRules for this ClusterRole",
"aggregationRule": "AggregationRule is an optional field that describes how to build the Rules for this ClusterRole. If AggregationRule is set, then the Rules are controller managed and direct changes to Rules will be stomped by the controller.",
} }
func (ClusterRole) SwaggerDoc() map[string]string { func (ClusterRole) SwaggerDoc() map[string]string {
......
...@@ -21,10 +21,34 @@ limitations under the License. ...@@ -21,10 +21,34 @@ limitations under the License.
package v1beta1 package v1beta1
import ( import (
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *AggregationRule) DeepCopyInto(out *AggregationRule) {
*out = *in
if in.ClusterRoleSelectors != nil {
in, out := &in.ClusterRoleSelectors, &out.ClusterRoleSelectors
*out = make([]v1.LabelSelector, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AggregationRule.
func (in *AggregationRule) DeepCopy() *AggregationRule {
if in == nil {
return nil
}
out := new(AggregationRule)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
*out = *in *out = *in
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) { ...@@ -36,6 +60,15 @@ func (in *ClusterRole) DeepCopyInto(out *ClusterRole) {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.AggregationRule != nil {
in, out := &in.AggregationRule, &out.AggregationRule
if *in == nil {
*out = nil
} else {
*out = new(AggregationRule)
(*in).DeepCopyInto(*out)
}
}
return return
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment