Merge pull request #52950 from liggitt/persist-rbac-v1

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

Change RBAC storage version to v1 for 1.9

v1 was introduced in 1.8, but storage version remained at v1beta1 to accommodate HA rolling upgrades. in 1.9, we can change the persisted and preferred version to v1

```release-note
RBAC objects are now stored in etcd in v1 format. After completing an upgrade to 1.9, RBAC objects (Roles, RoleBindings, ClusterRoles, ClusterRoleBindings) should be migrated to ensure all persisted objects are written in `v1` format, prior to `v1alpha1` support being removed in a future release.
```
This commit is contained in:
Kubernetes Submit Queue 2017-10-03 00:24:31 -07:00 committed by GitHub
commit aa7d9b1da9
10 changed files with 52 additions and 56 deletions

View File

@ -105,14 +105,14 @@
"path": "/apis/policy",
"description": "get information of a group"
},
{
"path": "/apis/rbac.authorization.k8s.io/v1beta1",
"description": "API at /apis/rbac.authorization.k8s.io/v1beta1"
},
{
"path": "/apis/rbac.authorization.k8s.io/v1",
"description": "API at /apis/rbac.authorization.k8s.io/v1"
},
{
"path": "/apis/rbac.authorization.k8s.io/v1beta1",
"description": "API at /apis/rbac.authorization.k8s.io/v1beta1"
},
{
"path": "/apis/rbac.authorization.k8s.io/v1alpha1",
"description": "API at /apis/rbac.authorization.k8s.io/v1alpha1"

View File

@ -49,6 +49,10 @@ declare -a resources=(
"jobs"
"horizontalpodautoscalers"
"storageclasses"
"roles.rbac.authorization.k8s.io"
"rolebindings.rbac.authorization.k8s.io"
"clusterroles.rbac.authorization.k8s.io"
"clusterrolebindings.rbac.authorization.k8s.io"
)
# Find all the namespaces.

View File

@ -57,13 +57,13 @@ go_test(
"//federation/pkg/kubefed/util:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/apis/rbac/v1beta1:go_default_library",
"//pkg/apis/rbac/v1:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",

View File

@ -55,7 +55,7 @@ go_test(
"//vendor/gopkg.in/gcfg.v1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",

View File

@ -35,7 +35,7 @@ import (
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/resource"
@ -785,10 +785,10 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
},
}
role := rbacv1beta1.Role{
role := rbacv1.Role{
TypeMeta: metav1.TypeMeta{
Kind: "Role",
APIVersion: rbacv1beta1.SchemeGroupVersion.String(),
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "federation-system:federation-controller-manager",
@ -798,7 +798,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
federation.FederationNameAnnotation: federationName,
},
},
Rules: []rbacv1beta1.PolicyRule{
Rules: []rbacv1.PolicyRule{
{
Verbs: []string{"get", "list", "watch"},
APIGroups: []string{""},
@ -807,10 +807,10 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
},
}
rolebinding := rbacv1beta1.RoleBinding{
rolebinding := rbacv1.RoleBinding{
TypeMeta: metav1.TypeMeta{
Kind: "RoleBinding",
APIVersion: rbacv1beta1.SchemeGroupVersion.String(),
APIVersion: rbacv1.SchemeGroupVersion.String(),
},
ObjectMeta: metav1.ObjectMeta{
Name: "federation-system:federation-controller-manager",
@ -820,7 +820,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
federation.FederationNameAnnotation: federationName,
},
},
Subjects: []rbacv1beta1.Subject{
Subjects: []rbacv1.Subject{
{
Kind: "ServiceAccount",
APIGroup: "",
@ -828,7 +828,7 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
Namespace: "federation-system",
},
},
RoleRef: rbacv1beta1.RoleRef{
RoleRef: rbacv1.RoleRef{
APIGroup: "rbac.authorization.k8s.io",
Kind: "Role",
Name: "federation-system:federation-controller-manager",
@ -1137,8 +1137,8 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
Name: rbac.GroupName,
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: rbac.GroupName + "/v1beta1",
Version: "v1beta1",
GroupVersion: rbac.GroupName + "/v1",
Version: "v1",
},
},
}
@ -1296,12 +1296,12 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
return nil, fmt.Errorf("unexpected service account object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, sa))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(codec, &sa)}, nil
case p == "/apis/rbac.authorization.k8s.io/v1beta1/namespaces/federation-system/roles" && m == http.MethodPost:
case p == "/apis/rbac.authorization.k8s.io/v1/namespaces/federation-system/roles" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got rbacv1beta1.Role
var got rbacv1.Role
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err
@ -1310,12 +1310,12 @@ func fakeInitHostFactory(apiserverServiceType v1.ServiceType, federationName, na
return nil, fmt.Errorf("unexpected role object\n\tDiff: %s", diff.ObjectGoPrintDiff(got, role))
}
return &http.Response{StatusCode: http.StatusCreated, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(rbacCodec, &role)}, nil
case p == "/apis/rbac.authorization.k8s.io/v1beta1/namespaces/federation-system/rolebindings" && m == http.MethodPost:
case p == "/apis/rbac.authorization.k8s.io/v1/namespaces/federation-system/rolebindings" && m == http.MethodPost:
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return nil, err
}
var got rbacv1beta1.RoleBinding
var got rbacv1.RoleBinding
_, _, err = codec.Decode(body, nil, &got)
if err != nil {
return nil, err

View File

@ -26,7 +26,7 @@ import (
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
rbacv1 "k8s.io/api/rbac/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
@ -40,7 +40,7 @@ import (
"k8s.io/kubernetes/federation/pkg/kubefed/util"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
k8srbacv1beta1 "k8s.io/kubernetes/pkg/apis/rbac/v1beta1"
k8srbacv1 "k8s.io/kubernetes/pkg/apis/rbac/v1"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -477,7 +477,7 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPa
}
roleName := util.ClusterRoleName(testFederationName, saName)
clusterRole := rbacv1beta1.ClusterRole{
clusterRole := rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: roleName,
Namespace: util.DefaultFederationSystemNamespace,
@ -486,12 +486,12 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPa
federation.ClusterNameAnnotation: clusterName,
},
},
Rules: []rbacv1beta1.PolicyRule{
k8srbacv1beta1.NewRule(rbacv1beta1.VerbAll).Groups(rbacv1beta1.APIGroupAll).Resources(rbacv1beta1.ResourceAll).RuleOrDie(),
Rules: []rbacv1.PolicyRule{
k8srbacv1.NewRule(rbacv1.VerbAll).Groups(rbacv1.APIGroupAll).Resources(rbacv1.ResourceAll).RuleOrDie(),
},
}
clusterRoleBinding, err := k8srbacv1beta1.NewClusterBinding(roleName).SAs(util.DefaultFederationSystemNamespace, saName).Binding()
clusterRoleBinding, err := k8srbacv1.NewClusterBinding(roleName).SAs(util.DefaultFederationSystemNamespace, saName).Binding()
if err != nil {
return nil, err
}
@ -509,11 +509,11 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPa
apiGroupList.Groups = append(apiGroupList.Groups, testGroup)
if isRBACAPIAvailable {
rbacGroup := metav1.APIGroup{
Name: rbacv1beta1.GroupName,
Name: rbacv1.GroupName,
Versions: []metav1.GroupVersionForDiscovery{
{
GroupVersion: rbacv1beta1.GroupName + "/v1beta1",
Version: "v1beta1",
GroupVersion: rbacv1.GroupName + "/v1",
Version: "v1",
},
},
}
@ -544,9 +544,9 @@ func fakeJoinTargetClusterFactory(clusterName, clusterCtx, dnsProvider, tmpDirPa
case p == "/api/v1/namespaces/federation-system/serviceaccounts" && m == http.MethodPost && r:
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(defaultCodec, &serviceAccount)}, nil
case p == "/apis/rbac.authorization.k8s.io/v1beta1/clusterroles" && m == http.MethodPost && r:
case p == "/apis/rbac.authorization.k8s.io/v1/clusterroles" && m == http.MethodPost && r:
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(rbacCodec, &clusterRole)}, nil
case p == "/apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindings" && m == http.MethodPost && r:
case p == "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings" && m == http.MethodPost && r:
return &http.Response{StatusCode: http.StatusOK, Header: kubefedtesting.DefaultHeader(), Body: kubefedtesting.ObjBody(rbacCodec, &clusterRoleBinding)}, nil
case p == "/api/v1/namespaces/federation-system/secrets/serviceaccountsecret" && m == http.MethodGet && r:

View File

@ -217,9 +217,9 @@ func testUnjoinFederationFactory(name, server, secret string) cmdutil.Factory {
func fakeUnjoinHostFactory(clusterName string) cmdutil.Factory {
secretsPrefix := "/api/v1/namespaces/federation-system/secrets/"
clusterRolePrefix := "/apis/rbac.authorization.k8s.io/v1beta1/clusterroles/"
clusterRolePrefix := "/apis/rbac.authorization.k8s.io/v1/clusterroles/"
serviceAccountPrefix := "/api/v1/namespaces/federation-system/serviceaccounts/"
clusterRoleBindingPrefix := "/apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindings/"
clusterRoleBindingPrefix := "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/"
// Using dummy bytes for now
configBytes, _ := clientcmd.Write(clientcmdapi.Config{})

View File

@ -40,16 +40,9 @@ func Install(groupFactoryRegistry announced.APIGroupFactoryRegistry, registry *r
&announced.GroupMetaFactoryArgs{
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},
// * remove v1alpha1 (announced deprecated in 1.8)
VersionPreferenceOrder: []string{v1.SchemeGroupVersion.Version, v1beta1.SchemeGroupVersion.Version, v1alpha1.SchemeGroupVersion.Version},
RootScopedKinds: sets.NewString("ClusterRole", "ClusterRoleBinding"),
AddInternalObjectsToScheme: rbac.AddToScheme,
},

View File

@ -74,15 +74,14 @@ 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
}
if apiResourceConfigSource.AnyResourcesForVersionEnabled(rbacapiv1.SchemeGroupVersion) {
apiGroupInfo.VersionedResourcesStorageMap[rbacapiv1.SchemeGroupVersion.Version] = p.storage(rbacapiv1.SchemeGroupVersion, apiResourceConfigSource, restOptionsGetter)
apiGroupInfo.GroupMeta.GroupVersion = rbacapiv1.SchemeGroupVersion
}
return apiGroupInfo, true
}

View File

@ -286,22 +286,22 @@ var etcdStorageData = map[schema.GroupVersionResource]struct {
gvr("rbac.authorization.k8s.io", "v1alpha1", "roles"): {
stub: `{"metadata": {"name": "role1"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
expectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role1",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "Role"),
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterroles"): {
stub: `{"metadata": {"name": "crole1"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`,
expectedEtcdPath: "/registry/clusterroles/crole1",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "ClusterRole"),
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "rolebindings"): {
stub: `{"metadata": {"name": "roleb1"}, "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/roleb1",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "RoleBinding"),
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"),
},
gvr("rbac.authorization.k8s.io", "v1alpha1", "clusterrolebindings"): {
stub: `{"metadata": {"name": "croleb1"}, "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/croleb1",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1beta1", "ClusterRoleBinding"),
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
},
// --
@ -309,18 +309,22 @@ var etcdStorageData = map[schema.GroupVersionResource]struct {
gvr("rbac.authorization.k8s.io", "v1beta1", "roles"): {
stub: `{"metadata": {"name": "role2"}, "rules": [{"apiGroups": ["v1"], "resources": ["events"], "verbs": ["watch"]}]}`,
expectedEtcdPath: "/registry/roles/etcdstoragepathtestnamespace/role2",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "Role"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "clusterroles"): {
stub: `{"metadata": {"name": "crole2"}, "rules": [{"nonResourceURLs": ["/version"], "verbs": ["get"]}]}`,
expectedEtcdPath: "/registry/clusterroles/crole2",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRole"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "rolebindings"): {
stub: `{"metadata": {"name": "roleb2"}, "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/roleb2",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "RoleBinding"),
},
gvr("rbac.authorization.k8s.io", "v1beta1", "clusterrolebindings"): {
stub: `{"metadata": {"name": "croleb2"}, "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/croleb2",
expectedGVK: gvkP("rbac.authorization.k8s.io", "v1", "ClusterRoleBinding"),
},
// --
@ -328,22 +332,18 @@ var etcdStorageData = map[schema.GroupVersionResource]struct {
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"),
},
// --