diff --git a/cmd/kube-apiserver/app/aggregator.go b/cmd/kube-apiserver/app/aggregator.go index bf7530104da..6261a39fc42 100644 --- a/cmd/kube-apiserver/app/aggregator.go +++ b/cmd/kube-apiserver/app/aggregator.go @@ -186,6 +186,7 @@ var apiVersionPriorities = map[schema.GroupVersion]priority{ {Group: "certificates.k8s.io", Version: "v1beta1"}: {group: 17300, version: 9}, {Group: "networking.k8s.io", Version: "v1"}: {group: 17200, version: 15}, {Group: "policy", Version: "v1beta1"}: {group: 17100, version: 9}, + {Group: "rbac.authorization.k8s.io", Version: "v1"}: {group: 17000, version: 15}, {Group: "rbac.authorization.k8s.io", Version: "v1beta1"}: {group: 17000, version: 12}, {Group: "rbac.authorization.k8s.io", Version: "v1alpha1"}: {group: 17000, version: 9}, {Group: "settings.k8s.io", Version: "v1alpha1"}: {group: 16900, version: 9}, diff --git a/federation/pkg/kubefed/init/init_test.go b/federation/pkg/kubefed/init/init_test.go index 7fc902a1cf5..c6d5b1ea65e 100644 --- a/federation/pkg/kubefed/init/init_test.go +++ b/federation/pkg/kubefed/init/init_test.go @@ -783,7 +783,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na role := rbacv1beta1.Role{ TypeMeta: metav1.TypeMeta{ Kind: "Role", - APIVersion: testapi.Rbac.GroupVersion().String(), + APIVersion: rbacv1beta1.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Name: "federation-system:federation-controller-manager", @@ -805,7 +805,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na rolebinding := rbacv1beta1.RoleBinding{ TypeMeta: metav1.TypeMeta{ Kind: "RoleBinding", - APIVersion: testapi.Rbac.GroupVersion().String(), + APIVersion: rbacv1beta1.SchemeGroupVersion.String(), }, ObjectMeta: metav1.ObjectMeta{ Name: "federation-system:federation-controller-manager", diff --git a/hack/.golint_failures b/hack/.golint_failures index 57d2bc2b388..9a522b95192 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -133,6 +133,7 @@ pkg/apis/policy/v1alpha1 pkg/apis/policy/v1beta1 pkg/apis/policy/validation pkg/apis/rbac +pkg/apis/rbac/v1 pkg/apis/rbac/v1beta1 pkg/apis/rbac/validation pkg/apis/scheduling @@ -523,6 +524,7 @@ staging/src/k8s.io/api/extensions/v1beta1 staging/src/k8s.io/api/imagepolicy/v1alpha1 staging/src/k8s.io/api/networking/v1 staging/src/k8s.io/api/policy/v1beta1 +staging/src/k8s.io/api/rbac/v1 staging/src/k8s.io/api/rbac/v1alpha1 staging/src/k8s.io/api/rbac/v1beta1 staging/src/k8s.io/api/scheduling/v1alpha1 @@ -712,6 +714,8 @@ staging/src/k8s.io/client-go/kubernetes/typed/networking/v1 staging/src/k8s.io/client-go/kubernetes/typed/networking/v1/fake staging/src/k8s.io/client-go/kubernetes/typed/policy/v1beta1 staging/src/k8s.io/client-go/kubernetes/typed/policy/v1beta1/fake +staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1 +staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1/fake staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1 staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1 diff --git a/hack/lib/init.sh b/hack/lib/init.sh index 324fcfcf9fc..a0c71b5d3de 100755 --- a/hack/lib/init.sh +++ b/hack/lib/init.sh @@ -69,6 +69,7 @@ extensions/v1beta1 \ imagepolicy.k8s.io/v1alpha1 \ networking.k8s.io/v1 \ policy/v1beta1 \ +rbac.authorization.k8s.io/v1 \ rbac.authorization.k8s.io/v1beta1 \ rbac.authorization.k8s.io/v1alpha1 \ scheduling.k8s.io/v1alpha1 \ diff --git a/pkg/api/defaulting_test.go b/pkg/api/defaulting_test.go index 562f1fda8b5..6d710b13184 100644 --- a/pkg/api/defaulting_test.go +++ b/pkg/api/defaulting_test.go @@ -128,6 +128,10 @@ func TestDefaulting(t *testing.T) { {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "ClusterRoleBindingList"}: {}, {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBinding"}: {}, {Group: "rbac.authorization.k8s.io", Version: "v1beta1", Kind: "RoleBindingList"}: {}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBinding"}: {}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "ClusterRoleBindingList"}: {}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBinding"}: {}, + {Group: "rbac.authorization.k8s.io", Version: "v1", Kind: "RoleBindingList"}: {}, {Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPreset"}: {}, {Group: "settings.k8s.io", Version: "v1alpha1", Kind: "PodPresetList"}: {}, {Group: "admissionregistration.k8s.io", Version: "v1alpha1", Kind: "InitializerConfiguration"}: {}, diff --git a/pkg/apis/rbac/BUILD b/pkg/apis/rbac/BUILD index 05d8e56eb3d..06ac7a23f48 100644 --- a/pkg/apis/rbac/BUILD +++ b/pkg/apis/rbac/BUILD @@ -38,6 +38,7 @@ filegroup( srcs = [ ":package-srcs", "//pkg/apis/rbac/install:all-srcs", + "//pkg/apis/rbac/v1:all-srcs", "//pkg/apis/rbac/v1alpha1:all-srcs", "//pkg/apis/rbac/v1beta1:all-srcs", "//pkg/apis/rbac/validation:all-srcs", diff --git a/pkg/apis/rbac/install/BUILD b/pkg/apis/rbac/install/BUILD index 17e4a91250e..9ffec7d76de 100644 --- a/pkg/apis/rbac/install/BUILD +++ b/pkg/apis/rbac/install/BUILD @@ -14,6 +14,7 @@ go_library( deps = [ "//pkg/api:go_default_library", "//pkg/apis/rbac:go_default_library", + "//pkg/apis/rbac/v1:go_default_library", "//pkg/apis/rbac/v1alpha1:go_default_library", "//pkg/apis/rbac/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library", diff --git a/pkg/apis/rbac/install/install.go b/pkg/apis/rbac/install/install.go index 4d3c6b61a8e..c787bfacb9d 100644 --- a/pkg/apis/rbac/install/install.go +++ b/pkg/apis/rbac/install/install.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/rbac" + "k8s.io/kubernetes/pkg/apis/rbac/v1" "k8s.io/kubernetes/pkg/apis/rbac/v1alpha1" "k8s.io/kubernetes/pkg/apis/rbac/v1beta1" ) @@ -37,13 +38,24 @@ func init() { func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *registered.APIRegistrationManager, scheme *runtime.Scheme) { if err := announced.NewGroupMetaFactory( &announced.GroupMetaFactoryArgs{ - GroupName: rbac.GroupName, - VersionPreferenceOrder: []string{v1beta1.SchemeGroupVersion.Version, v1alpha1.SchemeGroupVersion.Version}, + GroupName: rbac.GroupName, + // Rollout plan: + // 1.8: + // * announce deprecation of v1alpha1 (people should use v1beta1 or v1) + // 1.9 (once all version-skewed API servers in an HA cluster are capable of reading/writing v1 to etcd): + // * move v1 to the beginning + // * add RBAC objects to update-storage-objects.sh + // * update TestEtcdStoragePath to expect objects to be stored in v1 + // * document that RBAC storage objects should be migrated to ensure storage is a v1-level (via update-storage-objects.sh or otherwise) + // 1.10 (once all stored objects are at v1): + // * remove v1alpha1 + VersionPreferenceOrder: []string{v1beta1.SchemeGroupVersion.Version, v1.SchemeGroupVersion.Version, v1alpha1.SchemeGroupVersion.Version}, ImportPrefix: "k8s.io/api/rbac", RootScopedKinds: sets.NewString("ClusterRole", "ClusterRoleBinding"), AddInternalObjectsToScheme: rbac.AddToScheme, }, announced.VersionToSchemeFunc{ + v1.SchemeGroupVersion.Version: v1.AddToScheme, v1beta1.SchemeGroupVersion.Version: v1beta1.AddToScheme, v1alpha1.SchemeGroupVersion.Version: v1alpha1.AddToScheme, }, diff --git a/pkg/apis/rbac/v1/BUILD b/pkg/apis/rbac/v1/BUILD new file mode 100644 index 00000000000..049e992f706 --- /dev/null +++ b/pkg/apis/rbac/v1/BUILD @@ -0,0 +1,42 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = [ + "defaults.go", + "doc.go", + "helpers.go", + "register.go", + "zz_generated.conversion.go", + "zz_generated.defaults.go", + ], + tags = ["automanaged"], + deps = [ + "//pkg/apis/rbac:go_default_library", + "//vendor/k8s.io/api/rbac/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], +) diff --git a/pkg/apis/rbac/v1/defaults.go b/pkg/apis/rbac/v1/defaults.go new file mode 100644 index 00000000000..7d285a8574c --- /dev/null +++ b/pkg/apis/rbac/v1/defaults.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func addDefaultingFuncs(scheme *runtime.Scheme) error { + return RegisterDefaults(scheme) +} + +func SetDefaults_ClusterRoleBinding(obj *rbacv1.ClusterRoleBinding) { + if len(obj.RoleRef.APIGroup) == 0 { + obj.RoleRef.APIGroup = GroupName + } +} +func SetDefaults_RoleBinding(obj *rbacv1.RoleBinding) { + if len(obj.RoleRef.APIGroup) == 0 { + obj.RoleRef.APIGroup = GroupName + } +} +func SetDefaults_Subject(obj *rbacv1.Subject) { + if len(obj.APIGroup) == 0 { + switch obj.Kind { + case rbacv1.ServiceAccountKind: + obj.APIGroup = "" + case rbacv1.UserKind: + obj.APIGroup = GroupName + case rbacv1.GroupKind: + obj.APIGroup = GroupName + } + } +} diff --git a/pkg/apis/rbac/v1/doc.go b/pkg/apis/rbac/v1/doc.go new file mode 100644 index 00000000000..6dec982b781 --- /dev/null +++ b/pkg/apis/rbac/v1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/rbac +// +k8s:conversion-gen-external-types=../../../../vendor/k8s.io/api/rbac/v1 +// +k8s:defaulter-gen=TypeMeta +// +k8s:defaulter-gen-input=../../../../vendor/k8s.io/api/rbac/v1 + +// +groupName=rbac.authorization.k8s.io +package v1 // import "k8s.io/kubernetes/pkg/apis/rbac/v1" diff --git a/pkg/apis/rbac/v1/helpers.go b/pkg/apis/rbac/v1/helpers.go new file mode 100644 index 00000000000..02484796905 --- /dev/null +++ b/pkg/apis/rbac/v1/helpers.go @@ -0,0 +1,150 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + "fmt" + + rbacv1 "k8s.io/api/rbac/v1" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +k8s:deepcopy-gen=false +// PolicyRuleBuilder let's us attach methods. A no-no for API types. +// We use it to construct rules in code. It's more compact than trying to write them +// out in a literal and allows us to perform some basic checking during construction +type PolicyRuleBuilder struct { + PolicyRule rbacv1.PolicyRule `protobuf:"bytes,1,opt,name=policyRule"` +} + +func NewRule(verbs ...string) *PolicyRuleBuilder { + return &PolicyRuleBuilder{ + PolicyRule: rbacv1.PolicyRule{Verbs: verbs}, + } +} + +func (r *PolicyRuleBuilder) Groups(groups ...string) *PolicyRuleBuilder { + r.PolicyRule.APIGroups = append(r.PolicyRule.APIGroups, groups...) + return r +} + +func (r *PolicyRuleBuilder) Resources(resources ...string) *PolicyRuleBuilder { + r.PolicyRule.Resources = append(r.PolicyRule.Resources, resources...) + return r +} + +func (r *PolicyRuleBuilder) Names(names ...string) *PolicyRuleBuilder { + r.PolicyRule.ResourceNames = append(r.PolicyRule.ResourceNames, names...) + return r +} + +func (r *PolicyRuleBuilder) URLs(urls ...string) *PolicyRuleBuilder { + r.PolicyRule.NonResourceURLs = append(r.PolicyRule.NonResourceURLs, urls...) + return r +} + +func (r *PolicyRuleBuilder) RuleOrDie() rbacv1.PolicyRule { + ret, err := r.Rule() + if err != nil { + panic(err) + } + return ret +} + +func (r *PolicyRuleBuilder) Rule() (rbacv1.PolicyRule, error) { + if len(r.PolicyRule.Verbs) == 0 { + return rbacv1.PolicyRule{}, fmt.Errorf("verbs are required: %#v", r.PolicyRule) + } + + switch { + case len(r.PolicyRule.NonResourceURLs) > 0: + if len(r.PolicyRule.APIGroups) != 0 || len(r.PolicyRule.Resources) != 0 || len(r.PolicyRule.ResourceNames) != 0 { + return rbacv1.PolicyRule{}, fmt.Errorf("non-resource rule may not have apiGroups, resources, or resourceNames: %#v", r.PolicyRule) + } + case len(r.PolicyRule.Resources) > 0: + if len(r.PolicyRule.NonResourceURLs) != 0 { + return rbacv1.PolicyRule{}, fmt.Errorf("resource rule may not have nonResourceURLs: %#v", r.PolicyRule) + } + if len(r.PolicyRule.APIGroups) == 0 { + // this a common bug + return rbacv1.PolicyRule{}, fmt.Errorf("resource rule must have apiGroups: %#v", r.PolicyRule) + } + default: + return rbacv1.PolicyRule{}, fmt.Errorf("a rule must have either nonResourceURLs or resources: %#v", r.PolicyRule) + } + + return r.PolicyRule, nil +} + +// +k8s:deepcopy-gen=false +// ClusterRoleBindingBuilder let's us attach methods. A no-no for API types. +// We use it to construct bindings in code. It's more compact than trying to write them +// out in a literal. +type ClusterRoleBindingBuilder struct { + ClusterRoleBinding rbacv1.ClusterRoleBinding `protobuf:"bytes,1,opt,name=clusterRoleBinding"` +} + +func NewClusterBinding(clusterRoleName string) *ClusterRoleBindingBuilder { + return &ClusterRoleBindingBuilder{ + ClusterRoleBinding: rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{Name: clusterRoleName}, + RoleRef: rbacv1.RoleRef{ + APIGroup: GroupName, + Kind: "ClusterRole", + Name: clusterRoleName, + }, + }, + } +} + +func (r *ClusterRoleBindingBuilder) Groups(groups ...string) *ClusterRoleBindingBuilder { + for _, group := range groups { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, rbacv1.Subject{Kind: rbacv1.GroupKind, Name: group}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) Users(users ...string) *ClusterRoleBindingBuilder { + for _, user := range users { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, rbacv1.Subject{Kind: rbacv1.UserKind, Name: user}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) SAs(namespace string, serviceAccountNames ...string) *ClusterRoleBindingBuilder { + for _, saName := range serviceAccountNames { + r.ClusterRoleBinding.Subjects = append(r.ClusterRoleBinding.Subjects, rbacv1.Subject{Kind: rbacv1.ServiceAccountKind, Namespace: namespace, Name: saName}) + } + return r +} + +func (r *ClusterRoleBindingBuilder) BindingOrDie() rbacv1.ClusterRoleBinding { + ret, err := r.Binding() + if err != nil { + panic(err) + } + return ret +} + +func (r *ClusterRoleBindingBuilder) Binding() (rbacv1.ClusterRoleBinding, error) { + if len(r.ClusterRoleBinding.Subjects) == 0 { + return rbacv1.ClusterRoleBinding{}, fmt.Errorf("subjects are required: %#v", r.ClusterRoleBinding) + } + + return r.ClusterRoleBinding, nil +} diff --git a/pkg/apis/rbac/v1/register.go b/pkg/apis/rbac/v1/register.go new file mode 100644 index 00000000000..ae138c88837 --- /dev/null +++ b/pkg/apis/rbac/v1/register.go @@ -0,0 +1,44 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const GroupName = "rbac.authorization.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + localSchemeBuilder = &rbacv1.SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +func init() { + // We only register manually written functions here. The registration of the + // generated functions takes place in the generated files. The separation + // makes the code compile even when the generated files are missing. + localSchemeBuilder.Register(addDefaultingFuncs) +} diff --git a/pkg/generated/openapi/BUILD b/pkg/generated/openapi/BUILD index 6d867fedab7..280367f267b 100644 --- a/pkg/generated/openapi/BUILD +++ b/pkg/generated/openapi/BUILD @@ -34,6 +34,7 @@ openapi_library( "k8s.io/api/imagepolicy/v1alpha1", "k8s.io/api/networking/v1", "k8s.io/api/policy/v1beta1", + "k8s.io/api/rbac/v1", "k8s.io/api/rbac/v1alpha1", "k8s.io/api/rbac/v1beta1", "k8s.io/api/scheduling/v1alpha1", diff --git a/pkg/master/master.go b/pkg/master/master.go index 22886c75020..107c4aabd09 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -36,6 +36,7 @@ import ( extensionsapiv1beta1 "k8s.io/api/extensions/v1beta1" networkingapiv1 "k8s.io/api/networking/v1" policyapiv1beta1 "k8s.io/api/policy/v1beta1" + rbacv1 "k8s.io/api/rbac/v1" rbacv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacv1beta1 "k8s.io/api/rbac/v1beta1" schedulingapiv1alpha1 "k8s.io/api/scheduling/v1alpha1" @@ -392,6 +393,7 @@ func DefaultAPIResourceConfigSource() *serverstorage.ResourceConfig { // TODO: enable apps/v1beta2 by default before 1.8 release, after the API changes are done // appsv1beta2.SchemeGroupVersion, policyapiv1beta1.SchemeGroupVersion, + rbacv1.SchemeGroupVersion, rbacv1beta1.SchemeGroupVersion, // Don't copy this pattern. We enable rbac/v1alpha1 and settings/v1laph1 // by default only because they were enabled in previous releases. diff --git a/pkg/registry/rbac/rest/BUILD b/pkg/registry/rbac/rest/BUILD index 38d373335f6..4bb65571784 100644 --- a/pkg/registry/rbac/rest/BUILD +++ b/pkg/registry/rbac/rest/BUILD @@ -33,6 +33,7 @@ go_library( "//pkg/registry/rbac/validation:go_default_library", "//plugin/pkg/auth/authorizer/rbac/bootstrappolicy:go_default_library", "//vendor/github.com/golang/glog:go_default_library", + "//vendor/k8s.io/api/rbac/v1:go_default_library", "//vendor/k8s.io/api/rbac/v1alpha1:go_default_library", "//vendor/k8s.io/api/rbac/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", diff --git a/pkg/registry/rbac/rest/storage_rbac.go b/pkg/registry/rbac/rest/storage_rbac.go index cdb38b5328b..663f6233a09 100644 --- a/pkg/registry/rbac/rest/storage_rbac.go +++ b/pkg/registry/rbac/rest/storage_rbac.go @@ -23,6 +23,7 @@ import ( "github.com/golang/glog" + rbacapiv1 "k8s.io/api/rbac/v1" rbacapiv1alpha1 "k8s.io/api/rbac/v1alpha1" rbacapiv1beta1 "k8s.io/api/rbac/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -73,6 +74,11 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1alpha1.SchemeGroupVersion.Version] = p.storage(rbacapiv1alpha1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter) apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1alpha1.SchemeGroupVersion } + // TODO: move this after v1beta1 in 1.9, so RBAC objects write to storage in v1 + if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1.SchemeGroupVersion) { + apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1.SchemeGroupVersion.Version] = p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter) + apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1.SchemeGroupVersion + } if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1beta1.SchemeGroupVersion) { apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1beta1.SchemeGroupVersion.Version] = p.storage(rbacapiv1beta1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter) apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1beta1.SchemeGroupVersion diff --git a/staging/src/k8s.io/api/rbac/v1/BUILD b/staging/src/k8s.io/api/rbac/v1/BUILD new file mode 100644 index 00000000000..78423d91765 --- /dev/null +++ b/staging/src/k8s.io/api/rbac/v1/BUILD @@ -0,0 +1,31 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", +) + +go_library( + name = "go_default_library", + srcs = [ + "doc.go", + "generated.pb.go", + "register.go", + "types.generated.go", + "types.go", + "types_swagger_doc_generated.go", + "zz_generated.deepcopy.go", + ], + tags = ["automanaged"], + deps = [ + "//vendor/github.com/gogo/protobuf/proto:go_default_library", + "//vendor/github.com/ugorji/go/codec:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", + ], +) diff --git a/staging/src/k8s.io/api/rbac/v1/doc.go b/staging/src/k8s.io/api/rbac/v1/doc.go new file mode 100644 index 00000000000..737261a097f --- /dev/null +++ b/staging/src/k8s.io/api/rbac/v1/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package,register +// +k8s:openapi-gen=true + +// +groupName=rbac.authorization.k8s.io +package v1 // import "k8s.io/api/rbac/v1" diff --git a/staging/src/k8s.io/api/rbac/v1/register.go b/staging/src/k8s.io/api/rbac/v1/register.go new file mode 100644 index 00000000000..7336b5455e6 --- /dev/null +++ b/staging/src/k8s.io/api/rbac/v1/register.go @@ -0,0 +1,58 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +const GroupName = "rbac.authorization.k8s.io" + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + // TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api. + // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + localSchemeBuilder = &SchemeBuilder + AddToScheme = localSchemeBuilder.AddToScheme +) + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &Role{}, + &RoleBinding{}, + &RoleBindingList{}, + &RoleList{}, + + &ClusterRole{}, + &ClusterRoleBinding{}, + &ClusterRoleBindingList{}, + &ClusterRoleList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/staging/src/k8s.io/api/rbac/v1/types.go b/staging/src/k8s.io/api/rbac/v1/types.go new file mode 100644 index 00000000000..8dbd1a8b89a --- /dev/null +++ b/staging/src/k8s.io/api/rbac/v1/types.go @@ -0,0 +1,219 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// Authorization is calculated against +// 1. evaluation of ClusterRoleBindings - short circuit on match +// 2. evaluation of RoleBindings in the namespace requested - short circuit on match +// 3. deny by default + +const ( + APIGroupAll = "*" + ResourceAll = "*" + VerbAll = "*" + NonResourceAll = "*" + + GroupKind = "Group" + ServiceAccountKind = "ServiceAccount" + UserKind = "User" + + // AutoUpdateAnnotationKey is the name of an annotation which prevents reconciliation if set to "false" + AutoUpdateAnnotationKey = "rbac.authorization.kubernetes.io/autoupdate" +) + +// Authorization is calculated against +// 1. evaluation of ClusterRoleBindings - short circuit on match +// 2. evaluation of RoleBindings in the namespace requested - short circuit on match +// 3. deny by default + +// PolicyRule holds information that describes a policy rule, but does not contain information +// about who the rule applies to or which namespace the rule applies to. +type PolicyRule struct { + // Verbs is a list of Verbs that apply to ALL the ResourceKinds and AttributeRestrictions contained in this rule. VerbAll represents all kinds. + Verbs []string `json:"verbs" protobuf:"bytes,1,rep,name=verbs"` + + // APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of + // the enumerated resources in any API group will be allowed. + // +optional + APIGroups []string `json:"apiGroups,omitempty" protobuf:"bytes,2,rep,name=apiGroups"` + // Resources is a list of resources this rule applies to. ResourceAll represents all resources. + // +optional + Resources []string `json:"resources,omitempty" protobuf:"bytes,3,rep,name=resources"` + // ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. + // +optional + ResourceNames []string `json:"resourceNames,omitempty" protobuf:"bytes,4,rep,name=resourceNames"` + + // NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path + // Since non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding. + // Rules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. + // +optional + NonResourceURLs []string `json:"nonResourceURLs,omitempty" protobuf:"bytes,5,rep,name=nonResourceURLs"` +} + +// 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. +type Subject struct { + // 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. + Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"` + // APIGroup holds the API group of the referenced subject. + // Defaults to "" for ServiceAccount subjects. + // Defaults to "rbac.authorization.k8s.io" for User and Group subjects. + // +optional + APIGroup string `json:"apiGroup,omitempty" protobuf:"bytes,2,opt.name=apiGroup"` + // Name of the object being referenced. + Name string `json:"name" protobuf:"bytes,3,opt,name=name"` + // 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. + // +optional + Namespace string `json:"namespace,omitempty" protobuf:"bytes,4,opt,name=namespace"` +} + +// RoleRef contains information that points to the role being used +type RoleRef struct { + // APIGroup is the group for the resource being referenced + APIGroup string `json:"apiGroup" protobuf:"bytes,1,opt,name=apiGroup"` + // Kind is the type of resource being referenced + Kind string `json:"kind" protobuf:"bytes,2,opt,name=kind"` + // Name is the name of resource being referenced + Name string `json:"name" protobuf:"bytes,3,opt,name=name"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// Role is a namespaced, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding. +type Role struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Rules holds all the PolicyRules for this Role + Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// 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. +type RoleBinding struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Subjects holds references to the objects the role applies to. + Subjects []Subject `json:"subjects" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can reference a Role in the current namespace or a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RoleBindingList is a collection of RoleBindings +type RoleBindingList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is a list of RoleBindings + Items []RoleBinding `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// RoleList is a collection of Roles +type RoleList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is a list of Roles + Items []Role `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterRole is a cluster level, logical grouping of PolicyRules that can be referenced as a unit by a RoleBinding or ClusterRoleBinding. +type ClusterRole struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Rules holds all the PolicyRules for this ClusterRole + Rules []PolicyRule `json:"rules" protobuf:"bytes,2,rep,name=rules"` +} + +// +genclient +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// 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 { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Subjects holds references to the objects the role applies to. + Subjects []Subject `json:"subjects" protobuf:"bytes,2,rep,name=subjects"` + + // RoleRef can only reference a ClusterRole in the global namespace. + // If the RoleRef cannot be resolved, the Authorizer must return an error. + RoleRef RoleRef `json:"roleRef" protobuf:"bytes,3,opt,name=roleRef"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterRoleBindingList is a collection of ClusterRoleBindings +type ClusterRoleBindingList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is a list of ClusterRoleBindings + Items []ClusterRoleBinding `json:"items" protobuf:"bytes,2,rep,name=items"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterRoleList is a collection of ClusterRoles +type ClusterRoleList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // +optional + metav1.ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` + + // Items is a list of ClusterRoles + Items []ClusterRole `json:"items" protobuf:"bytes,2,rep,name=items"` +} diff --git a/test/e2e/examples.go b/test/e2e/examples.go index c3c06521f84..ed7e9931090 100644 --- a/test/e2e/examples.go +++ b/test/e2e/examples.go @@ -72,7 +72,7 @@ var _ = framework.KubeDescribe("[Feature:Example]", func() { // this test wants powerful permissions. Since the namespace names are unique, we can leave this // lying around so we don't have to race any caches - framework.BindClusterRoleInNamespace(c.Rbac(), "edit", f.Namespace.Name, + framework.BindClusterRoleInNamespace(c.RbacV1beta1(), "edit", f.Namespace.Name, rbacv1beta1.Subject{Kind: rbacv1beta1.ServiceAccountKind, Namespace: f.Namespace.Name, Name: "default"}) err := framework.WaitForAuthorizationUpdate(c.AuthorizationV1beta1(), diff --git a/test/e2e/kubectl/kubectl.go b/test/e2e/kubectl/kubectl.go index 0a5d54d1073..f002b5c9e06 100644 --- a/test/e2e/kubectl/kubectl.go +++ b/test/e2e/kubectl/kubectl.go @@ -614,7 +614,7 @@ var _ = SIGDescribe("Kubectl client", func() { It("should handle in-cluster config", func() { By("adding rbac permissions") // grant the view permission widely to allow inspection of the `invalid` namespace and the default namespace - framework.BindClusterRole(f.ClientSet.Rbac(), "view", f.Namespace.Name, + framework.BindClusterRole(f.ClientSet.RbacV1beta1(), "view", f.Namespace.Name, rbacv1beta1.Subject{Kind: rbacv1beta1.ServiceAccountKind, Namespace: f.Namespace.Name, Name: "default"}) err := framework.WaitForAuthorizationUpdate(f.ClientSet.AuthorizationV1beta1(), diff --git a/test/e2e/network/ingress.go b/test/e2e/network/ingress.go index 14bed30dbe4..607ac4d3dbf 100644 --- a/test/e2e/network/ingress.go +++ b/test/e2e/network/ingress.go @@ -48,7 +48,7 @@ var _ = SIGDescribe("Loadbalancing: L7", func() { // this test wants powerful permissions. Since the namespace names are unique, we can leave this // lying around so we don't have to race any caches - framework.BindClusterRole(jig.Client.Rbac(), "cluster-admin", f.Namespace.Name, + framework.BindClusterRole(jig.Client.RbacV1beta1(), "cluster-admin", f.Namespace.Name, rbacv1beta1.Subject{Kind: rbacv1beta1.ServiceAccountKind, Namespace: f.Namespace.Name, Name: "default"}) err := framework.WaitForAuthorizationUpdate(jig.Client.AuthorizationV1beta1(), diff --git a/test/e2e/storage/volume_provisioning.go b/test/e2e/storage/volume_provisioning.go index b6f0f62e460..5b038f8dbc7 100644 --- a/test/e2e/storage/volume_provisioning.go +++ b/test/e2e/storage/volume_provisioning.go @@ -521,7 +521,7 @@ var _ = SIGDescribe("Dynamic Provisioning", func() { It("should let an external dynamic provisioner create and delete persistent volumes [Slow]", func() { // external dynamic provisioner pods need additional permissions provided by the // persistent-volume-provisioner role - framework.BindClusterRole(c.Rbac(), "system:persistent-volume-provisioner", ns, + framework.BindClusterRole(c.RbacV1beta1(), "system:persistent-volume-provisioner", ns, rbacv1beta1.Subject{Kind: rbacv1beta1.ServiceAccountKind, Namespace: ns, Name: "default"}) err := framework.WaitForAuthorizationUpdate(c.AuthorizationV1beta1(), diff --git a/test/integration/auth/rbac_test.go b/test/integration/auth/rbac_test.go index 36283a2afc3..58093392b0b 100644 --- a/test/integration/auth/rbac_test.go +++ b/test/integration/auth/rbac_test.go @@ -162,9 +162,12 @@ func (s statusCode) String() string { // Declare a set of raw objects to use. var ( + // Make a role binding with the version enabled in testapi.Rbac + // This assumes testapi is using rbac.authorization.k8s.io/v1beta1 or rbac.authorization.k8s.io/v1, which are identical in structure. + // TODO: rework or remove testapi usage to allow writing integration tests that don't depend on envvars writeJobsRoleBinding = ` { - "apiVersion": "rbac.authorization.k8s.io/v1beta1", + "apiVersion": "` + testapi.Rbac.GroupVersion().String() + `", "kind": "RoleBinding", "metadata": { "name": "pi"%s diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index 61c87c83b2d..7611c7cd656 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -317,6 +317,29 @@ var etcdStorageData = map[schema.GroupVersionResource]struct { }, // -- + // k8s.io/kubernetes/pkg/apis/rbac/v1 + gvr("rbac.authorization.k8s.io", "v1", "roles"): { + stub: `{"metadata": {"name": "role3"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`, + expectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role3", + expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "Role"), + }, + gvr("rbac.authorization.k8s.io", "v1", "clusterroles"): { + stub: `{"metadata": {"name": "crole3"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`, + expectedEtcdPath: "/registry/clusterroles/crole3", + expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "ClusterRole"), + }, + gvr("rbac.authorization.k8s.io", "v1", "rolebindings"): { + stub: `{"metadata": {"name": "roleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, + expectedEtcdPath: "/registry/rolebindings/etcdstoragepathtestnamespace/roleb3", + expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "RoleBinding"), + }, + gvr("rbac.authorization.k8s.io", "v1", "clusterrolebindings"): { + stub: `{"metadata": {"name": "croleb3"}, "roleRef": {"apiGroup": "rbac.authorization.k8s.io", "kind": "ClusterRole", "name": "somecr"}, "subjects": [{"apiVersion": "rbac.authorization.k8s.io/v1alpha1", "kind": "Group", "name": "system:authenticated"}]}`, + expectedEtcdPath: "/registry/clusterrolebindings/croleb3", + expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "ClusterRoleBinding"), + }, + // -- + // k8s.io/kubernetes/pkg/apis/admissionregistration/v1alpha1 gvr("admissionregistration.k8s.io", "v1alpha1", "initializerconfigurations"): { stub: `{"metadata":{"name":"ic1"},"initializers":[{"name":"initializer.k8s.io","rules":[{"apiGroups":["group"],"apiVersions":["version"],"resources":["resource"]}],"failurePolicy":"Ignore"}]}`,