Merge pull request #25634 from ericchiang/rbac_api_group
Add RBAC authorization API group and authorizer
This commit is contained in:
110
api/swagger-spec/rbac.authorization.k8s.io.json
Normal file
110
api/swagger-spec/rbac.authorization.k8s.io.json
Normal file
@@ -0,0 +1,110 @@
|
||||
{
|
||||
"swaggerVersion": "1.2",
|
||||
"apiVersion": "",
|
||||
"basePath": "https://10.10.10.10:443",
|
||||
"resourcePath": "/apis/rbac.authorization.k8s.io",
|
||||
"apis": [
|
||||
{
|
||||
"path": "/apis/rbac.authorization.k8s.io",
|
||||
"description": "get information of a group",
|
||||
"operations": [
|
||||
{
|
||||
"type": "unversioned.APIGroup",
|
||||
"method": "GET",
|
||||
"summary": "get information of a group",
|
||||
"nickname": "getAPIGroup",
|
||||
"parameters": [],
|
||||
"produces": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json",
|
||||
"application/yaml",
|
||||
"application/vnd.kubernetes.protobuf"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"models": {
|
||||
"unversioned.APIGroup": {
|
||||
"id": "unversioned.APIGroup",
|
||||
"description": "APIGroup contains the name, the supported versions, and the preferred version of a group.",
|
||||
"required": [
|
||||
"name",
|
||||
"versions",
|
||||
"serverAddressByClientCIDRs"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds"
|
||||
},
|
||||
"apiVersion": {
|
||||
"type": "string",
|
||||
"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: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources"
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "name is the name of the group."
|
||||
},
|
||||
"versions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "unversioned.GroupVersionForDiscovery"
|
||||
},
|
||||
"description": "versions are the versions supported in this group."
|
||||
},
|
||||
"preferredVersion": {
|
||||
"$ref": "unversioned.GroupVersionForDiscovery",
|
||||
"description": "preferredVersion is the version preferred by the API server, which probably is the storage version."
|
||||
},
|
||||
"serverAddressByClientCIDRs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "unversioned.ServerAddressByClientCIDR"
|
||||
},
|
||||
"description": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP."
|
||||
}
|
||||
}
|
||||
},
|
||||
"unversioned.GroupVersionForDiscovery": {
|
||||
"id": "unversioned.GroupVersionForDiscovery",
|
||||
"description": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensiblity.",
|
||||
"required": [
|
||||
"groupVersion",
|
||||
"version"
|
||||
],
|
||||
"properties": {
|
||||
"groupVersion": {
|
||||
"type": "string",
|
||||
"description": "groupVersion specifies the API group and version in the form \"group/version\""
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion."
|
||||
}
|
||||
}
|
||||
},
|
||||
"unversioned.ServerAddressByClientCIDR": {
|
||||
"id": "unversioned.ServerAddressByClientCIDR",
|
||||
"description": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.",
|
||||
"required": [
|
||||
"clientCIDR",
|
||||
"serverAddress"
|
||||
],
|
||||
"properties": {
|
||||
"clientCIDR": {
|
||||
"type": "string",
|
||||
"description": "The CIDR with which clients can match their IP to figure out the server address that they should use."
|
||||
},
|
||||
"serverAddress": {
|
||||
"type": "string",
|
||||
"description": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
3289
api/swagger-spec/rbac.authorization.k8s.io_v1alpha1.json
Normal file
3289
api/swagger-spec/rbac.authorization.k8s.io_v1alpha1.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -60,6 +60,14 @@
|
||||
{
|
||||
"path": "/apis/apps",
|
||||
"description": "get information of a group"
|
||||
},
|
||||
{
|
||||
"path": "/apis/rbac.authorization.k8s.io/v1alpha1",
|
||||
"description": "API at /apis/rbac.authorization.k8s.io/v1alpha1"
|
||||
},
|
||||
{
|
||||
"path": "/apis/rbac.authorization.k8s.io",
|
||||
"description": "get information of a group"
|
||||
}
|
||||
],
|
||||
"apiVersion": "",
|
||||
|
@@ -46,6 +46,15 @@ import (
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/master"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
|
||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||
)
|
||||
|
||||
@@ -198,6 +207,32 @@ func Run(s *options.APIServer) error {
|
||||
}
|
||||
|
||||
authorizationModeNames := strings.Split(s.AuthorizationMode, ",")
|
||||
|
||||
modeEnabled := func(mode string) bool {
|
||||
for _, m := range authorizationModeNames {
|
||||
if m == mode {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if modeEnabled(apiserver.ModeRBAC) {
|
||||
mustGetRESTOptions := func(resource string) generic.RESTOptions {
|
||||
s, err := storageFactory.New(api.Resource(resource))
|
||||
if err != nil {
|
||||
glog.Fatalf("Unable to get %s storage: %v", resource, err)
|
||||
}
|
||||
return generic.RESTOptions{Storage: s, Decorator: generic.UndecoratedStorage}
|
||||
}
|
||||
|
||||
// For initial bootstrapping go directly to etcd to avoid privillege escalation check.
|
||||
s.AuthorizationConfig.RBACRoleRegistry = role.NewRegistry(roleetcd.NewREST(mustGetRESTOptions("roles")))
|
||||
s.AuthorizationConfig.RBACRoleBindingRegistry = rolebinding.NewRegistry(rolebindingetcd.NewREST(mustGetRESTOptions("rolebindings")))
|
||||
s.AuthorizationConfig.RBACClusterRoleRegistry = clusterrole.NewRegistry(clusterroleetcd.NewREST(mustGetRESTOptions("clusterroles")))
|
||||
s.AuthorizationConfig.RBACClusterRoleBindingRegistry = clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(mustGetRESTOptions("clusterrolebindings")))
|
||||
}
|
||||
|
||||
authorizer, err := apiserver.NewAuthorizerFromAuthorizationConfig(authorizationModeNames, s.AuthorizationConfig)
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid Authorization Config: %v", err)
|
||||
@@ -216,6 +251,7 @@ func Run(s *options.APIServer) error {
|
||||
genericConfig.Authenticator = authenticator
|
||||
genericConfig.SupportsBasicAuth = len(s.BasicAuthFile) > 0
|
||||
genericConfig.Authorizer = authorizer
|
||||
genericConfig.AuthorizerRBACSuperUser = s.AuthorizationConfig.RBACSuperUser
|
||||
genericConfig.AdmissionControl = admissionController
|
||||
genericConfig.APIResourceConfigSource = storageFactory.APIResourceConfigSource
|
||||
genericConfig.MasterServiceNamespace = s.MasterServiceNamespace
|
||||
|
@@ -63,7 +63,7 @@ func generatedBy(customArgs clientgenargs.Args) string {
|
||||
return fmt.Sprintf("\n// This package is generated by client-gen with the default arguments.\n\n")
|
||||
}
|
||||
|
||||
func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package {
|
||||
func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, inputPath string, boilerplate []byte, generatedBy string) generator.Package {
|
||||
outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version)
|
||||
return &generator.DefaultPackage{
|
||||
PackageName: gv.Version,
|
||||
@@ -99,6 +99,7 @@ func packageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
|
||||
OptionalName: normalization.BeforeFirstDot(gv.Group) + "_client",
|
||||
},
|
||||
outputPackage: outputPackagePath,
|
||||
inputPacakge: inputPath,
|
||||
group: gv.Group,
|
||||
version: gv.Version,
|
||||
types: typeList,
|
||||
@@ -216,9 +217,10 @@ func Packages(context *generator.Context, arguments *args.GeneratorArgs) generat
|
||||
orderer := namer.Orderer{Namer: namer.NewPrivateNamer(0)}
|
||||
for _, gv := range customArgs.GroupVersions {
|
||||
types := gvToTypes[gv]
|
||||
packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate, generatedBy))
|
||||
inputPath := customArgs.GroupVersionToInputPath[gv]
|
||||
packageList = append(packageList, packageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy))
|
||||
if customArgs.FakeClient {
|
||||
packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, boilerplate, generatedBy))
|
||||
packageList = append(packageList, fake.PackageForGroup(normalization.GroupVersion(gv), orderer.OrderTypes(types), typedClientBasePath, arguments.OutputBase, inputPath, boilerplate, generatedBy))
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -27,7 +27,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
)
|
||||
|
||||
func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, boilerplate []byte, generatedBy string) generator.Package {
|
||||
func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packageBasePath string, srcTreePath string, inputPath string, boilerplate []byte, generatedBy string) generator.Package {
|
||||
outputPackagePath := filepath.Join(packageBasePath, gv.Group, gv.Version, "fake")
|
||||
// TODO: should make this a function, called by here and in client-generator.go
|
||||
realClientPath := filepath.Join(packageBasePath, gv.Group, gv.Version)
|
||||
@@ -55,6 +55,7 @@ func PackageForGroup(gv unversioned.GroupVersion, typeList []*types.Type, packag
|
||||
},
|
||||
outputPackage: outputPackagePath,
|
||||
group: normalization.BeforeFirstDot(gv.Group),
|
||||
inputPackage: inputPath,
|
||||
version: gv.Version,
|
||||
typeToMatch: t,
|
||||
imports: generator.NewImportTracker(),
|
||||
|
@@ -31,6 +31,7 @@ type genFakeForType struct {
|
||||
generator.DefaultGen
|
||||
outputPackage string
|
||||
group string
|
||||
inputPackage string
|
||||
version string
|
||||
typeToMatch *types.Type
|
||||
imports namer.ImportTracker
|
||||
@@ -87,6 +88,20 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
|
||||
if canonicalVersion == "unversioned" {
|
||||
canonicalVersion = ""
|
||||
}
|
||||
|
||||
groupName := g.group
|
||||
if g.group == "core" {
|
||||
groupName = ""
|
||||
}
|
||||
|
||||
// allow user to define a group name that's different from the one parsed from the directory.
|
||||
for _, comment := range c.Universe.Package(g.inputPackage).DocComments {
|
||||
comment = strings.TrimLeft(comment, "//")
|
||||
if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok {
|
||||
groupName = override
|
||||
}
|
||||
}
|
||||
|
||||
m := map[string]interface{}{
|
||||
"type": t,
|
||||
"package": pkg,
|
||||
@@ -94,6 +109,7 @@ func (g *genFakeForType) GenerateType(c *generator.Context, t *types.Type, w io.
|
||||
"namespaced": namespaced,
|
||||
"Group": namer.IC(g.group),
|
||||
"group": canonicalGroup,
|
||||
"groupName": groupName,
|
||||
"version": canonicalVersion,
|
||||
"watchInterface": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/watch", Name: "Interface"}),
|
||||
"apiDeleteOptions": c.Universe.Type(types.Name{Package: "k8s.io/kubernetes/pkg/api", Name: "DeleteOptions"}),
|
||||
@@ -168,7 +184,7 @@ type Fake$.type|publicPlural$ struct {
|
||||
`
|
||||
|
||||
var resource = `
|
||||
var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.group$", Version: "$.version$", Resource: "$.type|allLowercasePlural$"}
|
||||
var $.type|allLowercasePlural$Resource = $.GroupVersionResource|raw${Group: "$.groupName$", Version: "$.version$", Resource: "$.type|allLowercasePlural$"}
|
||||
`
|
||||
|
||||
var listTemplate = `
|
||||
|
@@ -18,6 +18,7 @@ package generators
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/client-gen/generators/normalization"
|
||||
"k8s.io/kubernetes/cmd/libs/go2idl/generator"
|
||||
@@ -34,6 +35,7 @@ type genGroup struct {
|
||||
// types in this group
|
||||
types []*types.Type
|
||||
imports namer.ImportTracker
|
||||
inputPacakge string
|
||||
}
|
||||
|
||||
var _ generator.Generator = &genGroup{}
|
||||
@@ -66,17 +68,22 @@ func (g *genGroup) GenerateType(c *generator.Context, t *types.Type, w io.Writer
|
||||
return `"/apis"`
|
||||
}
|
||||
|
||||
canonize := func(group string) string {
|
||||
if group == "core" {
|
||||
return ""
|
||||
groupName := g.group
|
||||
if g.group == "core" {
|
||||
groupName = ""
|
||||
}
|
||||
// allow user to define a group name that's different from the one parsed from the directory.
|
||||
for _, comment := range c.Universe.Package(g.inputPacakge).DocComments {
|
||||
comment = strings.TrimLeft(comment, "//")
|
||||
if override, ok := types.ExtractCommentTags("+", comment)["groupName"]; ok && override != "" {
|
||||
groupName = override
|
||||
}
|
||||
return group
|
||||
}
|
||||
|
||||
m := map[string]interface{}{
|
||||
"group": normalization.BeforeFirstDot(g.group),
|
||||
"Group": namer.IC(normalization.BeforeFirstDot(g.group)),
|
||||
"canonicalGroup": canonize(g.group),
|
||||
"groupName": groupName,
|
||||
"types": g.types,
|
||||
"Config": c.Universe.Type(types.Name{Package: pkgRESTClient, Name: "Config"}),
|
||||
"DefaultKubernetesUserAgent": c.Universe.Function(types.Name{Package: pkgRESTClient, Name: "DefaultKubernetesUserAgent"}),
|
||||
@@ -190,7 +197,7 @@ func New(c *$.RESTClient|raw$) *$.Group$Client {
|
||||
var setInternalVersionClientDefaultsTemplate = `
|
||||
func setConfigDefaults(config *$.Config|raw$) error {
|
||||
// if $.group$ group is not registered, return an error
|
||||
g, err := $.latestGroup|raw$("$.canonicalGroup$")
|
||||
g, err := $.latestGroup|raw$("$.groupName$")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -219,7 +226,7 @@ func setConfigDefaults(config *$.Config|raw$) error {
|
||||
var setClientDefaultsTemplate = `
|
||||
func setConfigDefaults(config *$.Config|raw$) error {
|
||||
// if $.group$ group is not registered, return an error
|
||||
g, err := $.latestGroup|raw$("$.canonicalGroup$")
|
||||
g, err := $.latestGroup|raw$("$.groupName$")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ import (
|
||||
|
||||
var (
|
||||
test = flag.BoolP("test", "t", false, "set this flag to generate the client code for the testdata")
|
||||
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "batch/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/\"")
|
||||
inputVersions = flag.StringSlice("input", []string{"api/", "extensions/", "autoscaling/", "batch/", "rbac/"}, "group/versions that client-gen will generate clients for. At most one version per group is allowed. Specified in the format \"group1/version1,group2/version2...\". Default to \"api/,extensions/,autoscaling/,batch/,rbac/\"")
|
||||
includedTypesOverrides = flag.StringSlice("included-types-overrides", []string{}, "list of group/version/type for which client should be generated. By default, client is generated for all types which have genclient=true in types.go. This overrides that. For each groupVersion in this list, only the types mentioned here will be included. The default check of genclient=true will be used for other group versions.")
|
||||
basePath = flag.String("input-base", "k8s.io/kubernetes/pkg/apis", "base path to look for the api group. Default to \"k8s.io/kubernetes/pkg/apis\"")
|
||||
clientsetName = flag.StringP("clientset-name", "n", "internalclientset", "the name of the generated clientset package.")
|
||||
|
18
cmd/libs/go2idl/client-gen/test_apis/testgroup.k8s.io/doc.go
Normal file
18
cmd/libs/go2idl/client-gen/test_apis/testgroup.k8s.io/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// +groupName=testgroup.k8s.io
|
||||
package testgroup
|
@@ -31,7 +31,7 @@ type FakeTestTypes struct {
|
||||
ns string
|
||||
}
|
||||
|
||||
var testtypesResource = unversioned.GroupVersionResource{Group: "testgroup", Version: "", Resource: "testtypes"}
|
||||
var testtypesResource = unversioned.GroupVersionResource{Group: "testgroup.k8s.io", Version: "", Resource: "testtypes"}
|
||||
|
||||
func (c *FakeTestTypes) Create(testType *testgroup_k8s_io.TestType) (result *testgroup_k8s_io.TestType, err error) {
|
||||
obj, err := c.Fake.
|
||||
|
@@ -58,8 +58,9 @@ kube-apiserver
|
||||
--apiserver-count=1: The number of apiservers running in the cluster
|
||||
--authentication-token-webhook-cache-ttl=2m0s: The duration to cache responses from the webhook token authenticator. Default is 2m
|
||||
--authentication-token-webhook-config-file="": File with webhook configuration for token authentication in kubeconfig format. The API server will query the remote service to determine authentication for bearer tokens.
|
||||
--authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook
|
||||
--authorization-mode="AlwaysAllow": Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC
|
||||
--authorization-policy-file="": File with authorization policy in csv format, used with --authorization-mode=ABAC, on the secure port.
|
||||
--authorization-rbac-super-user="": If specified, a username which avoids RBAC authorization checks and role binding privilege escalation checks, to be used with --authorization-mode=RBAC.
|
||||
--authorization-webhook-cache-authorized-ttl=5m0s: The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.
|
||||
--authorization-webhook-cache-unauthorized-ttl=30s: The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.
|
||||
--authorization-webhook-config-file="": File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.
|
||||
@@ -115,7 +116,7 @@ kube-apiserver
|
||||
--ssh-user="": If non-empty, use secure SSH proxy to the nodes, using this user name
|
||||
--storage-backend="": The storage backend for persistence. Options: 'etcd2' (default), 'etcd3'.
|
||||
--storage-media-type="application/json": The media type to use to store objects in storage. Defaults to application/json. Some resources may only support a specific media type and will ignore this setting.
|
||||
--storage-versions="apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,policy/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
|
||||
--storage-versions="apps/v1alpha1,authentication.k8s.io/v1beta1,authorization.k8s.io/v1beta1,autoscaling/v1,batch/v1,componentconfig/v1alpha1,extensions/v1beta1,metrics/v1alpha1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1,v1": The per-group version to store resources in. Specified in the format "group1/version1,group2/version2,...". In the case where objects are moved from one group to the other, you may specify the format "group1=group2/v1beta1,group3/v1beta1,...". You only need to pass the groups you wish to change from the defaults. It defaults to a list of preferred versions of all registered groups, which is derived from the KUBE_API_VERSIONS environment variable.
|
||||
--tls-cert-file="": File containing x509 Certificate for HTTPS. (CA cert, if any, concatenated after server cert). If HTTPS serving is enabled, and --tls-cert-file and --tls-private-key-file are not provided, a self-signed certificate and key are generated for the public address and saved to /var/run/kubernetes.
|
||||
--tls-private-key-file="": File containing x509 private key matching --tls-cert-file.
|
||||
--token-auth-file="": If set, the file that will be used to secure the secure port of the API server via token authentication.
|
||||
@@ -123,7 +124,7 @@ kube-apiserver
|
||||
--watch-cache-sizes=[]: List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. The individual override format: resource#size, where size is a number. It takes effect when watch-cache is enabled.
|
||||
```
|
||||
|
||||
###### Auto generated by spf13/cobra on 18-May-2016
|
||||
###### Auto generated by spf13/cobra on 23-May-2016
|
||||
|
||||
|
||||
<!-- BEGIN MUNGE: GENERATED_ANALYTICS -->
|
||||
|
@@ -58,7 +58,7 @@ KUBE_GOVERALLS_BIN=${KUBE_GOVERALLS_BIN:-}
|
||||
# Lists of API Versions of each groups that should be tested, groups are
|
||||
# separated by comma, lists are separated by semicolon. e.g.,
|
||||
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
|
||||
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1,federation/v1alpha1;v1,autoscaling/v1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,metrics/v1alpha1,federation/v1alpha1,policy/v1alpha1"}
|
||||
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1,metrics/v1alpha1,federation/v1alpha1;v1,autoscaling/v1,batch/v1,batch/v2alpha1,extensions/v1beta1,apps/v1alpha1,metrics/v1alpha1,federation/v1alpha1,policy/v1alpha1,rbac.authorization.k8s.io/v1alpha1"}
|
||||
# once we have multiple group supports
|
||||
# Run tests with the standard (registry) and a custom etcd prefix
|
||||
# (kubernetes.io/registry).
|
||||
|
@@ -29,7 +29,7 @@ source "${KUBE_ROOT}/hack/lib/init.sh"
|
||||
# "v1,compute/v1alpha1,experimental/v1alpha2;v1,compute/v2,experimental/v1alpha3"
|
||||
# TODO: It's going to be:
|
||||
# KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1"}
|
||||
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1"}
|
||||
KUBE_TEST_API_VERSIONS=${KUBE_TEST_API_VERSIONS:-"v1,extensions/v1beta1;v1,autoscaling/v1,batch/v1,apps/v1alpha1,policy/v1alpha1,extensions/v1beta1,rbac.authorization.k8s.io/v1alpha1"}
|
||||
|
||||
# Give integration tests longer to run
|
||||
# TODO: allow a larger value to be passed in
|
||||
|
@@ -74,7 +74,7 @@ APISERVER_PID=$!
|
||||
kube::util::wait_for_url "http://127.0.0.1:${API_PORT}/healthz" "apiserver: "
|
||||
|
||||
SWAGGER_API_PATH="http://127.0.0.1:${API_PORT}/swaggerapi/"
|
||||
DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1"
|
||||
DEFAULT_GROUP_VERSIONS="v1 autoscaling/v1 batch/v1 batch/v2alpha1 extensions/v1beta1 apps/v1alpha1 policy/v1alpha1 rbac.authorization.k8s.io/v1alpha1"
|
||||
VERSIONS=${VERSIONS:-$DEFAULT_GROUP_VERSIONS}
|
||||
|
||||
kube::log::status "Updating " ${SWAGGER_ROOT_DIR}
|
||||
|
@@ -22,6 +22,7 @@ authentication-token-webhook-cache-ttl
|
||||
authentication-token-webhook-config-file
|
||||
authorization-mode
|
||||
authorization-policy-file
|
||||
authorization-rbac-super-user
|
||||
authorization-webhook-config-file
|
||||
authorization-webhook-cache-authorized-ttl
|
||||
authorization-webhook-cache-unauthorized-ttl
|
||||
|
@@ -34,6 +34,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/runtime/serializer/recognizer"
|
||||
|
||||
@@ -46,6 +47,7 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/metrics/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/policy/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -57,6 +59,7 @@ var (
|
||||
Apps TestGroup
|
||||
Policy TestGroup
|
||||
Federation TestGroup
|
||||
Rbac TestGroup
|
||||
|
||||
serializer runtime.SerializerInfo
|
||||
storageSerializer runtime.SerializerInfo
|
||||
@@ -191,6 +194,13 @@ func init() {
|
||||
internalTypes: api.Scheme.KnownTypes(federation.SchemeGroupVersion),
|
||||
}
|
||||
}
|
||||
if _, ok := Groups[rbac.GroupName]; !ok {
|
||||
Groups[rbac.GroupName] = TestGroup{
|
||||
externalGroupVersions: []unversioned.GroupVersion{{Group: rbac.GroupName, Version: registered.GroupOrDie(rbac.GroupName).GroupVersion.Version}},
|
||||
internalGroupVersion: rbac.SchemeGroupVersion,
|
||||
internalTypes: api.Scheme.KnownTypes(rbac.SchemeGroupVersion),
|
||||
}
|
||||
}
|
||||
|
||||
Default = Groups[api.GroupName]
|
||||
Autoscaling = Groups[autoscaling.GroupName]
|
||||
@@ -199,6 +209,7 @@ func init() {
|
||||
Policy = Groups[policy.GroupName]
|
||||
Extensions = Groups[extensions.GroupName]
|
||||
Federation = Groups[federation.GroupName]
|
||||
Rbac = Groups[rbac.GroupName]
|
||||
}
|
||||
|
||||
func (g TestGroup) ContentConfig() (string, *unversioned.GroupVersion, runtime.Codec) {
|
||||
|
18
pkg/apis/rbac/doc.go
Normal file
18
pkg/apis/rbac/doc.go
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// +groupName=rbac.authorization.k8s.io
|
||||
package rbac
|
114
pkg/apis/rbac/install/install_test.go
Normal file
114
pkg/apis/rbac/install/install_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 install
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestResourceVersioner(t *testing.T) {
|
||||
roleBinding := rbac.RoleBinding{ObjectMeta: api.ObjectMeta{ResourceVersion: "10"}}
|
||||
version, err := accessor.ResourceVersion(&roleBinding)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if version != "10" {
|
||||
t.Errorf("unexpected version %v", version)
|
||||
}
|
||||
|
||||
roleBindingList := rbac.RoleBindingList{ListMeta: unversioned.ListMeta{ResourceVersion: "10"}}
|
||||
version, err = accessor.ResourceVersion(&roleBindingList)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if version != "10" {
|
||||
t.Errorf("unexpected version %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCodec(t *testing.T) {
|
||||
roleBinding := rbac.RoleBinding{}
|
||||
// We do want to use package registered rather than testapi here, because we
|
||||
// want to test if the package install and package registered work as expected.
|
||||
data, err := runtime.Encode(api.Codecs.LegacyCodec(registered.GroupOrDie(rbac.GroupName).GroupVersion), &roleBinding)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
other := rbac.RoleBinding{}
|
||||
if err := json.Unmarshal(data, &other); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if other.APIVersion != registered.GroupOrDie(rbac.GroupName).GroupVersion.String() || other.Kind != "RoleBinding" {
|
||||
t.Errorf("unexpected unmarshalled object %#v", other)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterfacesFor(t *testing.T) {
|
||||
if _, err := registered.GroupOrDie(rbac.GroupName).InterfacesFor(rbac.SchemeGroupVersion); err == nil {
|
||||
t.Fatalf("unexpected non-error: %v", err)
|
||||
}
|
||||
for i, version := range registered.GroupOrDie(rbac.GroupName).GroupVersions {
|
||||
if vi, err := registered.GroupOrDie(rbac.GroupName).InterfacesFor(version); err != nil || vi == nil {
|
||||
t.Fatalf("%d: unexpected result: %v", i, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRESTMapper(t *testing.T) {
|
||||
gv := v1alpha1.SchemeGroupVersion
|
||||
roleBindingGVK := gv.WithKind("RoleBinding")
|
||||
|
||||
if gvk, err := registered.GroupOrDie(rbac.GroupName).RESTMapper.KindFor(gv.WithResource("rolebindings")); err != nil || gvk != roleBindingGVK {
|
||||
t.Errorf("unexpected version mapping: %v %v", gvk, err)
|
||||
}
|
||||
|
||||
for _, version := range registered.GroupOrDie(rbac.GroupName).GroupVersions {
|
||||
mapping, err := registered.GroupOrDie(rbac.GroupName).RESTMapper.RESTMapping(roleBindingGVK.GroupKind(), version.Version)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if mapping.Resource != "rolebindings" {
|
||||
t.Errorf("incorrect resource name: %#v", mapping)
|
||||
}
|
||||
if mapping.GroupVersionKind.GroupVersion() != version {
|
||||
t.Errorf("incorrect groupVersion: %v", mapping)
|
||||
}
|
||||
|
||||
interfaces, _ := registered.GroupOrDie(rbac.GroupName).InterfacesFor(version)
|
||||
if mapping.ObjectConvertor != interfaces.ObjectConvertor {
|
||||
t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces)
|
||||
}
|
||||
|
||||
roleBinding := &rbac.RoleBinding{ObjectMeta: api.ObjectMeta{Name: "foo"}}
|
||||
name, err := mapping.MetadataAccessor.Name(roleBinding)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if name != "foo" {
|
||||
t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor)
|
||||
}
|
||||
}
|
||||
}
|
@@ -36,6 +36,8 @@ const (
|
||||
GroupKind = "Group"
|
||||
ServiceAccountKind = "ServiceAccount"
|
||||
UserKind = "User"
|
||||
|
||||
UserAll = "*"
|
||||
)
|
||||
|
||||
// PolicyRule holds information that describes a policy rule, but does not contain information
|
||||
@@ -66,7 +68,7 @@ type Subject struct {
|
||||
// If the Authorizer does not recognized the kind value, the Authorizer should report an error.
|
||||
Kind string
|
||||
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
|
||||
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1".
|
||||
// expected to be API version of this API group. For example "rbac/v1alpha1".
|
||||
APIVersion string
|
||||
// Name of the object being referenced.
|
||||
Name string
|
||||
@@ -75,6 +77,8 @@ type Subject struct {
|
||||
Namespace string
|
||||
}
|
||||
|
||||
// +genclient=true
|
||||
|
||||
// Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.
|
||||
type Role struct {
|
||||
unversioned.TypeMeta
|
||||
@@ -85,6 +89,8 @@ type Role struct {
|
||||
Rules []PolicyRule
|
||||
}
|
||||
|
||||
// +genclient=true
|
||||
|
||||
// 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.
|
||||
@@ -120,6 +126,8 @@ type RoleList struct {
|
||||
Items []Role
|
||||
}
|
||||
|
||||
// +genclient=true,nonNamespaced=true
|
||||
|
||||
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
|
||||
type ClusterRole struct {
|
||||
unversioned.TypeMeta
|
||||
@@ -130,6 +138,8 @@ type ClusterRole struct {
|
||||
Rules []PolicyRule
|
||||
}
|
||||
|
||||
// +genclient=true,nonNamespaced=true
|
||||
|
||||
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
|
||||
// and adds who information via Subject.
|
||||
type ClusterRoleBinding struct {
|
||||
|
@@ -14,5 +14,6 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// +groupName=rbac.authorization.k8s.io
|
||||
// +genconversion=true
|
||||
package v1alpha1
|
||||
|
@@ -147,7 +147,7 @@ message Subject {
|
||||
optional string kind = 1;
|
||||
|
||||
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
|
||||
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1".
|
||||
// expected to be API version of this API group. For example "rbac/v1alpha1".
|
||||
optional string apiVersion = 2;
|
||||
|
||||
// Name of the object being referenced.
|
||||
|
@@ -24,7 +24,7 @@ import (
|
||||
)
|
||||
|
||||
// SchemeGroupVersion is group version used to register these objects
|
||||
var SchemeGroupVersion = unversioned.GroupVersion{Group: rbac.GroupName, Version: "v1alpha"}
|
||||
var SchemeGroupVersion = unversioned.GroupVersion{Group: rbac.GroupName, Version: "v1alpha1"}
|
||||
|
||||
func AddToScheme(scheme *runtime.Scheme) {
|
||||
addKnownTypes(scheme)
|
||||
|
@@ -55,7 +55,7 @@ type Subject struct {
|
||||
// If the Authorizer does not recognized the kind value, the Authorizer should report an error.
|
||||
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
|
||||
// APIVersion holds the API group and version of the referenced object. For non-object references such as "Group" and "User" this is
|
||||
// expected to be API version of this API group. For example "rbac.authorization.k8s.io/v1alpha1".
|
||||
// expected to be API version of this API group. For example "rbac/v1alpha1".
|
||||
APIVersion string `json:"apiVersion" protobuf:"bytes,2,opt.name=apiVersion"`
|
||||
// Name of the object being referenced.
|
||||
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
|
||||
@@ -64,6 +64,8 @@ type Subject struct {
|
||||
Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"`
|
||||
}
|
||||
|
||||
// +genclient=true
|
||||
|
||||
// Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding.
|
||||
type Role struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
@@ -74,6 +76,8 @@ type Role struct {
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
}
|
||||
|
||||
// +genclient=true
|
||||
|
||||
// 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.
|
||||
@@ -110,6 +114,8 @@ type RoleList struct {
|
||||
Items []Role `json:"items" protobuf:"bytes,2,rep,name=items"`
|
||||
}
|
||||
|
||||
// +genclient=true,nonNamespaced=true
|
||||
|
||||
// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding.
|
||||
type ClusterRole struct {
|
||||
unversioned.TypeMeta `json:",inline"`
|
||||
@@ -120,6 +126,8 @@ type ClusterRole struct {
|
||||
Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"`
|
||||
}
|
||||
|
||||
// +genclient=true,nonNamespaced=true
|
||||
|
||||
// ClusterRoleBinding references a ClusterRole, but not contain it. It can reference a ClusterRole in the global namespace,
|
||||
// and adds who information via Subject.
|
||||
type ClusterRoleBinding struct {
|
||||
|
@@ -126,7 +126,7 @@ func (RoleList) SwaggerDoc() map[string]string {
|
||||
var map_Subject = map[string]string{
|
||||
"": "Subject contains a reference to the object or user identities a role binding applies to. This can either hold a direct API object reference, or a value for non-objects such as user and group names.",
|
||||
"kind": "Kind of object being referenced. Values defined by this API group are \"User\", \"Group\", and \"ServiceAccount\". If the Authorizer does not recognized the kind value, the Authorizer should report an error.",
|
||||
"apiVersion": "APIVersion holds the API group and version of the referenced object. For non-object references such as \"Group\" and \"User\" this is expected to be API version of this API group. For example \"rbac.authorization.k8s.io/v1alpha1\".",
|
||||
"apiVersion": "APIVersion holds the API group and version of the referenced object. For non-object references such as \"Group\" and \"User\" this is expected to be API version of this API group. For example \"rbac/v1alpha1\".",
|
||||
"name": "Name of the object being referenced.",
|
||||
"namespace": "Namespace of the referenced object. If the object kind is non-namespace, such as \"User\" or \"Group\", and this value is not empty the Authorizer should report an error.",
|
||||
}
|
||||
|
120
pkg/apis/rbac/validation/policy_comparator.go
Normal file
120
pkg/apis/rbac/validation/policy_comparator.go
Normal file
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 validation
|
||||
|
||||
import "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
|
||||
// Covers determines whether or not the ownerRules cover the servantRules in terms of allowed actions.
|
||||
// It returns whether or not the ownerRules cover and a list of the rules that the ownerRules do not cover.
|
||||
func Covers(ownerRules, servantRules []rbac.PolicyRule) (bool, []rbac.PolicyRule) {
|
||||
// 1. Break every servantRule into individual rule tuples: group, verb, resource, resourceName
|
||||
// 2. Compare the mini-rules against each owner rule. Because the breakdown is down to the most atomic level, we're guaranteed that each mini-servant rule will be either fully covered or not covered by a single owner rule
|
||||
// 3. Any left over mini-rules means that we are not covered and we have a nice list of them.
|
||||
// TODO: it might be nice to collapse the list down into something more human readable
|
||||
|
||||
subrules := []rbac.PolicyRule{}
|
||||
for _, servantRule := range servantRules {
|
||||
subrules = append(subrules, breakdownRule(servantRule)...)
|
||||
}
|
||||
|
||||
uncoveredRules := []rbac.PolicyRule{}
|
||||
for _, subrule := range subrules {
|
||||
covered := false
|
||||
for _, ownerRule := range ownerRules {
|
||||
if ruleCovers(ownerRule, subrule) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
uncoveredRules = append(uncoveredRules, subrule)
|
||||
}
|
||||
}
|
||||
|
||||
return (len(uncoveredRules) == 0), uncoveredRules
|
||||
}
|
||||
|
||||
// breadownRule takes a rule and builds an equivalent list of rules that each have at most one verb, one
|
||||
// resource, and one resource name
|
||||
func breakdownRule(rule rbac.PolicyRule) []rbac.PolicyRule {
|
||||
subrules := []rbac.PolicyRule{}
|
||||
for _, group := range rule.APIGroups {
|
||||
for _, resource := range rule.Resources {
|
||||
for _, verb := range rule.Verbs {
|
||||
if len(rule.ResourceNames) > 0 {
|
||||
for _, resourceName := range rule.ResourceNames {
|
||||
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}, ResourceNames: []string{resourceName}})
|
||||
}
|
||||
|
||||
} else {
|
||||
subrules = append(subrules, rbac.PolicyRule{APIGroups: []string{group}, Resources: []string{resource}, Verbs: []string{verb}})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Non-resource URLs are unique because they don't combine with other policy rule fields.
|
||||
for _, nonResourceURL := range rule.NonResourceURLs {
|
||||
subrules = append(subrules, rbac.PolicyRule{NonResourceURLs: []string{nonResourceURL}})
|
||||
}
|
||||
|
||||
return subrules
|
||||
}
|
||||
|
||||
func has(set []string, ele string) bool {
|
||||
for _, s := range set {
|
||||
if s == ele {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hasAll(set, contains []string) bool {
|
||||
owning := make(map[string]struct{}, len(set))
|
||||
for _, ele := range set {
|
||||
owning[ele] = struct{}{}
|
||||
}
|
||||
for _, ele := range contains {
|
||||
if _, ok := owning[ele]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// ruleCovers determines whether the ownerRule (which may have multiple verbs, resources, and resourceNames) covers
|
||||
// the subrule (which may only contain at most one verb, resource, and resourceName)
|
||||
func ruleCovers(ownerRule, subRule rbac.PolicyRule) bool {
|
||||
|
||||
verbMatches := has(ownerRule.Verbs, rbac.VerbAll) || hasAll(ownerRule.Verbs, subRule.Verbs)
|
||||
groupMatches := has(ownerRule.APIGroups, rbac.APIGroupAll) || hasAll(ownerRule.APIGroups, subRule.APIGroups)
|
||||
resourceMatches := has(ownerRule.Resources, rbac.ResourceAll) || hasAll(ownerRule.Resources, subRule.Resources)
|
||||
nonResourceURLMatches := has(ownerRule.NonResourceURLs, rbac.NonResourceAll) || hasAll(ownerRule.NonResourceURLs, subRule.NonResourceURLs)
|
||||
|
||||
resourceNameMatches := false
|
||||
|
||||
if len(subRule.ResourceNames) == 0 {
|
||||
resourceNameMatches = (len(ownerRule.ResourceNames) == 0)
|
||||
} else {
|
||||
resourceNameMatches = (len(ownerRule.ResourceNames) == 0) || hasAll(ownerRule.ResourceNames, subRule.ResourceNames)
|
||||
}
|
||||
|
||||
return verbMatches && groupMatches && resourceMatches && resourceNameMatches && nonResourceURLMatches
|
||||
}
|
371
pkg/apis/rbac/validation/policy_comparator_test.go
Normal file
371
pkg/apis/rbac/validation/policy_comparator_test.go
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
)
|
||||
|
||||
type escalationTest struct {
|
||||
ownerRules []rbac.PolicyRule
|
||||
servantRules []rbac.PolicyRule
|
||||
|
||||
expectedCovered bool
|
||||
expectedUncoveredRules []rbac.PolicyRule
|
||||
}
|
||||
|
||||
func TestCoversExactMatch(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversMultipleRulesCoveringSingleRule(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversMultipleAPIGroupsCoveringSingleRule(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversSingleAPIGroupsCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group1"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"deployments"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"delete"}, Resources: []string{"builds"}},
|
||||
{APIGroups: []string{"group2"}, Verbs: []string{"update"}, Resources: []string{"builds", "deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
|
||||
}
|
||||
|
||||
func TestCoversMultipleRulesMissingSingleVerbResourceCombination(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments"}},
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete"}, Resources: []string{"pods"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"delete", "update"}, Resources: []string{"builds", "deployments", "pods"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"update"}, Resources: []string{"pods"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversAPIGroupStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"group1", "group2"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringAPIGroupStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"dummy-group"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversAPIGroupStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"*"}, Verbs: []string{"get"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversVerbStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"watch", "list"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringVerbStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get", "list", "watch", "create", "update", "delete", "exec"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversVerbStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"*"}, Resources: []string{"roles"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceStarCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"resourcegroup:deployments"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringResourceStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"roles", "resourcegroup:deployments"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceStarCoveringStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"*"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversResourceNameEmptyCoveringMultiple(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversEnumerationNotCoveringResourceNameEmpty(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{"foo", "bar"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}, ResourceNames: []string{}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"pods"}},
|
||||
},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLs(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsStar(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"*"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{NonResourceURLs: []string{"/apis", "/apis/v1", "/"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsWithOtherFields(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
|
||||
expectedCovered: true,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func TestCoversNonResourceURLsWithOtherFieldsFailure(t *testing.T) {
|
||||
escalationTest{
|
||||
ownerRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}},
|
||||
},
|
||||
servantRules: []rbac.PolicyRule{
|
||||
{APIGroups: []string{"v1"}, Verbs: []string{"get"}, Resources: []string{"builds"}, NonResourceURLs: []string{"/apis"}},
|
||||
},
|
||||
|
||||
expectedCovered: false,
|
||||
expectedUncoveredRules: []rbac.PolicyRule{{NonResourceURLs: []string{"/apis"}}},
|
||||
}.test(t)
|
||||
}
|
||||
|
||||
func (test escalationTest) test(t *testing.T) {
|
||||
actualCovered, actualUncoveredRules := Covers(test.ownerRules, test.servantRules)
|
||||
|
||||
if actualCovered != test.expectedCovered {
|
||||
t.Errorf("expected %v, but got %v", test.expectedCovered, actualCovered)
|
||||
}
|
||||
|
||||
if !rulesMatch(test.expectedUncoveredRules, actualUncoveredRules) {
|
||||
t.Errorf("expected %v, but got %v", test.expectedUncoveredRules, actualUncoveredRules)
|
||||
}
|
||||
}
|
||||
|
||||
func rulesMatch(expectedRules, actualRules []rbac.PolicyRule) bool {
|
||||
if len(expectedRules) != len(actualRules) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, expectedRule := range expectedRules {
|
||||
found := false
|
||||
for _, actualRule := range actualRules {
|
||||
if reflect.DeepEqual(expectedRule, actualRule) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
208
pkg/apis/rbac/validation/rulevalidation.go
Normal file
208
pkg/apis/rbac/validation/rulevalidation.go
Normal file
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
||||
type AuthorizationRuleResolver interface {
|
||||
// GetRoleReferenceRules attempts to resolve the role reference of a RoleBinding or ClusterRoleBinding. The passed namespace should be the namepsace
|
||||
// of the role binding, the empty string if a cluster role binding.
|
||||
GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, namespace string) ([]rbac.PolicyRule, error)
|
||||
|
||||
// GetEffectivePolicyRules returns the list of rules that apply to a given user in a given namespace and error. If an error is returned, the slice of
|
||||
// PolicyRules may not be complete, but it contains all retrievable rules. This is done because policy rules are purely additive and policy determinations
|
||||
// can be made on the basis of those rules that are found.
|
||||
GetEffectivePolicyRules(ctx api.Context) ([]rbac.PolicyRule, error)
|
||||
}
|
||||
|
||||
// ConfirmNoEscalation determines if the roles for a given user in a given namespace encompass the provided role.
|
||||
func ConfirmNoEscalation(ctx api.Context, ruleResolver AuthorizationRuleResolver, rules []rbac.PolicyRule) error {
|
||||
ruleResolutionErrors := []error{}
|
||||
|
||||
ownerLocalRules, err := ruleResolver.GetEffectivePolicyRules(ctx)
|
||||
if err != nil {
|
||||
// As per AuthorizationRuleResolver contract, this may return a non fatal error with an incomplete list of policies. Log the error and continue.
|
||||
user, _ := api.UserFrom(ctx)
|
||||
glog.V(1).Infof("non-fatal error getting local rules for %v: %v", user, err)
|
||||
ruleResolutionErrors = append(ruleResolutionErrors, err)
|
||||
}
|
||||
|
||||
masterContext := api.WithNamespace(ctx, "")
|
||||
ownerGlobalRules, err := ruleResolver.GetEffectivePolicyRules(masterContext)
|
||||
if err != nil {
|
||||
// Same case as above. Log error, don't fail.
|
||||
user, _ := api.UserFrom(ctx)
|
||||
glog.V(1).Infof("non-fatal error getting global rules for %v: %v", user, err)
|
||||
ruleResolutionErrors = append(ruleResolutionErrors, err)
|
||||
}
|
||||
|
||||
ownerRules := make([]rbac.PolicyRule, 0, len(ownerGlobalRules)+len(ownerLocalRules))
|
||||
ownerRules = append(ownerRules, ownerLocalRules...)
|
||||
ownerRules = append(ownerRules, ownerGlobalRules...)
|
||||
|
||||
ownerRightsCover, missingRights := Covers(ownerRules, rules)
|
||||
if !ownerRightsCover {
|
||||
user, _ := api.UserFrom(ctx)
|
||||
return errors.NewUnauthorized(fmt.Sprintf("attempt to grant extra privileges: %v user=%v ownerrules=%v ruleResolutionErrors=%v", missingRights, user, ownerRules, ruleResolutionErrors))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DefaultRuleResolver struct {
|
||||
roleGetter RoleGetter
|
||||
roleBindingLister RoleBindingLister
|
||||
clusterRoleGetter ClusterRoleGetter
|
||||
clusterRoleBindingLister ClusterRoleBindingLister
|
||||
}
|
||||
|
||||
func NewDefaultRuleResolver(roleGetter RoleGetter, roleBindingLister RoleBindingLister, clusterRoleGetter ClusterRoleGetter, clusterRoleBindingLister ClusterRoleBindingLister) *DefaultRuleResolver {
|
||||
return &DefaultRuleResolver{roleGetter, roleBindingLister, clusterRoleGetter, clusterRoleBindingLister}
|
||||
}
|
||||
|
||||
type RoleGetter interface {
|
||||
GetRole(ctx api.Context, id string) (*rbac.Role, error)
|
||||
}
|
||||
|
||||
type RoleBindingLister interface {
|
||||
ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error)
|
||||
}
|
||||
|
||||
type ClusterRoleGetter interface {
|
||||
GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error)
|
||||
}
|
||||
|
||||
type ClusterRoleBindingLister interface {
|
||||
ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error)
|
||||
}
|
||||
|
||||
// GetRoleReferenceRules attempts resolve the RoleBinding or ClusterRoleBinding.
|
||||
func (r *DefaultRuleResolver) GetRoleReferenceRules(ctx api.Context, roleRef api.ObjectReference, bindingNamespace string) ([]rbac.PolicyRule, error) {
|
||||
switch roleRef.Kind {
|
||||
case "Role":
|
||||
// Roles can only be referenced by RoleBindings within the same namespace.
|
||||
if len(bindingNamespace) == 0 {
|
||||
return nil, fmt.Errorf("cluster role binding references role %q in namespace %q", roleRef.Name, roleRef.Namespace)
|
||||
} else {
|
||||
if bindingNamespace != roleRef.Namespace {
|
||||
return nil, fmt.Errorf("role binding in namespace %q references role %q in namespace %q", bindingNamespace, roleRef.Name, roleRef.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
role, err := r.roleGetter.GetRole(api.WithNamespace(ctx, roleRef.Namespace), roleRef.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return role.Rules, nil
|
||||
case "ClusterRole":
|
||||
clusterRole, err := r.clusterRoleGetter.GetClusterRole(api.WithNamespace(ctx, ""), roleRef.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return clusterRole.Rules, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported role reference kind: %q", roleRef.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *DefaultRuleResolver) GetEffectivePolicyRules(ctx api.Context) ([]rbac.PolicyRule, error) {
|
||||
policyRules := []rbac.PolicyRule{}
|
||||
errorlist := []error{}
|
||||
|
||||
if namespace := api.NamespaceValue(ctx); len(namespace) == 0 {
|
||||
clusterRoleBindings, err := r.clusterRoleBindingLister.ListClusterRoleBindings(ctx, &api.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, clusterRoleBinding := range clusterRoleBindings.Items {
|
||||
if ok, err := appliesTo(ctx, clusterRoleBinding.Subjects); err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
} else if !ok {
|
||||
continue
|
||||
}
|
||||
rules, err := r.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
continue
|
||||
}
|
||||
policyRules = append(policyRules, rules...)
|
||||
}
|
||||
} else {
|
||||
roleBindings, err := r.roleBindingLister.ListRoleBindings(ctx, &api.ListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, roleBinding := range roleBindings.Items {
|
||||
if ok, err := appliesTo(ctx, roleBinding.Subjects); err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
} else if !ok {
|
||||
continue
|
||||
}
|
||||
rules, err := r.GetRoleReferenceRules(ctx, roleBinding.RoleRef, namespace)
|
||||
if err != nil {
|
||||
errorlist = append(errorlist, err)
|
||||
continue
|
||||
}
|
||||
policyRules = append(policyRules, rules...)
|
||||
}
|
||||
}
|
||||
|
||||
if len(errorlist) != 0 {
|
||||
return policyRules, utilerrors.NewAggregate(errorlist)
|
||||
}
|
||||
return policyRules, nil
|
||||
}
|
||||
|
||||
func appliesTo(ctx api.Context, subjects []rbac.Subject) (bool, error) {
|
||||
user, ok := api.UserFrom(ctx)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("no user data associated with context")
|
||||
}
|
||||
for _, subject := range subjects {
|
||||
if ok, err := appliesToUser(user, subject); err != nil || ok {
|
||||
return ok, err
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func appliesToUser(user user.Info, subject rbac.Subject) (bool, error) {
|
||||
switch subject.Kind {
|
||||
case rbac.UserKind:
|
||||
return subject.Name == rbac.UserAll || user.GetName() == subject.Name, nil
|
||||
case rbac.GroupKind:
|
||||
return has(user.GetGroups(), subject.Name), nil
|
||||
case rbac.ServiceAccountKind:
|
||||
if subject.Namespace == "" {
|
||||
return false, fmt.Errorf("subject of kind service account without specified namespace")
|
||||
}
|
||||
// TODO(ericchiang): Is there a better way of matching a service account name?
|
||||
return "system:serviceaccount:"+subject.Name+":"+subject.Namespace == user.GetName(), nil
|
||||
default:
|
||||
return false, fmt.Errorf("unknown subject kind: %s", subject.Kind)
|
||||
}
|
||||
}
|
335
pkg/apis/rbac/validation/rulevalidation_test.go
Normal file
335
pkg/apis/rbac/validation/rulevalidation_test.go
Normal file
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 validation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"hash/fnv"
|
||||
"io"
|
||||
"reflect"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
"k8s.io/kubernetes/pkg/util/diff"
|
||||
)
|
||||
|
||||
func newMockRuleResolver(r *staticRoles) AuthorizationRuleResolver {
|
||||
return NewDefaultRuleResolver(r, r, r, r)
|
||||
}
|
||||
|
||||
type staticRoles struct {
|
||||
roles []rbac.Role
|
||||
roleBindings []rbac.RoleBinding
|
||||
clusterRoles []rbac.ClusterRole
|
||||
clusterRoleBindings []rbac.ClusterRoleBinding
|
||||
}
|
||||
|
||||
func (r *staticRoles) GetRole(ctx api.Context, id string) (*rbac.Role, error) {
|
||||
namespace, ok := api.NamespaceFrom(ctx)
|
||||
if !ok || namespace == "" {
|
||||
return nil, errors.New("must provide namespace when getting role")
|
||||
}
|
||||
for _, role := range r.roles {
|
||||
if role.Namespace == namespace && role.Name == id {
|
||||
return &role, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *staticRoles) GetClusterRole(ctx api.Context, id string) (*rbac.ClusterRole, error) {
|
||||
namespace, ok := api.NamespaceFrom(ctx)
|
||||
if ok && namespace != "" {
|
||||
return nil, errors.New("cannot provide namespace when getting cluster role")
|
||||
}
|
||||
for _, clusterRole := range r.clusterRoles {
|
||||
if clusterRole.Namespace == namespace && clusterRole.Name == id {
|
||||
return &clusterRole, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("role not found")
|
||||
}
|
||||
|
||||
func (r *staticRoles) ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) {
|
||||
namespace, ok := api.NamespaceFrom(ctx)
|
||||
if !ok || namespace == "" {
|
||||
return nil, errors.New("must provide namespace when listing role bindings")
|
||||
}
|
||||
|
||||
roleBindingList := new(rbac.RoleBindingList)
|
||||
for _, roleBinding := range r.roleBindings {
|
||||
if roleBinding.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
// TODO(ericchiang): need to implement label selectors?
|
||||
roleBindingList.Items = append(roleBindingList.Items, roleBinding)
|
||||
}
|
||||
return roleBindingList, nil
|
||||
}
|
||||
|
||||
func (r *staticRoles) ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
|
||||
namespace, ok := api.NamespaceFrom(ctx)
|
||||
if ok && namespace != "" {
|
||||
return nil, errors.New("cannot list cluster role bindings from within a namespace")
|
||||
}
|
||||
clusterRoleBindings := new(rbac.ClusterRoleBindingList)
|
||||
clusterRoleBindings.Items = make([]rbac.ClusterRoleBinding, len(r.clusterRoleBindings))
|
||||
copy(clusterRoleBindings.Items, r.clusterRoleBindings)
|
||||
return clusterRoleBindings, nil
|
||||
}
|
||||
|
||||
// compute a hash of a policy rule so we can sort in a deterministic order
|
||||
func hashOf(p rbac.PolicyRule) string {
|
||||
hash := fnv.New32()
|
||||
writeStrings := func(slis ...[]string) {
|
||||
for _, sli := range slis {
|
||||
for _, s := range sli {
|
||||
io.WriteString(hash, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
writeStrings(p.Verbs, p.APIGroups, p.Resources, p.ResourceNames, p.NonResourceURLs)
|
||||
return string(hash.Sum(nil))
|
||||
}
|
||||
|
||||
// byHash sorts a set of policy rules by a hash of its fields
|
||||
type byHash []rbac.PolicyRule
|
||||
|
||||
func (b byHash) Len() int { return len(b) }
|
||||
func (b byHash) Less(i, j int) bool { return hashOf(b[i]) < hashOf(b[j]) }
|
||||
func (b byHash) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
|
||||
|
||||
func TestDefaultRuleResolver(t *testing.T) {
|
||||
ruleReadPods := rbac.PolicyRule{
|
||||
Verbs: []string{"GET", "WATCH"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"pods"},
|
||||
}
|
||||
ruleReadServices := rbac.PolicyRule{
|
||||
Verbs: []string{"GET", "WATCH"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"services"},
|
||||
}
|
||||
ruleWriteNodes := rbac.PolicyRule{
|
||||
Verbs: []string{"PUT", "CREATE", "UPDATE"},
|
||||
APIGroups: []string{"v1"},
|
||||
Resources: []string{"nodes"},
|
||||
}
|
||||
ruleAdmin := rbac.PolicyRule{
|
||||
Verbs: []string{"*"},
|
||||
APIGroups: []string{"*"},
|
||||
Resources: []string{"*"},
|
||||
}
|
||||
|
||||
staticRoles1 := staticRoles{
|
||||
roles: []rbac.Role{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: "namespace1", Name: "readthings"},
|
||||
Rules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
},
|
||||
clusterRoles: []rbac.ClusterRole{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "cluster-admin"},
|
||||
Rules: []rbac.PolicyRule{ruleAdmin},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "write-nodes"},
|
||||
Rules: []rbac.PolicyRule{ruleWriteNodes},
|
||||
},
|
||||
},
|
||||
roleBindings: []rbac.RoleBinding{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: "namespace1"},
|
||||
Subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
{Kind: rbac.GroupKind, Name: "group1"},
|
||||
},
|
||||
RoleRef: api.ObjectReference{Kind: "Role", Namespace: "namespace1", Name: "readthings"},
|
||||
},
|
||||
},
|
||||
clusterRoleBindings: []rbac.ClusterRoleBinding{
|
||||
{
|
||||
Subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "admin"},
|
||||
{Kind: rbac.GroupKind, Name: "admin"},
|
||||
},
|
||||
RoleRef: api.ObjectReference{Kind: "ClusterRole", Name: "cluster-admin"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
staticRoles
|
||||
|
||||
// For a given context, what are the rules that apply?
|
||||
ctx api.Context
|
||||
effectiveRules []rbac.PolicyRule
|
||||
}{
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
ctx: api.WithNamespace(
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace1",
|
||||
),
|
||||
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
ctx: api.WithNamespace(
|
||||
// Same as above but diffrerent namespace. Should return no rules.
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}), "namespace2",
|
||||
),
|
||||
effectiveRules: []rbac.PolicyRule{},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
// GetEffectivePolicyRules only returns the policies for the namespace, not the master namespace.
|
||||
ctx: api.WithNamespace(
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{
|
||||
Name: "foobar", Groups: []string{"admin"},
|
||||
}), "namespace1",
|
||||
),
|
||||
effectiveRules: []rbac.PolicyRule{ruleReadPods, ruleReadServices},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
// Same as above but without a namespace. Only cluster rules should apply.
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{
|
||||
Name: "foobar", Groups: []string{"admin"},
|
||||
}),
|
||||
effectiveRules: []rbac.PolicyRule{ruleAdmin},
|
||||
},
|
||||
{
|
||||
staticRoles: staticRoles1,
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{}),
|
||||
effectiveRules: []rbac.PolicyRule{},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tc := range tests {
|
||||
ruleResolver := newMockRuleResolver(&tc.staticRoles)
|
||||
rules, err := ruleResolver.GetEffectivePolicyRules(tc.ctx)
|
||||
if err != nil {
|
||||
t.Errorf("case %d: GetEffectivePolicyRules(context)=%v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Sort for deep equals
|
||||
sort.Sort(byHash(rules))
|
||||
sort.Sort(byHash(tc.effectiveRules))
|
||||
|
||||
if !reflect.DeepEqual(rules, tc.effectiveRules) {
|
||||
ruleDiff := diff.ObjectDiff(rules, tc.effectiveRules)
|
||||
t.Errorf("case %d: %s", i, ruleDiff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppliesTo(t *testing.T) {
|
||||
tests := []struct {
|
||||
subjects []rbac.Subject
|
||||
ctx api.Context
|
||||
appliesTo bool
|
||||
testCase string
|
||||
}{
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
|
||||
appliesTo: true,
|
||||
testCase: "single subject that matches username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that matches username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.UserKind, Name: "foobar"},
|
||||
},
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam"}),
|
||||
appliesTo: false,
|
||||
testCase: "multiple subjects, none that match username",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
},
|
||||
ctx: api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that match group",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
},
|
||||
ctx: api.WithNamespace(
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "zimzam", Groups: []string{"foobar"}}),
|
||||
"namespace1",
|
||||
),
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects, one that match group, should ignore namespace",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "barfoo"},
|
||||
{Kind: rbac.GroupKind, Name: "foobar"},
|
||||
{Kind: rbac.ServiceAccountKind, Name: "kube-system", Namespace: "default"},
|
||||
},
|
||||
ctx: api.WithNamespace(
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "system:serviceaccount:kube-system:default"}),
|
||||
"default",
|
||||
),
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects with a service account that matches",
|
||||
},
|
||||
{
|
||||
subjects: []rbac.Subject{
|
||||
{Kind: rbac.UserKind, Name: "*"},
|
||||
},
|
||||
ctx: api.WithNamespace(
|
||||
api.WithUser(api.NewContext(), &user.DefaultInfo{Name: "foobar"}),
|
||||
"default",
|
||||
),
|
||||
appliesTo: true,
|
||||
testCase: "multiple subjects with a service account that matches",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
got, err := appliesTo(tc.ctx, tc.subjects)
|
||||
if err != nil {
|
||||
t.Errorf("case %q %v", tc.testCase, err)
|
||||
continue
|
||||
}
|
||||
if got != tc.appliesTo {
|
||||
t.Errorf("case %q want appliesTo=%t, got appliesTo=%t", tc.testCase, tc.appliesTo, got)
|
||||
}
|
||||
}
|
||||
}
|
@@ -29,73 +29,65 @@ func minimalNameRequirements(name string, prefix bool) []string {
|
||||
return validation.IsValidPathSegmentName(name)
|
||||
}
|
||||
|
||||
func ValidateLocalRole(policy *rbac.Role) field.ErrorList {
|
||||
return ValidateRole(policy, true)
|
||||
func ValidateRole(policy *rbac.Role) field.ErrorList {
|
||||
return validateRole(policy, true)
|
||||
}
|
||||
|
||||
func ValidateLocalRoleUpdate(policy *rbac.Role, oldRole *rbac.Role) field.ErrorList {
|
||||
return ValidateRoleUpdate(policy, oldRole, true)
|
||||
func ValidateRoleUpdate(policy *rbac.Role, oldRole *rbac.Role) field.ErrorList {
|
||||
return validateRoleUpdate(policy, oldRole, true)
|
||||
}
|
||||
|
||||
func ValidateClusterRole(policy *rbac.ClusterRole) field.ErrorList {
|
||||
return ValidateRole(toRole(policy), false)
|
||||
return validateRole(toRole(policy), false)
|
||||
}
|
||||
|
||||
func ValidateClusterRoleUpdate(policy *rbac.ClusterRole, oldRole *rbac.ClusterRole) field.ErrorList {
|
||||
return ValidateRoleUpdate(toRole(policy), toRole(oldRole), false)
|
||||
return validateRoleUpdate(toRole(policy), toRole(oldRole), false)
|
||||
}
|
||||
|
||||
func ValidateRole(role *rbac.Role, isNamespaced bool) field.ErrorList {
|
||||
return validateRole(role, isNamespaced, nil)
|
||||
func validateRole(role *rbac.Role, isNamespaced bool) field.ErrorList {
|
||||
return validation.ValidateObjectMeta(&role.ObjectMeta, isNamespaced, minimalNameRequirements, field.NewPath("metadata"))
|
||||
}
|
||||
|
||||
func validateRole(role *rbac.Role, isNamespaced bool, fldPath *field.Path) field.ErrorList {
|
||||
return validation.ValidateObjectMeta(&role.ObjectMeta, isNamespaced, minimalNameRequirements, fldPath.Child("metadata"))
|
||||
}
|
||||
|
||||
func ValidateRoleUpdate(role *rbac.Role, oldRole *rbac.Role, isNamespaced bool) field.ErrorList {
|
||||
allErrs := ValidateRole(role, isNamespaced)
|
||||
func validateRoleUpdate(role *rbac.Role, oldRole *rbac.Role, isNamespaced bool) field.ErrorList {
|
||||
allErrs := validateRole(role, isNamespaced)
|
||||
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&role.ObjectMeta, &oldRole.ObjectMeta, field.NewPath("metadata"))...)
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateLocalRoleBinding(policy *rbac.RoleBinding) field.ErrorList {
|
||||
return ValidateRoleBinding(policy, true)
|
||||
func ValidateRoleBinding(policy *rbac.RoleBinding) field.ErrorList {
|
||||
return validateRoleBinding(policy, true)
|
||||
}
|
||||
|
||||
func ValidateLocalRoleBindingUpdate(policy *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList {
|
||||
return ValidateRoleBindingUpdate(policy, oldRoleBinding, true)
|
||||
func ValidateRoleBindingUpdate(policy *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding) field.ErrorList {
|
||||
return validateRoleBindingUpdate(policy, oldRoleBinding, true)
|
||||
}
|
||||
|
||||
func ValidateClusterRoleBinding(policy *rbac.ClusterRoleBinding) field.ErrorList {
|
||||
return ValidateRoleBinding(toRoleBinding(policy), false)
|
||||
return validateRoleBinding(toRoleBinding(policy), false)
|
||||
}
|
||||
|
||||
func ValidateClusterRoleBindingUpdate(policy *rbac.ClusterRoleBinding, oldRoleBinding *rbac.ClusterRoleBinding) field.ErrorList {
|
||||
return ValidateRoleBindingUpdate(toRoleBinding(policy), toRoleBinding(oldRoleBinding), false)
|
||||
return validateRoleBindingUpdate(toRoleBinding(policy), toRoleBinding(oldRoleBinding), false)
|
||||
}
|
||||
|
||||
func ValidateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
|
||||
return validateRoleBinding(roleBinding, isNamespaced, nil)
|
||||
}
|
||||
|
||||
func validateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool, fldPath *field.Path) field.ErrorList {
|
||||
func validateRoleBinding(roleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, isNamespaced, minimalNameRequirements, fldPath.Child("metadata"))...)
|
||||
allErrs = append(allErrs, validation.ValidateObjectMeta(&roleBinding.ObjectMeta, isNamespaced, minimalNameRequirements, field.NewPath("metadata"))...)
|
||||
|
||||
// roleRef namespace is empty when referring to global policy.
|
||||
if len(roleBinding.RoleRef.Namespace) > 0 {
|
||||
for _, msg := range validation.ValidateNamespaceName(roleBinding.RoleRef.Namespace, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg))
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "namespace"), roleBinding.RoleRef.Namespace, msg))
|
||||
}
|
||||
}
|
||||
|
||||
if len(roleBinding.RoleRef.Name) == 0 {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("roleRef", "name"), ""))
|
||||
allErrs = append(allErrs, field.Required(field.NewPath("roleRef", "name"), ""))
|
||||
} else {
|
||||
for _, msg := range minimalNameRequirements(roleBinding.RoleRef.Name, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("roleRef", "name"), roleBinding.RoleRef.Name, msg))
|
||||
allErrs = append(allErrs, field.Invalid(field.NewPath("roleRef", "name"), roleBinding.RoleRef.Name, msg))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,8 +139,8 @@ func validateRoleBindingSubject(subject rbac.Subject, isNamespaced bool, fldPath
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func ValidateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
|
||||
allErrs := ValidateRoleBinding(roleBinding, isNamespaced)
|
||||
func validateRoleBindingUpdate(roleBinding *rbac.RoleBinding, oldRoleBinding *rbac.RoleBinding, isNamespaced bool) field.ErrorList {
|
||||
allErrs := validateRoleBinding(roleBinding, isNamespaced)
|
||||
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(&roleBinding.ObjectMeta, &oldRoleBinding.ObjectMeta, field.NewPath("metadata"))...)
|
||||
|
||||
if oldRoleBinding.RoleRef != roleBinding.RoleRef {
|
||||
|
@@ -25,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
func TestValidateRoleBinding(t *testing.T) {
|
||||
errs := ValidateRoleBinding(
|
||||
errs := validateRoleBinding(
|
||||
&rbac.RoleBinding{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
|
||||
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
|
||||
@@ -116,7 +116,7 @@ func TestValidateRoleBinding(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateRoleBinding(&v.A, true)
|
||||
errs := validateRoleBinding(&v.A, true)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure %s for %v", k, v.A)
|
||||
continue
|
||||
@@ -138,7 +138,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
|
||||
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
|
||||
}
|
||||
|
||||
errs := ValidateRoleBindingUpdate(
|
||||
errs := validateRoleBindingUpdate(
|
||||
&rbac.RoleBinding{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master", ResourceVersion: "1"},
|
||||
RoleRef: api.ObjectReference{Namespace: "master", Name: "valid"},
|
||||
@@ -165,7 +165,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateRoleBindingUpdate(&v.A, old, true)
|
||||
errs := validateRoleBindingUpdate(&v.A, old, true)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure %s for %v", k, v.A)
|
||||
continue
|
||||
@@ -182,7 +182,7 @@ func TestValidateRoleBindingUpdate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestValidateRole(t *testing.T) {
|
||||
errs := ValidateRole(
|
||||
errs := validateRole(
|
||||
&rbac.Role{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault, Name: "master"},
|
||||
},
|
||||
@@ -213,7 +213,7 @@ func TestValidateRole(t *testing.T) {
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := ValidateRole(&v.A, true)
|
||||
errs := validateRole(&v.A, true)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("expected failure %s for %v", k, v.A)
|
||||
continue
|
||||
|
@@ -24,6 +24,11 @@ import (
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer/abac"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer/union"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/rbac"
|
||||
"k8s.io/kubernetes/plugin/pkg/auth/authorizer/webhook"
|
||||
)
|
||||
|
||||
@@ -63,10 +68,11 @@ const (
|
||||
ModeAlwaysDeny string = "AlwaysDeny"
|
||||
ModeABAC string = "ABAC"
|
||||
ModeWebhook string = "Webhook"
|
||||
ModeRBAC string = "RBAC"
|
||||
)
|
||||
|
||||
// Keep this list in sync with constant list above.
|
||||
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook}
|
||||
var AuthorizationModeChoices = []string{ModeAlwaysAllow, ModeAlwaysDeny, ModeABAC, ModeWebhook, ModeRBAC}
|
||||
|
||||
type AuthorizationConfig struct {
|
||||
// Options for ModeABAC
|
||||
@@ -82,6 +88,16 @@ type AuthorizationConfig struct {
|
||||
WebhookCacheAuthorizedTTL time.Duration
|
||||
// TTL for caching of unauthorized responses from the webhook server.
|
||||
WebhookCacheUnauthorizedTTL time.Duration
|
||||
|
||||
// Options for RBAC
|
||||
|
||||
// User which can bootstrap role policies
|
||||
RBACSuperUser string
|
||||
|
||||
RBACClusterRoleRegistry clusterrole.Registry
|
||||
RBACClusterRoleBindingRegistry clusterrolebinding.Registry
|
||||
RBACRoleRegistry role.Registry
|
||||
RBACRoleBindingRegistry rolebinding.Registry
|
||||
}
|
||||
|
||||
// NewAuthorizerFromAuthorizationConfig returns the right sort of union of multiple authorizer.Authorizer objects
|
||||
@@ -126,6 +142,18 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au
|
||||
return nil, err
|
||||
}
|
||||
authorizers = append(authorizers, webhookAuthorizer)
|
||||
case ModeRBAC:
|
||||
rbacAuthorizer, err := rbac.New(
|
||||
config.RBACRoleRegistry,
|
||||
config.RBACRoleBindingRegistry,
|
||||
config.RBACClusterRoleRegistry,
|
||||
config.RBACClusterRoleBindingRegistry,
|
||||
config.RBACSuperUser,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authorizers = append(authorizers, rbacAuthorizer)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unknown authorization mode %s specified", authorizationMode)
|
||||
}
|
||||
@@ -138,6 +166,9 @@ func NewAuthorizerFromAuthorizationConfig(authorizationModes []string, config Au
|
||||
if !authorizerMap[ModeWebhook] && config.WebhookConfigFile != "" {
|
||||
return nil, errors.New("Cannot specify --authorization-webhook-config-file without mode Webhook")
|
||||
}
|
||||
if !authorizerMap[ModeRBAC] && config.RBACSuperUser != "" {
|
||||
return nil, errors.New("Cannot specify --authorization-rbac-super-user without mode RBAC")
|
||||
}
|
||||
|
||||
return union.New(authorizers...), nil
|
||||
}
|
||||
|
@@ -22,6 +22,7 @@ import (
|
||||
unversionedbatch "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/unversioned"
|
||||
unversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned"
|
||||
unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
|
||||
unversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
|
||||
restclient "k8s.io/kubernetes/pkg/client/restclient"
|
||||
discovery "k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/util/flowcontrol"
|
||||
@@ -33,6 +34,7 @@ type Interface interface {
|
||||
Extensions() unversionedextensions.ExtensionsInterface
|
||||
Autoscaling() unversionedautoscaling.AutoscalingInterface
|
||||
Batch() unversionedbatch.BatchInterface
|
||||
Rbac() unversionedrbac.RbacInterface
|
||||
}
|
||||
|
||||
// Clientset contains the clients for groups. Each group has exactly one
|
||||
@@ -43,6 +45,7 @@ type Clientset struct {
|
||||
*unversionedextensions.ExtensionsClient
|
||||
*unversionedautoscaling.AutoscalingClient
|
||||
*unversionedbatch.BatchClient
|
||||
*unversionedrbac.RbacClient
|
||||
}
|
||||
|
||||
// Core retrieves the CoreClient
|
||||
@@ -77,6 +80,14 @@ func (c *Clientset) Batch() unversionedbatch.BatchInterface {
|
||||
return c.BatchClient
|
||||
}
|
||||
|
||||
// Rbac retrieves the RbacClient
|
||||
func (c *Clientset) Rbac() unversionedrbac.RbacInterface {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.RbacClient
|
||||
}
|
||||
|
||||
// Discovery retrieves the DiscoveryClient
|
||||
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
|
||||
return c.DiscoveryClient
|
||||
@@ -106,6 +117,10 @@ func NewForConfig(c *restclient.Config) (*Clientset, error) {
|
||||
if err != nil {
|
||||
return &clientset, err
|
||||
}
|
||||
clientset.RbacClient, err = unversionedrbac.NewForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
return &clientset, err
|
||||
}
|
||||
|
||||
clientset.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy)
|
||||
if err != nil {
|
||||
@@ -122,6 +137,7 @@ func NewForConfigOrDie(c *restclient.Config) *Clientset {
|
||||
clientset.ExtensionsClient = unversionedextensions.NewForConfigOrDie(c)
|
||||
clientset.AutoscalingClient = unversionedautoscaling.NewForConfigOrDie(c)
|
||||
clientset.BatchClient = unversionedbatch.NewForConfigOrDie(c)
|
||||
clientset.RbacClient = unversionedrbac.NewForConfigOrDie(c)
|
||||
|
||||
clientset.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c)
|
||||
return &clientset
|
||||
@@ -134,6 +150,7 @@ func New(c *restclient.RESTClient) *Clientset {
|
||||
clientset.ExtensionsClient = unversionedextensions.New(c)
|
||||
clientset.AutoscalingClient = unversionedautoscaling.New(c)
|
||||
clientset.BatchClient = unversionedbatch.New(c)
|
||||
clientset.RbacClient = unversionedrbac.New(c)
|
||||
|
||||
clientset.DiscoveryClient = discovery.NewDiscoveryClient(c)
|
||||
return &clientset
|
||||
|
@@ -28,6 +28,8 @@ import (
|
||||
fakeunversionedcore "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/unversioned/fake"
|
||||
unversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned"
|
||||
fakeunversionedextensions "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned/fake"
|
||||
unversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
|
||||
fakeunversionedrbac "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned/fake"
|
||||
"k8s.io/kubernetes/pkg/client/testing/core"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
fakediscovery "k8s.io/kubernetes/pkg/client/typed/discovery/fake"
|
||||
@@ -84,3 +86,8 @@ func (c *Clientset) Autoscaling() unversionedautoscaling.AutoscalingInterface {
|
||||
func (c *Clientset) Batch() unversionedbatch.BatchInterface {
|
||||
return &fakeunversionedbatch.FakeBatch{Fake: &c.Fake}
|
||||
}
|
||||
|
||||
// Rbac retrieves the RbacClient
|
||||
func (c *Clientset) Rbac() unversionedrbac.RbacInterface {
|
||||
return &fakeunversionedrbac.FakeRbac{Fake: &c.Fake}
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/metrics/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/policy/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// ClusterRolesGetter has a method to return a ClusterRoleInterface.
|
||||
// A group's client should implement this interface.
|
||||
type ClusterRolesGetter interface {
|
||||
ClusterRoles() ClusterRoleInterface
|
||||
}
|
||||
|
||||
// ClusterRoleInterface has methods to work with ClusterRole resources.
|
||||
type ClusterRoleInterface interface {
|
||||
Create(*rbac.ClusterRole) (*rbac.ClusterRole, error)
|
||||
Update(*rbac.ClusterRole) (*rbac.ClusterRole, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
|
||||
Get(name string) (*rbac.ClusterRole, error)
|
||||
List(opts api.ListOptions) (*rbac.ClusterRoleList, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
ClusterRoleExpansion
|
||||
}
|
||||
|
||||
// clusterRoles implements ClusterRoleInterface
|
||||
type clusterRoles struct {
|
||||
client *RbacClient
|
||||
}
|
||||
|
||||
// newClusterRoles returns a ClusterRoles
|
||||
func newClusterRoles(c *RbacClient) *clusterRoles {
|
||||
return &clusterRoles{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// Create takes the representation of a clusterRole and creates it. Returns the server's representation of the clusterRole, and an error, if there is any.
|
||||
func (c *clusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Post().
|
||||
Resource("clusterroles").
|
||||
Body(clusterRole).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a clusterRole and updates it. Returns the server's representation of the clusterRole, and an error, if there is any.
|
||||
func (c *clusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Put().
|
||||
Resource("clusterroles").
|
||||
Name(clusterRole.Name).
|
||||
Body(clusterRole).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the clusterRole and deletes it. Returns an error if one occurs.
|
||||
func (c *clusterRoles) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Resource("clusterroles").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *clusterRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
return c.client.Delete().
|
||||
Resource("clusterroles").
|
||||
VersionedParams(&listOptions, api.ParameterCodec).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Get takes name of the clusterRole, and returns the corresponding clusterRole object, and an error if there is any.
|
||||
func (c *clusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Get().
|
||||
Resource("clusterroles").
|
||||
Name(name).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ClusterRoles that match those selectors.
|
||||
func (c *clusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
|
||||
result = &rbac.ClusterRoleList{}
|
||||
err = c.client.Get().
|
||||
Resource("clusterroles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoles.
|
||||
func (c *clusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Resource("clusterroles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// ClusterRoleBindingsGetter has a method to return a ClusterRoleBindingInterface.
|
||||
// A group's client should implement this interface.
|
||||
type ClusterRoleBindingsGetter interface {
|
||||
ClusterRoleBindings() ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
// ClusterRoleBindingInterface has methods to work with ClusterRoleBinding resources.
|
||||
type ClusterRoleBindingInterface interface {
|
||||
Create(*rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
|
||||
Update(*rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
|
||||
Get(name string) (*rbac.ClusterRoleBinding, error)
|
||||
List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
ClusterRoleBindingExpansion
|
||||
}
|
||||
|
||||
// clusterRoleBindings implements ClusterRoleBindingInterface
|
||||
type clusterRoleBindings struct {
|
||||
client *RbacClient
|
||||
}
|
||||
|
||||
// newClusterRoleBindings returns a ClusterRoleBindings
|
||||
func newClusterRoleBindings(c *RbacClient) *clusterRoleBindings {
|
||||
return &clusterRoleBindings{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// Create takes the representation of a clusterRoleBinding and creates it. Returns the server's representation of the clusterRoleBinding, and an error, if there is any.
|
||||
func (c *clusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Post().
|
||||
Resource("clusterrolebindings").
|
||||
Body(clusterRoleBinding).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a clusterRoleBinding and updates it. Returns the server's representation of the clusterRoleBinding, and an error, if there is any.
|
||||
func (c *clusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Put().
|
||||
Resource("clusterrolebindings").
|
||||
Name(clusterRoleBinding.Name).
|
||||
Body(clusterRoleBinding).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the clusterRoleBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *clusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Resource("clusterrolebindings").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *clusterRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
return c.client.Delete().
|
||||
Resource("clusterrolebindings").
|
||||
VersionedParams(&listOptions, api.ParameterCodec).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Get takes name of the clusterRoleBinding, and returns the corresponding clusterRoleBinding object, and an error if there is any.
|
||||
func (c *clusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Get().
|
||||
Resource("clusterrolebindings").
|
||||
Name(name).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of ClusterRoleBindings that match those selectors.
|
||||
func (c *clusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
|
||||
result = &rbac.ClusterRoleBindingList{}
|
||||
err = c.client.Get().
|
||||
Resource("clusterrolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
|
||||
func (c *clusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Resource("clusterrolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// This package is generated by client-gen with the default arguments.
|
||||
|
||||
// This package has the automatically generated typed clients.
|
||||
package unversioned
|
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
// This package is generated by client-gen with the default arguments.
|
||||
|
||||
// Package fake has the automatically generated clients.
|
||||
package fake
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 fake
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/testing/core"
|
||||
labels "k8s.io/kubernetes/pkg/labels"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeClusterRoles implements ClusterRoleInterface
|
||||
type FakeClusterRoles struct {
|
||||
Fake *FakeRbac
|
||||
}
|
||||
|
||||
var clusterrolesResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "clusterroles"}
|
||||
|
||||
func (c *FakeClusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootCreateAction(clusterrolesResource, clusterRole), &rbac.ClusterRole{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootUpdateAction(clusterrolesResource, clusterRole), &rbac.ClusterRole{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Delete(name string, options *api.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(core.NewRootDeleteAction(clusterrolesResource, name), &rbac.ClusterRole{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
action := core.NewRootDeleteCollectionAction(clusterrolesResource, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &rbac.ClusterRoleList{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootGetAction(clusterrolesResource, name), &rbac.ClusterRole{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootListAction(clusterrolesResource, opts), &rbac.ClusterRoleList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label := opts.LabelSelector
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &rbac.ClusterRoleList{}
|
||||
for _, item := range obj.(*rbac.ClusterRoleList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoles.
|
||||
func (c *FakeClusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(core.NewRootWatchAction(clusterrolesResource, opts))
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 fake
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/testing/core"
|
||||
labels "k8s.io/kubernetes/pkg/labels"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeClusterRoleBindings implements ClusterRoleBindingInterface
|
||||
type FakeClusterRoleBindings struct {
|
||||
Fake *FakeRbac
|
||||
}
|
||||
|
||||
var clusterrolebindingsResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "clusterrolebindings"}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootCreateAction(clusterrolebindingsResource, clusterRoleBinding), &rbac.ClusterRoleBinding{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootUpdateAction(clusterrolebindingsResource, clusterRoleBinding), &rbac.ClusterRoleBinding{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(core.NewRootDeleteAction(clusterrolebindingsResource, name), &rbac.ClusterRoleBinding{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
action := core.NewRootDeleteCollectionAction(clusterrolebindingsResource, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &rbac.ClusterRoleBindingList{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootGetAction(clusterrolebindingsResource, name), &rbac.ClusterRoleBinding{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewRootListAction(clusterrolebindingsResource, opts), &rbac.ClusterRoleBindingList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label := opts.LabelSelector
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &rbac.ClusterRoleBindingList{}
|
||||
for _, item := range obj.(*rbac.ClusterRoleBindingList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
|
||||
func (c *FakeClusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(core.NewRootWatchAction(clusterrolebindingsResource, opts))
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 fake
|
||||
|
||||
import (
|
||||
unversioned "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned"
|
||||
restclient "k8s.io/kubernetes/pkg/client/restclient"
|
||||
core "k8s.io/kubernetes/pkg/client/testing/core"
|
||||
)
|
||||
|
||||
type FakeRbac struct {
|
||||
*core.Fake
|
||||
}
|
||||
|
||||
func (c *FakeRbac) ClusterRoles() unversioned.ClusterRoleInterface {
|
||||
return &FakeClusterRoles{c}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) ClusterRoleBindings() unversioned.ClusterRoleBindingInterface {
|
||||
return &FakeClusterRoleBindings{c}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) Roles(namespace string) unversioned.RoleInterface {
|
||||
return &FakeRoles{c, namespace}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) RoleBindings(namespace string) unversioned.RoleBindingInterface {
|
||||
return &FakeRoleBindings{c, namespace}
|
||||
}
|
||||
|
||||
// GetRESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *FakeRbac) GetRESTClient() *restclient.RESTClient {
|
||||
return nil
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 fake
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/testing/core"
|
||||
labels "k8s.io/kubernetes/pkg/labels"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeRoles implements RoleInterface
|
||||
type FakeRoles struct {
|
||||
Fake *FakeRbac
|
||||
ns string
|
||||
}
|
||||
|
||||
var rolesResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "roles"}
|
||||
|
||||
func (c *FakeRoles) Create(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewCreateAction(rolesResource, c.ns, role), &rbac.Role{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Update(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewUpdateAction(rolesResource, c.ns, role), &rbac.Role{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Delete(name string, options *api.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(core.NewDeleteAction(rolesResource, c.ns, name), &rbac.Role{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
action := core.NewDeleteCollectionAction(rolesResource, c.ns, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &rbac.RoleList{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Get(name string) (result *rbac.Role, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewGetAction(rolesResource, c.ns, name), &rbac.Role{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewListAction(rolesResource, c.ns, opts), &rbac.RoleList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label := opts.LabelSelector
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &rbac.RoleList{}
|
||||
for _, item := range obj.(*rbac.RoleList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roles.
|
||||
func (c *FakeRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(core.NewWatchAction(rolesResource, c.ns, opts))
|
||||
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 fake
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
unversioned "k8s.io/kubernetes/pkg/api/unversioned"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
core "k8s.io/kubernetes/pkg/client/testing/core"
|
||||
labels "k8s.io/kubernetes/pkg/labels"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeRoleBindings implements RoleBindingInterface
|
||||
type FakeRoleBindings struct {
|
||||
Fake *FakeRbac
|
||||
ns string
|
||||
}
|
||||
|
||||
var rolebindingsResource = unversioned.GroupVersionResource{Group: "rbac.authorization.k8s.io", Version: "", Resource: "rolebindings"}
|
||||
|
||||
func (c *FakeRoleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewCreateAction(rolebindingsResource, c.ns, roleBinding), &rbac.RoleBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewUpdateAction(rolebindingsResource, c.ns, roleBinding), &rbac.RoleBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
_, err := c.Fake.
|
||||
Invokes(core.NewDeleteAction(rolebindingsResource, c.ns, name), &rbac.RoleBinding{})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
action := core.NewDeleteCollectionAction(rolebindingsResource, c.ns, listOptions)
|
||||
|
||||
_, err := c.Fake.Invokes(action, &rbac.RoleBindingList{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewGetAction(rolebindingsResource, c.ns, name), &rbac.RoleBinding{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
|
||||
obj, err := c.Fake.
|
||||
Invokes(core.NewListAction(rolebindingsResource, c.ns, opts), &rbac.RoleBindingList{})
|
||||
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
label := opts.LabelSelector
|
||||
if label == nil {
|
||||
label = labels.Everything()
|
||||
}
|
||||
list := &rbac.RoleBindingList{}
|
||||
for _, item := range obj.(*rbac.RoleBindingList).Items {
|
||||
if label.Matches(labels.Set(item.Labels)) {
|
||||
list.Items = append(list.Items, item)
|
||||
}
|
||||
}
|
||||
return list, err
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roleBindings.
|
||||
func (c *FakeRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.
|
||||
InvokesWatch(core.NewWatchAction(rolebindingsResource, c.ns, opts))
|
||||
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
type ClusterRoleExpansion interface{}
|
||||
|
||||
type ClusterRoleBindingExpansion interface{}
|
||||
|
||||
type RoleExpansion interface{}
|
||||
|
||||
type RoleBindingExpansion interface{}
|
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
registered "k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
restclient "k8s.io/kubernetes/pkg/client/restclient"
|
||||
)
|
||||
|
||||
type RbacInterface interface {
|
||||
GetRESTClient() *restclient.RESTClient
|
||||
ClusterRolesGetter
|
||||
ClusterRoleBindingsGetter
|
||||
RolesGetter
|
||||
RoleBindingsGetter
|
||||
}
|
||||
|
||||
// RbacClient is used to interact with features provided by the Rbac group.
|
||||
type RbacClient struct {
|
||||
*restclient.RESTClient
|
||||
}
|
||||
|
||||
func (c *RbacClient) ClusterRoles() ClusterRoleInterface {
|
||||
return newClusterRoles(c)
|
||||
}
|
||||
|
||||
func (c *RbacClient) ClusterRoleBindings() ClusterRoleBindingInterface {
|
||||
return newClusterRoleBindings(c)
|
||||
}
|
||||
|
||||
func (c *RbacClient) Roles(namespace string) RoleInterface {
|
||||
return newRoles(c, namespace)
|
||||
}
|
||||
|
||||
func (c *RbacClient) RoleBindings(namespace string) RoleBindingInterface {
|
||||
return newRoleBindings(c, namespace)
|
||||
}
|
||||
|
||||
// NewForConfig creates a new RbacClient for the given config.
|
||||
func NewForConfig(c *restclient.Config) (*RbacClient, error) {
|
||||
config := *c
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := restclient.RESTClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RbacClient{client}, nil
|
||||
}
|
||||
|
||||
// NewForConfigOrDie creates a new RbacClient for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewForConfigOrDie(c *restclient.Config) *RbacClient {
|
||||
client, err := NewForConfig(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
// New creates a new RbacClient for the given RESTClient.
|
||||
func New(c *restclient.RESTClient) *RbacClient {
|
||||
return &RbacClient{c}
|
||||
}
|
||||
|
||||
func setConfigDefaults(config *restclient.Config) error {
|
||||
// if rbac group is not registered, return an error
|
||||
g, err := registered.Group("rbac.authorization.k8s.io")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.APIPath = "/apis"
|
||||
if config.UserAgent == "" {
|
||||
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
||||
}
|
||||
// TODO: Unconditionally set the config.Version, until we fix the config.
|
||||
//if config.Version == "" {
|
||||
copyGroupVersion := g.GroupVersion
|
||||
config.GroupVersion = ©GroupVersion
|
||||
//}
|
||||
|
||||
config.NegotiatedSerializer = api.Codecs
|
||||
|
||||
if config.QPS == 0 {
|
||||
config.QPS = 5
|
||||
}
|
||||
if config.Burst == 0 {
|
||||
config.Burst = 10
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetRESTClient returns a RESTClient that is used to communicate
|
||||
// with API server by this client implementation.
|
||||
func (c *RbacClient) GetRESTClient() *restclient.RESTClient {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return c.RESTClient
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// RolesGetter has a method to return a RoleInterface.
|
||||
// A group's client should implement this interface.
|
||||
type RolesGetter interface {
|
||||
Roles(namespace string) RoleInterface
|
||||
}
|
||||
|
||||
// RoleInterface has methods to work with Role resources.
|
||||
type RoleInterface interface {
|
||||
Create(*rbac.Role) (*rbac.Role, error)
|
||||
Update(*rbac.Role) (*rbac.Role, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
|
||||
Get(name string) (*rbac.Role, error)
|
||||
List(opts api.ListOptions) (*rbac.RoleList, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
RoleExpansion
|
||||
}
|
||||
|
||||
// roles implements RoleInterface
|
||||
type roles struct {
|
||||
client *RbacClient
|
||||
ns string
|
||||
}
|
||||
|
||||
// newRoles returns a Roles
|
||||
func newRoles(c *RbacClient, namespace string) *roles {
|
||||
return &roles{
|
||||
client: c,
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if there is any.
|
||||
func (c *roles) Create(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
Body(role).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if there is any.
|
||||
func (c *roles) Update(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
Name(role.Name).
|
||||
Body(role).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the role and deletes it. Returns an error if one occurs.
|
||||
func (c *roles) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *roles) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
VersionedParams(&listOptions, api.ParameterCodec).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Get takes name of the role, and returns the corresponding role object, and an error if there is any.
|
||||
func (c *roles) Get(name string) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
Name(name).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of Roles that match those selectors.
|
||||
func (c *roles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
|
||||
result = &rbac.RoleList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roles.
|
||||
func (c *roles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
api "k8s.io/kubernetes/pkg/api"
|
||||
rbac "k8s.io/kubernetes/pkg/apis/rbac"
|
||||
watch "k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// RoleBindingsGetter has a method to return a RoleBindingInterface.
|
||||
// A group's client should implement this interface.
|
||||
type RoleBindingsGetter interface {
|
||||
RoleBindings(namespace string) RoleBindingInterface
|
||||
}
|
||||
|
||||
// RoleBindingInterface has methods to work with RoleBinding resources.
|
||||
type RoleBindingInterface interface {
|
||||
Create(*rbac.RoleBinding) (*rbac.RoleBinding, error)
|
||||
Update(*rbac.RoleBinding) (*rbac.RoleBinding, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
|
||||
Get(name string) (*rbac.RoleBinding, error)
|
||||
List(opts api.ListOptions) (*rbac.RoleBindingList, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
RoleBindingExpansion
|
||||
}
|
||||
|
||||
// roleBindings implements RoleBindingInterface
|
||||
type roleBindings struct {
|
||||
client *RbacClient
|
||||
ns string
|
||||
}
|
||||
|
||||
// newRoleBindings returns a RoleBindings
|
||||
func newRoleBindings(c *RbacClient, namespace string) *roleBindings {
|
||||
return &roleBindings{
|
||||
client: c,
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if there is any.
|
||||
func (c *roleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Post().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
Body(roleBinding).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if there is any.
|
||||
func (c *roleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Put().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
Name(roleBinding.Name).
|
||||
Body(roleBinding).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes name of the roleBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *roleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
Name(name).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// DeleteCollection deletes a collection of objects.
|
||||
func (c *roleBindings) DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error {
|
||||
return c.client.Delete().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
VersionedParams(&listOptions, api.ParameterCodec).
|
||||
Body(options).
|
||||
Do().
|
||||
Error()
|
||||
}
|
||||
|
||||
// Get takes name of the roleBinding, and returns the corresponding roleBinding object, and an error if there is any.
|
||||
func (c *roleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
Name(name).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of RoleBindings that match those selectors.
|
||||
func (c *roleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
|
||||
result = &rbac.RoleBindingList{}
|
||||
err = c.client.Get().
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Do().
|
||||
Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roleBindings.
|
||||
func (c *roleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -47,6 +47,7 @@ type Interface interface {
|
||||
Autoscaling() AutoscalingInterface
|
||||
Batch() BatchInterface
|
||||
Extensions() ExtensionsInterface
|
||||
Rbac() RbacInterface
|
||||
Discovery() discovery.DiscoveryInterface
|
||||
}
|
||||
|
||||
@@ -121,6 +122,7 @@ type Client struct {
|
||||
*ExtensionsClient
|
||||
*AppsClient
|
||||
*PolicyClient
|
||||
*RbacClient
|
||||
*discovery.DiscoveryClient
|
||||
}
|
||||
|
||||
@@ -162,6 +164,10 @@ func (c *Client) Apps() AppsInterface {
|
||||
return c.AppsClient
|
||||
}
|
||||
|
||||
func (c *Client) Rbac() RbacInterface {
|
||||
return c.RbacClient
|
||||
}
|
||||
|
||||
func (c *Client) Discovery() discovery.DiscoveryInterface {
|
||||
return c.DiscoveryClient
|
||||
}
|
||||
|
92
pkg/client/unversioned/clusterrolebindings.go
Normal file
92
pkg/client/unversioned/clusterrolebindings.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// ClusterRoleBindings has methods to work with ClusterRoleBinding resources in a namespace
|
||||
type ClusterRoleBindings interface {
|
||||
ClusterRoleBindings() ClusterRoleBindingInterface
|
||||
}
|
||||
|
||||
// ClusterRoleBindingInterface has methods to work with ClusterRoleBinding resources.
|
||||
type ClusterRoleBindingInterface interface {
|
||||
List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error)
|
||||
Get(name string) (*rbac.ClusterRoleBinding, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
Create(clusterRoleBinding *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
|
||||
Update(clusterRoleBinding *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// clusterRoleBindings implements ClusterRoleBindingsNamespacer interface
|
||||
type clusterRoleBindings struct {
|
||||
client *RbacClient
|
||||
}
|
||||
|
||||
// newClusterRoleBindings returns a clusterRoleBindings
|
||||
func newClusterRoleBindings(c *RbacClient) *clusterRoleBindings {
|
||||
return &clusterRoleBindings{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of clusterRoleBindings that match those selectors.
|
||||
func (c *clusterRoleBindings) List(opts api.ListOptions) (result *rbac.ClusterRoleBindingList, err error) {
|
||||
result = &rbac.ClusterRoleBindingList{}
|
||||
err = c.client.Get().Resource("clusterrolebindings").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get takes the name of the clusterRoleBinding, and returns the corresponding ClusterRoleBinding object, and an error if it occurs
|
||||
func (c *clusterRoleBindings) Get(name string) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Get().Resource("clusterrolebindings").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes the name of the clusterRoleBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *clusterRoleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().Resource("clusterrolebindings").Name(name).Body(options).Do().Error()
|
||||
}
|
||||
|
||||
// Create takes the representation of a clusterRoleBinding and creates it. Returns the server's representation of the clusterRoleBinding, and an error, if it occurs.
|
||||
func (c *clusterRoleBindings) Create(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Post().Resource("clusterrolebindings").Body(clusterRoleBinding).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a clusterRoleBinding and updates it. Returns the server's representation of the clusterRoleBinding, and an error, if it occurs.
|
||||
func (c *clusterRoleBindings) Update(clusterRoleBinding *rbac.ClusterRoleBinding) (result *rbac.ClusterRoleBinding, err error) {
|
||||
result = &rbac.ClusterRoleBinding{}
|
||||
err = c.client.Put().Resource("clusterrolebindings").Name(clusterRoleBinding.Name).Body(clusterRoleBinding).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoleBindings.
|
||||
func (c *clusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Resource("clusterrolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
92
pkg/client/unversioned/clusterroles.go
Normal file
92
pkg/client/unversioned/clusterroles.go
Normal file
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// ClusterRoles has methods to work with ClusterRole resources in a namespace
|
||||
type ClusterRoles interface {
|
||||
ClusterRoles() ClusterRoleInterface
|
||||
}
|
||||
|
||||
// ClusterRoleInterface has methods to work with ClusterRole resources.
|
||||
type ClusterRoleInterface interface {
|
||||
List(opts api.ListOptions) (*rbac.ClusterRoleList, error)
|
||||
Get(name string) (*rbac.ClusterRole, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
Create(clusterRole *rbac.ClusterRole) (*rbac.ClusterRole, error)
|
||||
Update(clusterRole *rbac.ClusterRole) (*rbac.ClusterRole, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// clusterRoles implements ClusterRolesNamespacer interface
|
||||
type clusterRoles struct {
|
||||
client *RbacClient
|
||||
}
|
||||
|
||||
// newClusterRoles returns a clusterRoles
|
||||
func newClusterRoles(c *RbacClient) *clusterRoles {
|
||||
return &clusterRoles{
|
||||
client: c,
|
||||
}
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of clusterRoles that match those selectors.
|
||||
func (c *clusterRoles) List(opts api.ListOptions) (result *rbac.ClusterRoleList, err error) {
|
||||
result = &rbac.ClusterRoleList{}
|
||||
err = c.client.Get().Resource("clusterroles").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get takes the name of the clusterRole, and returns the corresponding ClusterRole object, and an error if it occurs
|
||||
func (c *clusterRoles) Get(name string) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Get().Resource("clusterroles").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes the name of the clusterRole and deletes it. Returns an error if one occurs.
|
||||
func (c *clusterRoles) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().Resource("clusterroles").Name(name).Body(options).Do().Error()
|
||||
}
|
||||
|
||||
// Create takes the representation of a clusterRole and creates it. Returns the server's representation of the clusterRole, and an error, if it occurs.
|
||||
func (c *clusterRoles) Create(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Post().Resource("clusterroles").Body(clusterRole).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a clusterRole and updates it. Returns the server's representation of the clusterRole, and an error, if it occurs.
|
||||
func (c *clusterRoles) Update(clusterRole *rbac.ClusterRole) (result *rbac.ClusterRole, err error) {
|
||||
result = &rbac.ClusterRole{}
|
||||
err = c.client.Put().Resource("clusterroles").Name(clusterRole.Name).Body(clusterRole).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested clusterRoles.
|
||||
func (c *clusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Resource("clusterroles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -27,6 +27,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/client/typed/discovery"
|
||||
"k8s.io/kubernetes/pkg/util/sets"
|
||||
@@ -104,7 +105,16 @@ func New(c *restclient.Config) (*Client, error) {
|
||||
}
|
||||
}
|
||||
|
||||
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient, AppsClient: appsClient, PolicyClient: policyClient}, nil
|
||||
var rbacClient *RbacClient
|
||||
if registered.IsRegistered(rbac.GroupName) {
|
||||
rbacConfig := *c
|
||||
rbacClient, err = NewRbac(&rbacConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &Client{RESTClient: client, AutoscalingClient: autoscalingClient, BatchClient: batchClient, ExtensionsClient: extensionsClient, DiscoveryClient: discoveryClient, AppsClient: appsClient, PolicyClient: policyClient, RbacClient: rbacClient}, nil
|
||||
}
|
||||
|
||||
// MatchesServerVersion queries the server to compares the build version
|
||||
|
@@ -31,6 +31,7 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/metrics/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/policy/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
103
pkg/client/unversioned/rbac.go
Normal file
103
pkg/client/unversioned/rbac.go
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apimachinery/registered"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
)
|
||||
|
||||
// Interface holds the methods for clients of Kubernetes to allow mock testing.
|
||||
type RbacInterface interface {
|
||||
RoleBindingsNamespacer
|
||||
RolesNamespacer
|
||||
ClusterRoleBindings
|
||||
ClusterRoles
|
||||
}
|
||||
|
||||
type RbacClient struct {
|
||||
*restclient.RESTClient
|
||||
}
|
||||
|
||||
func (c *RbacClient) RoleBindings(namespace string) RoleBindingInterface {
|
||||
return newRoleBindings(c, namespace)
|
||||
}
|
||||
|
||||
func (c *RbacClient) Roles(namespace string) RoleInterface {
|
||||
return newRoles(c, namespace)
|
||||
}
|
||||
|
||||
func (c *RbacClient) ClusterRoleBindings() ClusterRoleBindingInterface {
|
||||
return newClusterRoleBindings(c)
|
||||
}
|
||||
|
||||
func (c *RbacClient) ClusterRoles() ClusterRoleInterface {
|
||||
return newClusterRoles(c)
|
||||
}
|
||||
|
||||
// NewRbac creates a new RbacClient for the given config.
|
||||
func NewRbac(c *restclient.Config) (*RbacClient, error) {
|
||||
config := *c
|
||||
if err := setRbacDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := restclient.RESTClientFor(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &RbacClient{client}, nil
|
||||
}
|
||||
|
||||
// NewRbacOrDie creates a new RbacClient for the given config and
|
||||
// panics if there is an error in the config.
|
||||
func NewRbacOrDie(c *restclient.Config) *RbacClient {
|
||||
client, err := NewRbac(c)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
func setRbacDefaults(config *restclient.Config) error {
|
||||
// if rbac group is not registered, return an error
|
||||
g, err := registered.Group(rbac.GroupName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.APIPath = defaultAPIPath
|
||||
if config.UserAgent == "" {
|
||||
config.UserAgent = restclient.DefaultKubernetesUserAgent()
|
||||
}
|
||||
|
||||
// TODO: Unconditionally set the config.Version, until we fix the config.
|
||||
//if config.Version == "" {
|
||||
copyGroupVersion := g.GroupVersion
|
||||
config.GroupVersion = ©GroupVersion
|
||||
//}
|
||||
|
||||
config.Codec = api.Codecs.LegacyCodec(*config.GroupVersion)
|
||||
config.NegotiatedSerializer = api.Codecs
|
||||
if config.QPS == 0 {
|
||||
config.QPS = 5
|
||||
}
|
||||
if config.Burst == 0 {
|
||||
config.Burst = 10
|
||||
}
|
||||
return nil
|
||||
}
|
95
pkg/client/unversioned/rolebindings.go
Normal file
95
pkg/client/unversioned/rolebindings.go
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// RoleBindingsNamespacer has methods to work with RoleBinding resources in a namespace
|
||||
type RoleBindingsNamespacer interface {
|
||||
RoleBindings(namespace string) RoleBindingInterface
|
||||
}
|
||||
|
||||
// RoleBindingInterface has methods to work with RoleBinding resources.
|
||||
type RoleBindingInterface interface {
|
||||
List(opts api.ListOptions) (*rbac.RoleBindingList, error)
|
||||
Get(name string) (*rbac.RoleBinding, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
Create(roleBinding *rbac.RoleBinding) (*rbac.RoleBinding, error)
|
||||
Update(roleBinding *rbac.RoleBinding) (*rbac.RoleBinding, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// roleBindings implements RoleBindingsNamespacer interface
|
||||
type roleBindings struct {
|
||||
client *RbacClient
|
||||
ns string
|
||||
}
|
||||
|
||||
// newRoleBindings returns a roleBindings
|
||||
func newRoleBindings(c *RbacClient, namespace string) *roleBindings {
|
||||
return &roleBindings{
|
||||
client: c,
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of roleBindings that match those selectors.
|
||||
func (c *roleBindings) List(opts api.ListOptions) (result *rbac.RoleBindingList, err error) {
|
||||
result = &rbac.RoleBindingList{}
|
||||
err = c.client.Get().Namespace(c.ns).Resource("rolebindings").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get takes the name of the roleBinding, and returns the corresponding RoleBinding object, and an error if it occurs
|
||||
func (c *roleBindings) Get(name string) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Get().Namespace(c.ns).Resource("rolebindings").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes the name of the roleBinding and deletes it. Returns an error if one occurs.
|
||||
func (c *roleBindings) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().Namespace(c.ns).Resource("rolebindings").Name(name).Body(options).Do().Error()
|
||||
}
|
||||
|
||||
// Create takes the representation of a roleBinding and creates it. Returns the server's representation of the roleBinding, and an error, if it occurs.
|
||||
func (c *roleBindings) Create(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Post().Namespace(c.ns).Resource("rolebindings").Body(roleBinding).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a roleBinding and updates it. Returns the server's representation of the roleBinding, and an error, if it occurs.
|
||||
func (c *roleBindings) Update(roleBinding *rbac.RoleBinding) (result *rbac.RoleBinding, err error) {
|
||||
result = &rbac.RoleBinding{}
|
||||
err = c.client.Put().Namespace(c.ns).Resource("rolebindings").Name(roleBinding.Name).Body(roleBinding).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roleBindings.
|
||||
func (c *roleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Namespace(c.ns).
|
||||
Resource("rolebindings").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
95
pkg/client/unversioned/roles.go
Normal file
95
pkg/client/unversioned/roles.go
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 unversioned
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// RolesNamespacer has methods to work with Role resources in a namespace
|
||||
type RolesNamespacer interface {
|
||||
Roles(namespace string) RoleInterface
|
||||
}
|
||||
|
||||
// RoleInterface has methods to work with Role resources.
|
||||
type RoleInterface interface {
|
||||
List(opts api.ListOptions) (*rbac.RoleList, error)
|
||||
Get(name string) (*rbac.Role, error)
|
||||
Delete(name string, options *api.DeleteOptions) error
|
||||
Create(role *rbac.Role) (*rbac.Role, error)
|
||||
Update(role *rbac.Role) (*rbac.Role, error)
|
||||
Watch(opts api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// roles implements RolesNamespacer interface
|
||||
type roles struct {
|
||||
client *RbacClient
|
||||
ns string
|
||||
}
|
||||
|
||||
// newRoles returns a roles
|
||||
func newRoles(c *RbacClient, namespace string) *roles {
|
||||
return &roles{
|
||||
client: c,
|
||||
ns: namespace,
|
||||
}
|
||||
}
|
||||
|
||||
// List takes label and field selectors, and returns the list of roles that match those selectors.
|
||||
func (c *roles) List(opts api.ListOptions) (result *rbac.RoleList, err error) {
|
||||
result = &rbac.RoleList{}
|
||||
err = c.client.Get().Namespace(c.ns).Resource("roles").VersionedParams(&opts, api.ParameterCodec).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Get takes the name of the role, and returns the corresponding Role object, and an error if it occurs
|
||||
func (c *roles) Get(name string) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Get().Namespace(c.ns).Resource("roles").Name(name).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Delete takes the name of the role and deletes it. Returns an error if one occurs.
|
||||
func (c *roles) Delete(name string, options *api.DeleteOptions) error {
|
||||
return c.client.Delete().Namespace(c.ns).Resource("roles").Name(name).Body(options).Do().Error()
|
||||
}
|
||||
|
||||
// Create takes the representation of a role and creates it. Returns the server's representation of the role, and an error, if it occurs.
|
||||
func (c *roles) Create(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Post().Namespace(c.ns).Resource("roles").Body(role).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Update takes the representation of a role and updates it. Returns the server's representation of the role, and an error, if it occurs.
|
||||
func (c *roles) Update(role *rbac.Role) (result *rbac.Role, err error) {
|
||||
result = &rbac.Role{}
|
||||
err = c.client.Put().Namespace(c.ns).Resource("roles").Name(role.Name).Body(role).Do().Into(result)
|
||||
return
|
||||
}
|
||||
|
||||
// Watch returns a watch.Interface that watches the requested roles.
|
||||
func (c *roles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.client.Get().
|
||||
Prefix("watch").
|
||||
Namespace(c.ns).
|
||||
Resource("roles").
|
||||
VersionedParams(&opts, api.ParameterCodec).
|
||||
Watch()
|
||||
}
|
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeClusterRoleBindings implements ClusterRoleBindingInterface
|
||||
type FakeClusterRoleBindings struct {
|
||||
Fake *FakeRbac
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Get(name string) (*rbac.ClusterRoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootGetAction("clusterrolebindings", name), &rbac.ClusterRoleBinding{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) List(opts api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootListAction("clusterrolebindings", opts), &rbac.ClusterRoleBindingList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBindingList), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Create(csr *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootCreateAction("clusterrolebindings", csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Update(csr *rbac.ClusterRoleBinding) (*rbac.ClusterRoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootUpdateAction("clusterrolebindings", csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Delete(name string, opts *api.DeleteOptions) error {
|
||||
_, err := c.Fake.Invokes(NewRootDeleteAction("clusterrolebindings", name), &rbac.ClusterRoleBinding{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.InvokesWatch(NewRootWatchAction("clusterrolebindings", opts))
|
||||
}
|
73
pkg/client/unversioned/testclient/fake_clusterroles.go
Normal file
73
pkg/client/unversioned/testclient/fake_clusterroles.go
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeClusterRoles implements ClusterRoleInterface
|
||||
type FakeClusterRoles struct {
|
||||
Fake *FakeRbac
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Get(name string) (*rbac.ClusterRole, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootGetAction("clusterroles", name), &rbac.ClusterRole{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) List(opts api.ListOptions) (*rbac.ClusterRoleList, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootListAction("clusterroles", opts), &rbac.ClusterRoleList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleList), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Create(csr *rbac.ClusterRole) (*rbac.ClusterRole, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootCreateAction("clusterroles", csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Update(csr *rbac.ClusterRole) (*rbac.ClusterRole, error) {
|
||||
obj, err := c.Fake.Invokes(NewRootUpdateAction("clusterroles", csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRole), err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Delete(name string, opts *api.DeleteOptions) error {
|
||||
_, err := c.Fake.Invokes(NewRootDeleteAction("clusterroles", name), &rbac.ClusterRole{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeClusterRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.InvokesWatch(NewRootWatchAction("clusterroles", opts))
|
||||
}
|
74
pkg/client/unversioned/testclient/fake_rolebindings.go
Normal file
74
pkg/client/unversioned/testclient/fake_rolebindings.go
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeRoleBindings implements RoleBindingInterface
|
||||
type FakeRoleBindings struct {
|
||||
Fake *FakeRbac
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Get(name string) (*rbac.RoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewGetAction("rolebindings", c.Namespace, name), &rbac.RoleBinding{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) List(opts api.ListOptions) (*rbac.RoleBindingList, error) {
|
||||
obj, err := c.Fake.Invokes(NewListAction("rolebindings", c.Namespace, opts), &rbac.RoleBindingList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBindingList), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Create(csr *rbac.RoleBinding) (*rbac.RoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewCreateAction("rolebindings", c.Namespace, csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Update(csr *rbac.RoleBinding) (*rbac.RoleBinding, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateAction("rolebindings", c.Namespace, csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBinding), err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Delete(name string, opts *api.DeleteOptions) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("rolebindings", c.Namespace, name), &rbac.RoleBinding{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoleBindings) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.InvokesWatch(NewWatchAction("rolebindings", c.Namespace, opts))
|
||||
}
|
74
pkg/client/unversioned/testclient/fake_roles.go
Normal file
74
pkg/client/unversioned/testclient/fake_roles.go
Normal file
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 testclient
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// FakeRoles implements RoleInterface
|
||||
type FakeRoles struct {
|
||||
Fake *FakeRbac
|
||||
Namespace string
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Get(name string) (*rbac.Role, error) {
|
||||
obj, err := c.Fake.Invokes(NewGetAction("roles", c.Namespace, name), &rbac.Role{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) List(opts api.ListOptions) (*rbac.RoleList, error) {
|
||||
obj, err := c.Fake.Invokes(NewListAction("roles", c.Namespace, opts), &rbac.RoleList{})
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleList), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Create(csr *rbac.Role) (*rbac.Role, error) {
|
||||
obj, err := c.Fake.Invokes(NewCreateAction("roles", c.Namespace, csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Update(csr *rbac.Role) (*rbac.Role, error) {
|
||||
obj, err := c.Fake.Invokes(NewUpdateAction("roles", c.Namespace, csr), csr)
|
||||
if obj == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.Role), err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Delete(name string, opts *api.DeleteOptions) error {
|
||||
_, err := c.Fake.Invokes(NewDeleteAction("roles", c.Namespace, name), &rbac.Role{})
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *FakeRoles) Watch(opts api.ListOptions) (watch.Interface, error) {
|
||||
return c.Fake.InvokesWatch(NewWatchAction("roles", c.Namespace, opts))
|
||||
}
|
@@ -101,6 +101,10 @@ func (c *Client) Setup(t *testing.T) *Client {
|
||||
Host: c.server.URL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Extensions.GroupVersion()},
|
||||
})
|
||||
c.RbacClient = client.NewRbacOrDie(&restclient.Config{
|
||||
Host: c.server.URL,
|
||||
ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Rbac.GroupVersion()},
|
||||
})
|
||||
|
||||
c.Clientset = clientset.NewForConfigOrDie(&restclient.Config{Host: c.server.URL})
|
||||
}
|
||||
|
@@ -301,6 +301,10 @@ func (c *Fake) ConfigMaps(namespace string) client.ConfigMapsInterface {
|
||||
return &FakeConfigMaps{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
||||
func (c *Fake) Rbac() client.RbacInterface {
|
||||
return &FakeRbac{Fake: c}
|
||||
}
|
||||
|
||||
// SwaggerSchema returns an empty swagger.ApiDeclaration for testing
|
||||
func (c *Fake) SwaggerSchema(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) {
|
||||
action := ActionImpl{}
|
||||
@@ -386,6 +390,30 @@ func (c *FakeExperimental) NetworkPolicies(namespace string) client.NetworkPolic
|
||||
return &FakeNetworkPolicies{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
||||
func NewSimpleFakeRbac(objects ...runtime.Object) *FakeRbac {
|
||||
return &FakeRbac{Fake: NewSimpleFake(objects...)}
|
||||
}
|
||||
|
||||
type FakeRbac struct {
|
||||
*Fake
|
||||
}
|
||||
|
||||
func (c *FakeRbac) Roles(namespace string) client.RoleInterface {
|
||||
return &FakeRoles{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) RoleBindings(namespace string) client.RoleBindingInterface {
|
||||
return &FakeRoleBindings{Fake: c, Namespace: namespace}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) ClusterRoles() client.ClusterRoleInterface {
|
||||
return &FakeClusterRoles{Fake: c}
|
||||
}
|
||||
|
||||
func (c *FakeRbac) ClusterRoleBindings() client.ClusterRoleBindingInterface {
|
||||
return &FakeClusterRoleBindings{Fake: c}
|
||||
}
|
||||
|
||||
type FakeDiscovery struct {
|
||||
*Fake
|
||||
}
|
||||
|
@@ -118,6 +118,8 @@ type Config struct {
|
||||
Authorizer authorizer.Authorizer
|
||||
AdmissionControl admission.Interface
|
||||
MasterServiceNamespace string
|
||||
// TODO(ericchiang): Determine if policy escalation checks should be an admission controller.
|
||||
AuthorizerRBACSuperUser string
|
||||
|
||||
// Map requests to contexts. Exported so downstream consumers can provider their own mappers
|
||||
RequestContextMapper api.RequestContextMapper
|
||||
|
@@ -227,6 +227,7 @@ func (s *ServerRunOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.AuthorizationConfig.WebhookConfigFile, "authorization-webhook-config-file", s.AuthorizationConfig.WebhookConfigFile, "File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. The API server will query the remote service to determine access on the API server's secure port.")
|
||||
fs.DurationVar(&s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl", s.AuthorizationConfig.WebhookCacheAuthorizedTTL, "The duration to cache 'authorized' responses from the webhook authorizer. Default is 5m.")
|
||||
fs.DurationVar(&s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "authorization-webhook-cache-unauthorized-ttl", s.AuthorizationConfig.WebhookCacheUnauthorizedTTL, "The duration to cache 'unauthorized' responses from the webhook authorizer. Default is 30s.")
|
||||
fs.StringVar(&s.AuthorizationConfig.RBACSuperUser, "authorization-rbac-super-user", s.AuthorizationConfig.RBACSuperUser, "If specified, a username which avoids RBAC authorization checks and role binding privilege escalation checks, to be used with --authorization-mode=RBAC.")
|
||||
|
||||
fs.StringVar(&s.BasicAuthFile, "basic-auth-file", s.BasicAuthFile, "If set, the file that will be used to admit requests to the secure port of the API server via http basic authentication.")
|
||||
|
||||
|
@@ -49,6 +49,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/metrics"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
clientset "k8s.io/kubernetes/pkg/client/unversioned/adapters/internalclientset"
|
||||
@@ -347,6 +348,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
|
||||
return c.ExtensionsClient.RESTClient, nil
|
||||
case federation.GroupName:
|
||||
return clients.FederationClientForVersion(&mappingVersion)
|
||||
case rbac.GroupName:
|
||||
return c.RbacClient.RESTClient, nil
|
||||
default:
|
||||
if !registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||
return nil, fmt.Errorf("unknown api group/version: %s", gvk.String())
|
||||
@@ -1017,6 +1020,12 @@ func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
|
||||
}
|
||||
return getSchemaAndValidate(c.c.BatchClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
||||
}
|
||||
if gvk.Group == rbac.GroupName {
|
||||
if c.c.RbacClient == nil {
|
||||
return errors.New("unable to validate: no rbac client")
|
||||
}
|
||||
return getSchemaAndValidate(c.c.RbacClient.RESTClient, data, "apis/", gvk.GroupVersion().String(), c.cacheDir, c)
|
||||
}
|
||||
if registered.IsThirdPartyAPIGroupVersion(gvk.GroupVersion()) {
|
||||
// Don't attempt to validate third party objects
|
||||
return nil
|
||||
|
@@ -29,6 +29,7 @@ import (
|
||||
_ "k8s.io/kubernetes/pkg/apis/componentconfig/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/policy/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/rbac/install"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@@ -43,12 +43,21 @@ import (
|
||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
policyapiv1alpha1 "k8s.io/kubernetes/pkg/apis/policy/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
rbacapi "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1"
|
||||
rbacvalidation "k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
apiservermetrics "k8s.io/kubernetes/pkg/apiserver/metrics"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/healthz"
|
||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||
"k8s.io/kubernetes/pkg/master/ports"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
|
||||
clusterrolepolicybased "k8s.io/kubernetes/pkg/registry/clusterrole/policybased"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
|
||||
clusterrolebindingpolicybased "k8s.io/kubernetes/pkg/registry/clusterrolebinding/policybased"
|
||||
"k8s.io/kubernetes/pkg/registry/componentstatus"
|
||||
configmapetcd "k8s.io/kubernetes/pkg/registry/configmap/etcd"
|
||||
controlleretcd "k8s.io/kubernetes/pkg/registry/controller/etcd"
|
||||
@@ -75,6 +84,12 @@ import (
|
||||
podtemplateetcd "k8s.io/kubernetes/pkg/registry/podtemplate/etcd"
|
||||
replicasetetcd "k8s.io/kubernetes/pkg/registry/replicaset/etcd"
|
||||
resourcequotaetcd "k8s.io/kubernetes/pkg/registry/resourcequota/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
|
||||
rolepolicybased "k8s.io/kubernetes/pkg/registry/role/policybased"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
|
||||
rolebindingpolicybased "k8s.io/kubernetes/pkg/registry/rolebinding/policybased"
|
||||
secretetcd "k8s.io/kubernetes/pkg/registry/secret/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/service"
|
||||
etcdallocator "k8s.io/kubernetes/pkg/registry/service/allocator/etcd"
|
||||
@@ -411,6 +426,39 @@ func (m *Master) InstallAPIs(c *Config) {
|
||||
allGroups = append(allGroups, group)
|
||||
|
||||
}
|
||||
|
||||
if c.APIResourceConfigSource.AnyResourcesForVersionEnabled(rbacapi.SchemeGroupVersion) {
|
||||
rbacResources := m.getRBACResources(c)
|
||||
rbacGroupMeta := registered.GroupOrDie(rbac.GroupName)
|
||||
|
||||
// Hard code preferred group version to rbac/v1alpha1
|
||||
rbacGroupMeta.GroupVersion = rbacapi.SchemeGroupVersion
|
||||
|
||||
apiGroupInfo := genericapiserver.APIGroupInfo{
|
||||
GroupMeta: *rbacGroupMeta,
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
|
||||
"v1alpha1": rbacResources,
|
||||
},
|
||||
OptionsExternalVersion: ®istered.GroupOrDie(api.GroupName).GroupVersion,
|
||||
Scheme: api.Scheme,
|
||||
ParameterCodec: api.ParameterCodec,
|
||||
NegotiatedSerializer: api.Codecs,
|
||||
}
|
||||
apiGroupsInfo = append(apiGroupsInfo, apiGroupInfo)
|
||||
|
||||
rbacGVForDiscovery := unversioned.GroupVersionForDiscovery{
|
||||
GroupVersion: rbacGroupMeta.GroupVersion.String(),
|
||||
Version: rbacGroupMeta.GroupVersion.Version,
|
||||
}
|
||||
group := unversioned.APIGroup{
|
||||
Name: rbacGroupMeta.GroupVersion.Group,
|
||||
Versions: []unversioned.GroupVersionForDiscovery{rbacGVForDiscovery},
|
||||
PreferredVersion: rbacGVForDiscovery,
|
||||
}
|
||||
allGroups = append(allGroups, group)
|
||||
|
||||
}
|
||||
|
||||
if err := m.InstallAPIGroups(apiGroupsInfo); err != nil {
|
||||
glog.Fatalf("Error in registering group versions: %v", err)
|
||||
}
|
||||
@@ -908,6 +956,43 @@ func (m *Master) getAppsResources(c *Config) map[string]rest.Storage {
|
||||
return storage
|
||||
}
|
||||
|
||||
func (m *Master) getRBACResources(c *Config) map[string]rest.Storage {
|
||||
version := rbacapi.SchemeGroupVersion
|
||||
|
||||
once := new(sync.Once)
|
||||
var authorizationRuleResolver rbacvalidation.AuthorizationRuleResolver
|
||||
newRuleValidator := func() rbacvalidation.AuthorizationRuleResolver {
|
||||
once.Do(func() {
|
||||
authorizationRuleResolver = rbacvalidation.NewDefaultRuleResolver(
|
||||
role.NewRegistry(roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))),
|
||||
rolebinding.NewRegistry(rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))),
|
||||
clusterrole.NewRegistry(clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))),
|
||||
clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))),
|
||||
)
|
||||
})
|
||||
return authorizationRuleResolver
|
||||
}
|
||||
|
||||
storage := map[string]rest.Storage{}
|
||||
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("roles")) {
|
||||
rolesStorage := roleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("roles")))
|
||||
storage["roles"] = rolepolicybased.NewStorage(rolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
|
||||
}
|
||||
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("rolebindings")) {
|
||||
roleBindingsStorage := rolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("rolebindings")))
|
||||
storage["rolebindings"] = rolebindingpolicybased.NewStorage(roleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
|
||||
}
|
||||
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterroles")) {
|
||||
clusterRolesStorage := clusterroleetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterroles")))
|
||||
storage["clusterroles"] = clusterrolepolicybased.NewStorage(clusterRolesStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
|
||||
}
|
||||
if c.APIResourceConfigSource.ResourceEnabled(version.WithResource("clusterrolebindings")) {
|
||||
clusterRoleBindingsStorage := clusterrolebindingetcd.NewREST(m.GetRESTOptionsOrDie(c, rbac.Resource("clusterrolebindings")))
|
||||
storage["clusterrolebindings"] = clusterrolebindingpolicybased.NewStorage(clusterRoleBindingsStorage, newRuleValidator(), c.AuthorizerRBACSuperUser)
|
||||
}
|
||||
return storage
|
||||
}
|
||||
|
||||
// findExternalAddress returns ExternalIP of provided node with fallback to LegacyHostIP.
|
||||
func findExternalAddress(node *api.Node) (string, error) {
|
||||
var fallback string
|
||||
@@ -960,7 +1045,7 @@ func (m *Master) IsTunnelSyncHealthy(req *http.Request) error {
|
||||
|
||||
func DefaultAPIResourceConfigSource() *genericapiserver.ResourceConfig {
|
||||
ret := genericapiserver.NewResourceConfig()
|
||||
ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion)
|
||||
ret.EnableVersions(apiv1.SchemeGroupVersion, extensionsapiv1beta1.SchemeGroupVersion, batchapiv1.SchemeGroupVersion, autoscalingapiv1.SchemeGroupVersion, appsapi.SchemeGroupVersion, policyapiv1alpha1.SchemeGroupVersion, rbacapi.SchemeGroupVersion)
|
||||
|
||||
// all extensions resources except these are disabled by default
|
||||
ret.EnableResources(
|
||||
|
@@ -43,6 +43,7 @@ import (
|
||||
batchapiv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsapiv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
"k8s.io/kubernetes/pkg/genericapiserver"
|
||||
"k8s.io/kubernetes/pkg/kubelet/client"
|
||||
@@ -89,6 +90,7 @@ func setUp(t *testing.T) (*Master, *etcdtesting.EtcdTestServer, Config, *assert.
|
||||
resourceEncoding.SetVersionEncoding(batch.GroupName, *testapi.Batch.GroupVersion(), unversioned.GroupVersion{Group: batch.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(apps.GroupName, *testapi.Apps.GroupVersion(), unversioned.GroupVersion{Group: apps.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(extensions.GroupName, *testapi.Extensions.GroupVersion(), unversioned.GroupVersion{Group: extensions.GroupName, Version: runtime.APIVersionInternal})
|
||||
resourceEncoding.SetVersionEncoding(rbac.GroupName, *testapi.Rbac.GroupVersion(), unversioned.GroupVersion{Group: rbac.GroupName, Version: runtime.APIVersionInternal})
|
||||
storageFactory := genericapiserver.NewDefaultStorageFactory(storageConfig, testapi.StorageMediaType(), api.Codecs, resourceEncoding, DefaultAPIResourceConfigSource())
|
||||
|
||||
config.StorageFactory = storageFactory
|
||||
|
@@ -28,6 +28,8 @@ import (
|
||||
type Resource string
|
||||
|
||||
const (
|
||||
ClusterRoles Resource = "clusterroles"
|
||||
ClusterRoleBindings Resource = "clusterrolebindings"
|
||||
Controllers Resource = "controllers"
|
||||
Daemonsets Resource = "daemonsets"
|
||||
Deployments Resource = "deployments"
|
||||
@@ -48,6 +50,8 @@ const (
|
||||
Replicasets Resource = "replicasets"
|
||||
ResourceQuotas Resource = "resourcequotas"
|
||||
ScheduledJobs Resource = "scheduledjobs"
|
||||
Roles Resource = "roles"
|
||||
RoleBindings Resource = "rolebindings"
|
||||
Secrets Resource = "secrets"
|
||||
ServiceAccounts Resource = "serviceaccounts"
|
||||
Services Resource = "services"
|
||||
@@ -57,6 +61,8 @@ var watchCacheSizes map[Resource]int
|
||||
|
||||
func init() {
|
||||
watchCacheSizes = make(map[Resource]int)
|
||||
watchCacheSizes[ClusterRoles] = 100
|
||||
watchCacheSizes[ClusterRoleBindings] = 100
|
||||
watchCacheSizes[Controllers] = 100
|
||||
watchCacheSizes[Daemonsets] = 100
|
||||
watchCacheSizes[Deployments] = 100
|
||||
@@ -77,6 +83,8 @@ func init() {
|
||||
watchCacheSizes[Replicasets] = 100
|
||||
watchCacheSizes[ResourceQuotas] = 100
|
||||
watchCacheSizes[ScheduledJobs] = 100
|
||||
watchCacheSizes[Roles] = 100
|
||||
watchCacheSizes[RoleBindings] = 100
|
||||
watchCacheSizes[Secrets] = 100
|
||||
watchCacheSizes[ServiceAccounts] = 100
|
||||
watchCacheSizes[Services] = 100
|
||||
|
19
pkg/registry/clusterrole/doc.go
Normal file
19
pkg/registry/clusterrole/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing ClusterRole objects.
|
||||
package clusterrole
|
76
pkg/registry/clusterrole/etcd/etcd.go
Normal file
76
pkg/registry/clusterrole/etcd/etcd.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for ClusterRole against etcd
|
||||
type REST struct {
|
||||
*registry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against ClusterRole objects.
|
||||
func NewREST(opts generic.RESTOptions) *REST {
|
||||
prefix := "/clusterroles"
|
||||
|
||||
newListFunc := func() runtime.Object { return &rbac.ClusterRoleList{} }
|
||||
storageInterface := opts.Decorator(
|
||||
opts.Storage,
|
||||
cachesize.GetWatchCacheSizeByResource(cachesize.ClusterRoles),
|
||||
&rbac.ClusterRole{},
|
||||
prefix,
|
||||
clusterrole.Strategy,
|
||||
newListFunc,
|
||||
)
|
||||
|
||||
store := ®istry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.ClusterRole{} },
|
||||
NewListFunc: newListFunc,
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
return registry.NamespaceKeyRootFunc(ctx, prefix)
|
||||
},
|
||||
KeyFunc: func(ctx api.Context, id string) (string, error) {
|
||||
return registry.NoNamespaceKeyFunc(ctx, prefix, id)
|
||||
},
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*rbac.ClusterRole).Name, nil
|
||||
},
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return clusterrole.Matcher(label, field)
|
||||
},
|
||||
QualifiedResource: rbac.Resource("clusterroles"),
|
||||
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
|
||||
|
||||
CreateStrategy: clusterrole.Strategy,
|
||||
UpdateStrategy: clusterrole.Strategy,
|
||||
DeleteStrategy: clusterrole.Strategy,
|
||||
|
||||
Storage: storageInterface,
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
100
pkg/registry/clusterrole/policybased/storage.go
Normal file
100
pkg/registry/clusterrole/policybased/storage.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 policybased implements a standard storage for ClusterRole that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("clusterroles")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver validation.AuthorizationRuleResolver
|
||||
|
||||
// user which skips privilege escalation checks
|
||||
superUser string
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
|
||||
return &Storage{s, ruleResolver, superUser}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
}
|
||||
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
rules := clusterRole.Rules
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Update(ctx, name, obj)
|
||||
}
|
||||
}
|
||||
|
||||
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
|
||||
rules := clusterRole.Rules
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRole.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
|
||||
}
|
||||
|
||||
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
|
||||
type wrappedUpdatedObjectInfo struct {
|
||||
objInfo rest.UpdatedObjectInfo
|
||||
|
||||
transformFunc rest.TransformFunc
|
||||
}
|
||||
|
||||
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
|
||||
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
|
||||
return i.objInfo.Preconditions()
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
|
||||
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return i.transformFunc(ctx, obj, oldObj)
|
||||
}
|
81
pkg/registry/clusterrole/registry.go
Normal file
81
pkg/registry/clusterrole/registry.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 clusterrole
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store ClusterRoles.
|
||||
type Registry interface {
|
||||
ListClusterRoles(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleList, error)
|
||||
CreateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error
|
||||
UpdateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error
|
||||
GetClusterRole(ctx api.Context, name string) (*rbac.ClusterRole, error)
|
||||
DeleteClusterRole(ctx api.Context, name string) error
|
||||
WatchClusterRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListClusterRoles(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error {
|
||||
_, err := s.Create(ctx, clusterRole)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateClusterRole(ctx api.Context, clusterRole *rbac.ClusterRole) error {
|
||||
_, _, err := s.Update(ctx, clusterRole.Name, rest.DefaultUpdatedObjectInfo(clusterRole, api.Scheme))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchClusterRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetClusterRole(ctx api.Context, name string) (*rbac.ClusterRole, error) {
|
||||
obj, err := s.Get(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRole), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteClusterRole(ctx api.Context, name string) error {
|
||||
_, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
119
pkg/registry/clusterrole/strategy.go
Normal file
119
pkg/registry/clusterrole/strategy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 clusterrole
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// strategy implements behavior for ClusterRoles
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// ClusterRole objects.
|
||||
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for ClusterRoles.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for ClusterRoles.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRole)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newClusterRole := obj.(*rbac.ClusterRole)
|
||||
oldClusterRole := old.(*rbac.ClusterRole)
|
||||
|
||||
_, _ = newClusterRole, oldClusterRole
|
||||
}
|
||||
|
||||
// Validate validates a new ClusterRole. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
|
||||
clusterRole := obj.(*rbac.ClusterRole)
|
||||
return validation.ValidateClusterRole(clusterRole)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRole)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.ClusterRole)
|
||||
errorList := validation.ValidateClusterRole(newObj)
|
||||
return append(errorList, validation.ValidateClusterRoleUpdate(newObj, old.(*rbac.ClusterRole))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s strategy) Export(obj runtime.Object, exact bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Matcher returns a generic matcher for a given label and field selector.
|
||||
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||
sa, ok := obj.(*rbac.ClusterRole)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("not a ClusterRole")
|
||||
}
|
||||
fields := SelectableFields(sa)
|
||||
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
|
||||
})
|
||||
}
|
||||
|
||||
// SelectableFields returns a label set that can be used for filter selection
|
||||
func SelectableFields(obj *rbac.ClusterRole) labels.Set {
|
||||
return labels.Set{}
|
||||
}
|
19
pkg/registry/clusterrolebinding/doc.go
Normal file
19
pkg/registry/clusterrolebinding/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing ClusterRoleBinding objects.
|
||||
package clusterrolebinding
|
76
pkg/registry/clusterrolebinding/etcd/etcd.go
Normal file
76
pkg/registry/clusterrolebinding/etcd/etcd.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for ClusterRoleBinding against etcd
|
||||
type REST struct {
|
||||
*registry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against ClusterRoleBinding objects.
|
||||
func NewREST(opts generic.RESTOptions) *REST {
|
||||
prefix := "/clusterrolebindings"
|
||||
|
||||
newListFunc := func() runtime.Object { return &rbac.ClusterRoleBindingList{} }
|
||||
storageInterface := opts.Decorator(
|
||||
opts.Storage,
|
||||
cachesize.GetWatchCacheSizeByResource(cachesize.ClusterRoleBindings),
|
||||
&rbac.ClusterRoleBinding{},
|
||||
prefix,
|
||||
clusterrolebinding.Strategy,
|
||||
newListFunc,
|
||||
)
|
||||
|
||||
store := ®istry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.ClusterRoleBinding{} },
|
||||
NewListFunc: newListFunc,
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
return registry.NamespaceKeyRootFunc(ctx, prefix)
|
||||
},
|
||||
KeyFunc: func(ctx api.Context, id string) (string, error) {
|
||||
return registry.NoNamespaceKeyFunc(ctx, prefix, id)
|
||||
},
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*rbac.ClusterRoleBinding).Name, nil
|
||||
},
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return clusterrolebinding.Matcher(label, field)
|
||||
},
|
||||
QualifiedResource: rbac.Resource("clusterrolebindings"),
|
||||
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
|
||||
|
||||
CreateStrategy: clusterrolebinding.Strategy,
|
||||
UpdateStrategy: clusterrolebinding.Strategy,
|
||||
DeleteStrategy: clusterrolebinding.Strategy,
|
||||
|
||||
Storage: storageInterface,
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
106
pkg/registry/clusterrolebinding/policybased/storage.go
Normal file
106
pkg/registry/clusterrolebinding/policybased/storage.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 policybased implements a standard storage for ClusterRoleBinding that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("clusterrolebindings")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver validation.AuthorizationRuleResolver
|
||||
|
||||
// user which skips privilege escalation checks
|
||||
superUser string
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
|
||||
return &Storage{s, ruleResolver, superUser}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
}
|
||||
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Update(ctx, name, obj)
|
||||
}
|
||||
}
|
||||
|
||||
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, clusterRoleBinding.RoleRef, clusterRoleBinding.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, clusterRoleBinding.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
|
||||
}
|
||||
|
||||
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
|
||||
type wrappedUpdatedObjectInfo struct {
|
||||
objInfo rest.UpdatedObjectInfo
|
||||
|
||||
transformFunc rest.TransformFunc
|
||||
}
|
||||
|
||||
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
|
||||
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
|
||||
return i.objInfo.Preconditions()
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
|
||||
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return i.transformFunc(ctx, obj, oldObj)
|
||||
}
|
81
pkg/registry/clusterrolebinding/registry.go
Normal file
81
pkg/registry/clusterrolebinding/registry.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 clusterrolebinding
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store ClusterRoleBindings.
|
||||
type Registry interface {
|
||||
ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error)
|
||||
CreateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error
|
||||
UpdateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error
|
||||
GetClusterRoleBinding(ctx api.Context, name string) (*rbac.ClusterRoleBinding, error)
|
||||
DeleteClusterRoleBinding(ctx api.Context, name string) error
|
||||
WatchClusterRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListClusterRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.ClusterRoleBindingList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.ClusterRoleBindingList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error {
|
||||
_, err := s.Create(ctx, clusterRoleBinding)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateClusterRoleBinding(ctx api.Context, clusterRoleBinding *rbac.ClusterRoleBinding) error {
|
||||
_, _, err := s.Update(ctx, clusterRoleBinding.Name, rest.DefaultUpdatedObjectInfo(clusterRoleBinding, api.Scheme))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchClusterRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetClusterRoleBinding(ctx api.Context, name string) (*rbac.ClusterRoleBinding, error) {
|
||||
obj, err := s.Get(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.ClusterRoleBinding), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteClusterRoleBinding(ctx api.Context, name string) error {
|
||||
_, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
119
pkg/registry/clusterrolebinding/strategy.go
Normal file
119
pkg/registry/clusterrolebinding/strategy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 clusterrolebinding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// strategy implements behavior for ClusterRoleBindings
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// ClusterRoleBinding objects.
|
||||
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for ClusterRoleBindings.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for ClusterRoleBindings.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRoleBinding)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newClusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
oldClusterRoleBinding := old.(*rbac.ClusterRoleBinding)
|
||||
|
||||
_, _ = newClusterRoleBinding, oldClusterRoleBinding
|
||||
}
|
||||
|
||||
// Validate validates a new ClusterRoleBinding. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
|
||||
clusterRoleBinding := obj.(*rbac.ClusterRoleBinding)
|
||||
return validation.ValidateClusterRoleBinding(clusterRoleBinding)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.ClusterRoleBinding)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.ClusterRoleBinding)
|
||||
errorList := validation.ValidateClusterRoleBinding(newObj)
|
||||
return append(errorList, validation.ValidateClusterRoleBindingUpdate(newObj, old.(*rbac.ClusterRoleBinding))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s strategy) Export(obj runtime.Object, exact bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Matcher returns a generic matcher for a given label and field selector.
|
||||
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||
sa, ok := obj.(*rbac.ClusterRoleBinding)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("not a ClusterRoleBinding")
|
||||
}
|
||||
fields := SelectableFields(sa)
|
||||
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
|
||||
})
|
||||
}
|
||||
|
||||
// SelectableFields returns a label set that can be used for filter selection
|
||||
func SelectableFields(obj *rbac.ClusterRoleBinding) labels.Set {
|
||||
return labels.Set{}
|
||||
}
|
19
pkg/registry/role/doc.go
Normal file
19
pkg/registry/role/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing Role objects.
|
||||
package role
|
76
pkg/registry/role/etcd/etcd.go
Normal file
76
pkg/registry/role/etcd/etcd.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for Role against etcd
|
||||
type REST struct {
|
||||
*registry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against Role objects.
|
||||
func NewREST(opts generic.RESTOptions) *REST {
|
||||
prefix := "/roles"
|
||||
|
||||
newListFunc := func() runtime.Object { return &rbac.RoleList{} }
|
||||
storageInterface := opts.Decorator(
|
||||
opts.Storage,
|
||||
cachesize.GetWatchCacheSizeByResource(cachesize.Roles),
|
||||
&rbac.Role{},
|
||||
prefix,
|
||||
role.Strategy,
|
||||
newListFunc,
|
||||
)
|
||||
|
||||
store := ®istry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.Role{} },
|
||||
NewListFunc: newListFunc,
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
return registry.NamespaceKeyRootFunc(ctx, prefix)
|
||||
},
|
||||
KeyFunc: func(ctx api.Context, id string) (string, error) {
|
||||
return registry.NamespaceKeyFunc(ctx, prefix, id)
|
||||
},
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*rbac.Role).Name, nil
|
||||
},
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return role.Matcher(label, field)
|
||||
},
|
||||
QualifiedResource: rbac.Resource("roles"),
|
||||
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
|
||||
|
||||
CreateStrategy: role.Strategy,
|
||||
UpdateStrategy: role.Strategy,
|
||||
DeleteStrategy: role.Strategy,
|
||||
|
||||
Storage: storageInterface,
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
100
pkg/registry/role/policybased/storage.go
Normal file
100
pkg/registry/role/policybased/storage.go
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 policybased implements a standard storage for Role that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("roles")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver validation.AuthorizationRuleResolver
|
||||
|
||||
// user which skips privilege escalation checks
|
||||
superUser string
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
|
||||
return &Storage{s, ruleResolver, superUser}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
}
|
||||
|
||||
role := obj.(*rbac.Role)
|
||||
rules := role.Rules
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, role.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Update(ctx, name, obj)
|
||||
}
|
||||
}
|
||||
|
||||
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
role := obj.(*rbac.Role)
|
||||
|
||||
rules := role.Rules
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, role.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
|
||||
}
|
||||
|
||||
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
|
||||
type wrappedUpdatedObjectInfo struct {
|
||||
objInfo rest.UpdatedObjectInfo
|
||||
|
||||
transformFunc rest.TransformFunc
|
||||
}
|
||||
|
||||
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
|
||||
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
|
||||
return i.objInfo.Preconditions()
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
|
||||
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return i.transformFunc(ctx, obj, oldObj)
|
||||
}
|
81
pkg/registry/role/registry.go
Normal file
81
pkg/registry/role/registry.go
Normal file
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 role
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store Roles.
|
||||
type Registry interface {
|
||||
ListRoles(ctx api.Context, options *api.ListOptions) (*rbac.RoleList, error)
|
||||
CreateRole(ctx api.Context, role *rbac.Role) error
|
||||
UpdateRole(ctx api.Context, role *rbac.Role) error
|
||||
GetRole(ctx api.Context, name string) (*rbac.Role, error)
|
||||
DeleteRole(ctx api.Context, name string) error
|
||||
WatchRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListRoles(ctx api.Context, options *api.ListOptions) (*rbac.RoleList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateRole(ctx api.Context, role *rbac.Role) error {
|
||||
_, err := s.Create(ctx, role)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateRole(ctx api.Context, role *rbac.Role) error {
|
||||
_, _, err := s.Update(ctx, role.Name, rest.DefaultUpdatedObjectInfo(role, api.Scheme))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchRoles(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetRole(ctx api.Context, name string) (*rbac.Role, error) {
|
||||
obj, err := s.Get(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.Role), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteRole(ctx api.Context, name string) error {
|
||||
_, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
119
pkg/registry/role/strategy.go
Normal file
119
pkg/registry/role/strategy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 role
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// strategy implements behavior for Roles
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// Role objects.
|
||||
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for Roles.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for Roles.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(obj runtime.Object) {
|
||||
_ = obj.(*rbac.Role)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newRole := obj.(*rbac.Role)
|
||||
oldRole := old.(*rbac.Role)
|
||||
|
||||
_, _ = newRole, oldRole
|
||||
}
|
||||
|
||||
// Validate validates a new Role. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
|
||||
role := obj.(*rbac.Role)
|
||||
return validation.ValidateRole(role)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.Role)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.Role)
|
||||
errorList := validation.ValidateRole(newObj)
|
||||
return append(errorList, validation.ValidateRoleUpdate(newObj, old.(*rbac.Role))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s strategy) Export(obj runtime.Object, exact bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Matcher returns a generic matcher for a given label and field selector.
|
||||
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||
sa, ok := obj.(*rbac.Role)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("not a Role")
|
||||
}
|
||||
fields := SelectableFields(sa)
|
||||
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
|
||||
})
|
||||
}
|
||||
|
||||
// SelectableFields returns a label set that can be used for filter selection
|
||||
func SelectableFields(obj *rbac.Role) labels.Set {
|
||||
return labels.Set{}
|
||||
}
|
19
pkg/registry/rolebinding/doc.go
Normal file
19
pkg/registry/rolebinding/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 certificates provides Registry interface and its RESTStorage
|
||||
// implementation for storing RoleBinding objects.
|
||||
package rolebinding
|
76
pkg/registry/rolebinding/etcd/etcd.go
Normal file
76
pkg/registry/rolebinding/etcd/etcd.go
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 etcd
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/cachesize"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/generic/registry"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
// REST implements a RESTStorage for RoleBinding against etcd
|
||||
type REST struct {
|
||||
*registry.Store
|
||||
}
|
||||
|
||||
// NewREST returns a RESTStorage object that will work against RoleBinding objects.
|
||||
func NewREST(opts generic.RESTOptions) *REST {
|
||||
prefix := "/rolebindings"
|
||||
|
||||
newListFunc := func() runtime.Object { return &rbac.RoleBindingList{} }
|
||||
storageInterface := opts.Decorator(
|
||||
opts.Storage,
|
||||
cachesize.GetWatchCacheSizeByResource(cachesize.RoleBindings),
|
||||
&rbac.RoleBinding{},
|
||||
prefix,
|
||||
rolebinding.Strategy,
|
||||
newListFunc,
|
||||
)
|
||||
|
||||
store := ®istry.Store{
|
||||
NewFunc: func() runtime.Object { return &rbac.RoleBinding{} },
|
||||
NewListFunc: newListFunc,
|
||||
KeyRootFunc: func(ctx api.Context) string {
|
||||
return registry.NamespaceKeyRootFunc(ctx, prefix)
|
||||
},
|
||||
KeyFunc: func(ctx api.Context, id string) (string, error) {
|
||||
return registry.NamespaceKeyFunc(ctx, prefix, id)
|
||||
},
|
||||
ObjectNameFunc: func(obj runtime.Object) (string, error) {
|
||||
return obj.(*rbac.RoleBinding).Name, nil
|
||||
},
|
||||
PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return rolebinding.Matcher(label, field)
|
||||
},
|
||||
QualifiedResource: rbac.Resource("rolebindings"),
|
||||
DeleteCollectionWorkers: opts.DeleteCollectionWorkers,
|
||||
|
||||
CreateStrategy: rolebinding.Strategy,
|
||||
UpdateStrategy: rolebinding.Strategy,
|
||||
DeleteStrategy: rolebinding.Strategy,
|
||||
|
||||
Storage: storageInterface,
|
||||
}
|
||||
|
||||
return &REST{store}
|
||||
}
|
106
pkg/registry/rolebinding/policybased/storage.go
Normal file
106
pkg/registry/rolebinding/policybased/storage.go
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 policybased implements a standard storage for RoleBinding that prevents privilege escalation.
|
||||
package policybased
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/errors"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
var groupResource = rbac.Resource("rolebindings")
|
||||
|
||||
type Storage struct {
|
||||
rest.StandardStorage
|
||||
|
||||
ruleResolver validation.AuthorizationRuleResolver
|
||||
|
||||
// user which skips privilege escalation checks
|
||||
superUser string
|
||||
}
|
||||
|
||||
func NewStorage(s rest.StandardStorage, ruleResolver validation.AuthorizationRuleResolver, superUser string) *Storage {
|
||||
return &Storage{s, ruleResolver, superUser}
|
||||
}
|
||||
|
||||
func (s *Storage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
}
|
||||
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
|
||||
}
|
||||
return s.StandardStorage.Create(ctx, obj)
|
||||
}
|
||||
|
||||
func (s *Storage) Update(ctx api.Context, name string, obj rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
|
||||
if user, ok := api.UserFrom(ctx); ok {
|
||||
if s.superUser != "" && user.GetName() == s.superUser {
|
||||
return s.StandardStorage.Update(ctx, name, obj)
|
||||
}
|
||||
}
|
||||
|
||||
nonEscalatingInfo := wrapUpdatedObjectInfo(obj, func(ctx api.Context, obj runtime.Object, oldObj runtime.Object) (runtime.Object, error) {
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
|
||||
rules, err := s.ruleResolver.GetRoleReferenceRules(ctx, roleBinding.RoleRef, roleBinding.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := validation.ConfirmNoEscalation(ctx, s.ruleResolver, rules); err != nil {
|
||||
return nil, errors.NewForbidden(groupResource, roleBinding.Name, err)
|
||||
}
|
||||
return obj, nil
|
||||
})
|
||||
|
||||
return s.StandardStorage.Update(ctx, name, nonEscalatingInfo)
|
||||
}
|
||||
|
||||
// TODO(ericchiang): This logic is copied from #26240. Replace with once that PR is merged into master.
|
||||
type wrappedUpdatedObjectInfo struct {
|
||||
objInfo rest.UpdatedObjectInfo
|
||||
|
||||
transformFunc rest.TransformFunc
|
||||
}
|
||||
|
||||
func wrapUpdatedObjectInfo(objInfo rest.UpdatedObjectInfo, transformFunc rest.TransformFunc) rest.UpdatedObjectInfo {
|
||||
return &wrappedUpdatedObjectInfo{objInfo, transformFunc}
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
|
||||
return i.objInfo.Preconditions()
|
||||
}
|
||||
|
||||
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
|
||||
obj, err := i.objInfo.UpdatedObject(ctx, oldObj)
|
||||
if err != nil {
|
||||
return obj, err
|
||||
}
|
||||
return i.transformFunc(ctx, obj, oldObj)
|
||||
}
|
82
pkg/registry/rolebinding/registry.go
Normal file
82
pkg/registry/rolebinding/registry.go
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 rolebinding
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/watch"
|
||||
)
|
||||
|
||||
// Registry is an interface for things that know how to store RoleBindings.
|
||||
type Registry interface {
|
||||
ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error)
|
||||
CreateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error
|
||||
UpdateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error
|
||||
GetRoleBinding(ctx api.Context, name string) (*rbac.RoleBinding, error)
|
||||
DeleteRoleBinding(ctx api.Context, name string) error
|
||||
WatchRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
|
||||
}
|
||||
|
||||
// storage puts strong typing around storage calls
|
||||
type storage struct {
|
||||
rest.StandardStorage
|
||||
}
|
||||
|
||||
// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
|
||||
// types will panic.
|
||||
func NewRegistry(s rest.StandardStorage) Registry {
|
||||
return &storage{s}
|
||||
}
|
||||
|
||||
func (s *storage) ListRoleBindings(ctx api.Context, options *api.ListOptions) (*rbac.RoleBindingList, error) {
|
||||
obj, err := s.List(ctx, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return obj.(*rbac.RoleBindingList), nil
|
||||
}
|
||||
|
||||
func (s *storage) CreateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error {
|
||||
// TODO(ericchiang): add additional validation
|
||||
_, err := s.Create(ctx, roleBinding)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) UpdateRoleBinding(ctx api.Context, roleBinding *rbac.RoleBinding) error {
|
||||
_, _, err := s.Update(ctx, roleBinding.Name, rest.DefaultUpdatedObjectInfo(roleBinding, api.Scheme))
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *storage) WatchRoleBindings(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
|
||||
return s.Watch(ctx, options)
|
||||
}
|
||||
|
||||
func (s *storage) GetRoleBinding(ctx api.Context, name string) (*rbac.RoleBinding, error) {
|
||||
obj, err := s.Get(ctx, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return obj.(*rbac.RoleBinding), nil
|
||||
}
|
||||
|
||||
func (s *storage) DeleteRoleBinding(ctx api.Context, name string) error {
|
||||
_, err := s.Delete(ctx, name, nil)
|
||||
return err
|
||||
}
|
119
pkg/registry/rolebinding/strategy.go
Normal file
119
pkg/registry/rolebinding/strategy.go
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 rolebinding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/fields"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/runtime"
|
||||
"k8s.io/kubernetes/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
// strategy implements behavior for RoleBindings
|
||||
type strategy struct {
|
||||
runtime.ObjectTyper
|
||||
api.NameGenerator
|
||||
}
|
||||
|
||||
// strategy is the default logic that applies when creating and updating
|
||||
// RoleBinding objects.
|
||||
var Strategy = strategy{api.Scheme, api.SimpleNameGenerator}
|
||||
|
||||
// Strategy should implement rest.RESTCreateStrategy
|
||||
var _ rest.RESTCreateStrategy = Strategy
|
||||
|
||||
// Strategy should implement rest.RESTUpdateStrategy
|
||||
var _ rest.RESTUpdateStrategy = Strategy
|
||||
|
||||
// NamespaceScoped is true for RoleBindings.
|
||||
func (strategy) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// AllowCreateOnUpdate is true for RoleBindings.
|
||||
func (strategy) AllowCreateOnUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// PrepareForCreate clears fields that are not allowed to be set by end users
|
||||
// on creation.
|
||||
func (strategy) PrepareForCreate(obj runtime.Object) {
|
||||
_ = obj.(*rbac.RoleBinding)
|
||||
}
|
||||
|
||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
|
||||
func (strategy) PrepareForUpdate(obj, old runtime.Object) {
|
||||
newRoleBinding := obj.(*rbac.RoleBinding)
|
||||
oldRoleBinding := old.(*rbac.RoleBinding)
|
||||
|
||||
_, _ = newRoleBinding, oldRoleBinding
|
||||
}
|
||||
|
||||
// Validate validates a new RoleBinding. Validation must check for a correct signature.
|
||||
func (strategy) Validate(ctx api.Context, obj runtime.Object) field.ErrorList {
|
||||
roleBinding := obj.(*rbac.RoleBinding)
|
||||
return validation.ValidateRoleBinding(roleBinding)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
func (strategy) Canonicalize(obj runtime.Object) {
|
||||
_ = obj.(*rbac.RoleBinding)
|
||||
}
|
||||
|
||||
// ValidateUpdate is the default update validation for an end user.
|
||||
func (strategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList {
|
||||
newObj := obj.(*rbac.RoleBinding)
|
||||
errorList := validation.ValidateRoleBinding(newObj)
|
||||
return append(errorList, validation.ValidateRoleBindingUpdate(newObj, old.(*rbac.RoleBinding))...)
|
||||
}
|
||||
|
||||
// If AllowUnconditionalUpdate() is true and the object specified by
|
||||
// the user does not have a resource version, then generic Update()
|
||||
// populates it with the latest version. Else, it checks that the
|
||||
// version specified by the user matches the version of latest etcd
|
||||
// object.
|
||||
func (strategy) AllowUnconditionalUpdate() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (s strategy) Export(obj runtime.Object, exact bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Matcher returns a generic matcher for a given label and field selector.
|
||||
func Matcher(label labels.Selector, field fields.Selector) generic.Matcher {
|
||||
return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
|
||||
sa, ok := obj.(*rbac.RoleBinding)
|
||||
if !ok {
|
||||
return false, fmt.Errorf("not a RoleBinding")
|
||||
}
|
||||
fields := SelectableFields(sa)
|
||||
return label.Matches(labels.Set(sa.Labels)) && field.Matches(fields), nil
|
||||
})
|
||||
}
|
||||
|
||||
// SelectableFields returns a label set that can be used for filter selection
|
||||
func SelectableFields(obj *rbac.RoleBinding) labels.Set {
|
||||
return labels.Set{}
|
||||
}
|
79
plugin/pkg/auth/authorizer/rbac/rbac.go
Normal file
79
plugin/pkg/auth/authorizer/rbac/rbac.go
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 rbac implements the authorizer.Authorizer interface using roles base access control.
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac/validation"
|
||||
"k8s.io/kubernetes/pkg/auth/authorizer"
|
||||
"k8s.io/kubernetes/pkg/auth/user"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
)
|
||||
|
||||
type RBACAuthorizer struct {
|
||||
superUser string
|
||||
|
||||
authorizationRuleResolver validation.AuthorizationRuleResolver
|
||||
}
|
||||
|
||||
func (r *RBACAuthorizer) Authorize(attr authorizer.Attributes) error {
|
||||
if r.superUser != "" && attr.GetUserName() == r.superUser {
|
||||
return nil
|
||||
}
|
||||
|
||||
userInfo := &user.DefaultInfo{
|
||||
Name: attr.GetUserName(),
|
||||
Groups: attr.GetGroups(),
|
||||
}
|
||||
|
||||
ctx := api.WithNamespace(api.WithUser(api.NewContext(), userInfo), attr.GetNamespace())
|
||||
|
||||
// Frame the authorization request as a privilege escalation check.
|
||||
var requestedRule rbac.PolicyRule
|
||||
if attr.IsResourceRequest() {
|
||||
requestedRule = rbac.PolicyRule{
|
||||
Verbs: []string{attr.GetVerb()},
|
||||
APIGroups: []string{attr.GetAPIGroup()}, // TODO(ericchiang): add api version here too?
|
||||
Resources: []string{attr.GetResource()},
|
||||
ResourceNames: []string{attr.GetName()},
|
||||
}
|
||||
} else {
|
||||
requestedRule = rbac.PolicyRule{
|
||||
NonResourceURLs: []string{attr.GetPath()},
|
||||
}
|
||||
}
|
||||
|
||||
return validation.ConfirmNoEscalation(ctx, r.authorizationRuleResolver, []rbac.PolicyRule{requestedRule})
|
||||
}
|
||||
|
||||
func New(roleRegistry role.Registry, roleBindingRegistry rolebinding.Registry, clusterRoleRegistry clusterrole.Registry, clusterRoleBindingRegistry clusterrolebinding.Registry, superUser string) (*RBACAuthorizer, error) {
|
||||
authorizer := &RBACAuthorizer{
|
||||
superUser: superUser,
|
||||
authorizationRuleResolver: validation.NewDefaultRuleResolver(
|
||||
roleRegistry,
|
||||
roleBindingRegistry,
|
||||
clusterRoleRegistry,
|
||||
clusterRoleBindingRegistry,
|
||||
),
|
||||
}
|
||||
return authorizer, nil
|
||||
}
|
59
plugin/pkg/auth/authorizer/rbac/rbac_test.go
Normal file
59
plugin/pkg/auth/authorizer/rbac/rbac_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2016 The Kubernetes Authors All rights reserved.
|
||||
|
||||
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 rbac
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/testapi"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrole"
|
||||
clusterroleetcd "k8s.io/kubernetes/pkg/registry/clusterrole/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/clusterrolebinding"
|
||||
clusterrolebindingetcd "k8s.io/kubernetes/pkg/registry/clusterrolebinding/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/generic"
|
||||
"k8s.io/kubernetes/pkg/registry/role"
|
||||
roleetcd "k8s.io/kubernetes/pkg/registry/role/etcd"
|
||||
"k8s.io/kubernetes/pkg/registry/rolebinding"
|
||||
rolebindingetcd "k8s.io/kubernetes/pkg/registry/rolebinding/etcd"
|
||||
"k8s.io/kubernetes/pkg/storage/etcd"
|
||||
"k8s.io/kubernetes/pkg/storage/etcd/etcdtest"
|
||||
etcdtesting "k8s.io/kubernetes/pkg/storage/etcd/testing"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
// NOTE(ericchiang): Can't get this strategy to do reads. Get cryptic "client: etcd cluster is unavailable or misconfigured"
|
||||
// Writes work fine, so use to test storing initial data.
|
||||
server := etcdtesting.NewEtcdTestClientServer(t)
|
||||
defer server.Terminate(t)
|
||||
|
||||
codec := testapi.Groups[rbac.GroupName].StorageCodec()
|
||||
getRESTOptions := func(resource string) generic.RESTOptions {
|
||||
cacheSize := etcdtest.DeserializationCacheSize
|
||||
storage := etcd.NewEtcdStorage(server.Client, codec, resource, false, cacheSize)
|
||||
return generic.RESTOptions{Storage: storage, Decorator: generic.UndecoratedStorage}
|
||||
}
|
||||
|
||||
roleRegistry := role.NewRegistry(roleetcd.NewREST(getRESTOptions("roles")))
|
||||
roleBindingRegistry := rolebinding.NewRegistry(rolebindingetcd.NewREST(getRESTOptions("rolebindings")))
|
||||
clusterRoleRegistry := clusterrole.NewRegistry(clusterroleetcd.NewREST(getRESTOptions("clusterroles")))
|
||||
clusterRoleBindingRegistry := clusterrolebinding.NewRegistry(clusterrolebindingetcd.NewREST(getRESTOptions("clusterrolebindings")))
|
||||
_, err := New(roleRegistry, roleBindingRegistry, clusterRoleRegistry, clusterRoleBindingRegistry, "")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create authorizer: %v", err)
|
||||
}
|
||||
}
|
@@ -35,6 +35,7 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/batch"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/apis/policy"
|
||||
"k8s.io/kubernetes/pkg/apis/rbac"
|
||||
"k8s.io/kubernetes/pkg/apiserver"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/record"
|
||||
@@ -183,6 +184,10 @@ func NewMasterConfig() *master.Config {
|
||||
unversioned.GroupResource{Group: policy.GroupName, Resource: genericapiserver.AllResources},
|
||||
"",
|
||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Policy.Codec(), runtime.ContentTypeJSON))
|
||||
storageFactory.SetSerializer(
|
||||
unversioned.GroupResource{Group: rbac.GroupName, Resource: genericapiserver.AllResources},
|
||||
"",
|
||||
NewSingleContentTypeSerializer(api.Scheme, testapi.Rbac.Codec(), runtime.ContentTypeJSON))
|
||||
|
||||
return &master.Config{
|
||||
Config: &genericapiserver.Config{
|
||||
|
Reference in New Issue
Block a user