Merge pull request #72942 from caesarxuchao/expose-storage-version-hash

Populate the storage version hash
This commit is contained in:
Kubernetes Prow Robot
2019-03-11 21:36:36 -07:00
committed by GitHub
29 changed files with 553 additions and 23 deletions

View File

@@ -141,10 +141,13 @@ go_test(
deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/generated/openapi:go_default_library",
"//pkg/kubelet/client:go_default_library",
"//pkg/master/reconcilers:go_default_library",
"//pkg/master/storageversionhashdata:go_default_library",
"//pkg/registry/certificates/rest:go_default_library",
"//pkg/registry/core/rest:go_default_library",
"//pkg/registry/registrytest:go_default_library",
@@ -154,16 +157,23 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/api/apitesting/naming:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/intstr:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/options:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/resourceconfig:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/server/storage:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature/testing:go_default_library",
"//staging/src/k8s.io/client-go/discovery:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
@@ -191,6 +201,7 @@ filegroup(
"//pkg/master/controller/crdregistration:all-srcs",
"//pkg/master/ports:all-srcs",
"//pkg/master/reconcilers:all-srcs",
"//pkg/master/storageversionhashdata:all-srcs",
"//pkg/master/tunneler:all-srcs",
],
tags = ["automanaged"],

View File

@@ -31,22 +31,32 @@ import (
certificatesapiv1beta1 "k8s.io/api/certificates/v1beta1"
apiv1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/version"
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/features"
genericapiserver "k8s.io/apiserver/pkg/server"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/server/resourceconfig"
serverstorage "k8s.io/apiserver/pkg/server/storage"
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
"k8s.io/client-go/discovery"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/apis/batch"
api "k8s.io/kubernetes/pkg/apis/core"
apisstorage "k8s.io/kubernetes/pkg/apis/storage"
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
"k8s.io/kubernetes/pkg/master/reconcilers"
"k8s.io/kubernetes/pkg/master/storageversionhashdata"
certificatesrest "k8s.io/kubernetes/pkg/registry/certificates/rest"
corerest "k8s.io/kubernetes/pkg/registry/core/rest"
"k8s.io/kubernetes/pkg/registry/registrytest"
@@ -70,6 +80,14 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
}
resourceEncoding := serverstorage.NewDefaultResourceEncodingConfig(legacyscheme.Scheme)
// This configures the testing master the same way the real master is
// configured. The storage versions of these resources are different
// from the storage versions of other resources in their group.
resourceEncodingOverrides := []schema.GroupVersionResource{
batch.Resource("cronjobs").WithVersion("v1beta1"),
apisstorage.Resource("volumeattachments").WithVersion("v1beta1"),
}
resourceEncoding = resourceconfig.MergeResourceEncodingConfigs(resourceEncoding, resourceEncodingOverrides)
storageFactory := serverstorage.NewDefaultStorageFactory(*storageConfig, testapi.StorageMediaType(), legacyscheme.Codecs, resourceEncoding, DefaultAPIResourceConfigSource(), nil)
etcdOptions := options.NewEtcdOptions(storageConfig)
@@ -81,12 +99,12 @@ func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertion
}
kubeVersion := kubeversion.Get()
config.GenericConfig.Authorization.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
config.GenericConfig.Version = &kubeVersion
config.ExtraConfig.StorageFactory = storageFactory
config.GenericConfig.LoopbackClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
config.GenericConfig.PublicAddress = net.ParseIP("192.168.10.4")
config.GenericConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
config.GenericConfig.LoopbackClientConfig = &restclient.Config{APIPath: "/api", ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs}}
config.ExtraConfig.KubeletClientConfig = kubeletclient.KubeletClientConfig{Port: 10250}
config.ExtraConfig.ProxyTransport = utilnet.SetTransportDefaults(&http.Transport{
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return nil, nil },
@@ -363,6 +381,112 @@ func TestAPIVersionOfDiscoveryEndpoints(t *testing.T) {
}
// This test doesn't cover the apiregistration and apiextensions group, as they are installed by other apiservers.
func TestStorageVersionHashes(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StorageVersionHash, true)()
master, etcdserver, _, _ := newMaster(t)
defer etcdserver.Terminate(t)
server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux)
c := &restclient.Config{
Host: server.URL,
APIPath: "/api",
ContentConfig: restclient.ContentConfig{NegotiatedSerializer: legacyscheme.Codecs},
}
discover := discovery.NewDiscoveryClientForConfigOrDie(c)
all, err := discover.ServerResources()
if err != nil {
t.Error(err)
}
var count int
for _, g := range all {
for _, r := range g.APIResources {
if strings.Contains(r.Name, "/") ||
storageversionhashdata.NoStorageVersionHash.Has(g.GroupVersion+"/"+r.Name) {
if r.StorageVersionHash != "" {
t.Errorf("expect resource %s/%s to have empty storageVersionHash, got hash %q", g.GroupVersion, r.Name, r.StorageVersionHash)
}
continue
}
if r.StorageVersionHash == "" {
t.Errorf("expect the storageVersionHash of %s/%s to exist", g.GroupVersion, r.Name)
continue
}
// Uncomment the following line if you want to update storageversionhash/data.go
// fmt.Printf("\"%s/%s\": \"%s\",\n", g.GroupVersion, r.Name, r.StorageVersionHash)
expected := storageversionhashdata.GVRToStorageVersionHash[g.GroupVersion+"/"+r.Name]
if r.StorageVersionHash != expected {
t.Errorf("expect the storageVersionHash of %s/%s to be %q, got %q", g.GroupVersion, r.Name, expected, r.StorageVersionHash)
}
count++
}
}
if count != len(storageversionhashdata.GVRToStorageVersionHash) {
t.Errorf("please remove the redundant entries from GVRToStorageVersionHash")
}
}
func TestStorageVersionHashEqualities(t *testing.T) {
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StorageVersionHash, true)()
master, etcdserver, _, assert := newMaster(t)
defer etcdserver.Terminate(t)
server := httptest.NewServer(master.GenericAPIServer.Handler.GoRestfulContainer.ServeMux)
// Test 1: extensions/v1beta1/replicasets and apps/v1/replicasets have
// the same storage version hash.
resp, err := http.Get(server.URL + "/apis/extensions/v1beta1")
assert.Empty(err)
extList := metav1.APIResourceList{}
assert.NoError(decodeResponse(resp, &extList))
var extReplicasetHash, appsReplicasetHash string
for _, r := range extList.APIResources {
if r.Name == "replicasets" {
extReplicasetHash = r.StorageVersionHash
}
}
assert.NotEmpty(extReplicasetHash)
resp, err = http.Get(server.URL + "/apis/apps/v1")
assert.Empty(err)
appsList := metav1.APIResourceList{}
assert.NoError(decodeResponse(resp, &appsList))
for _, r := range appsList.APIResources {
if r.Name == "replicasets" {
appsReplicasetHash = r.StorageVersionHash
}
}
assert.Equal(extReplicasetHash, appsReplicasetHash)
// Test 2: batch/v1/jobs and batch/v1beta1/cronjobs have different
// storage version hashes.
resp, err = http.Get(server.URL + "/apis/batch/v1")
assert.Empty(err)
batchv1 := metav1.APIResourceList{}
assert.NoError(decodeResponse(resp, &batchv1))
var jobsHash string
for _, r := range batchv1.APIResources {
if r.Name == "jobs" {
jobsHash = r.StorageVersionHash
}
}
assert.NotEmpty(jobsHash)
resp, err = http.Get(server.URL + "/apis/batch/v1beta1")
assert.Empty(err)
batchv1beta1 := metav1.APIResourceList{}
assert.NoError(decodeResponse(resp, &batchv1beta1))
var cronjobsHash string
for _, r := range batchv1beta1.APIResources {
if r.Name == "cronjobs" {
cronjobsHash = r.StorageVersionHash
}
}
assert.NotEmpty(cronjobsHash)
assert.NotEqual(jobsHash, cronjobsHash)
}
func TestNoAlphaVersionsEnabledByDefault(t *testing.T) {
config := DefaultAPIResourceConfigSource()
for gv, enable := range config.GroupVersionConfigs {

View File

@@ -0,0 +1,23 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["data.go"],
importpath = "k8s.io/kubernetes/pkg/master/storageversionhashdata",
visibility = ["//visibility:public"],
deps = ["//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,4 @@
approvers:
- api-approvers
reviewers:
- api-reviewers

View File

@@ -0,0 +1,111 @@
/*
Copyright 2019 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 storageversionhashdata is for test only.
package storageversionhashdata
import (
"k8s.io/apimachinery/pkg/util/sets"
)
// NoStorageVersionHash lists resources that legitimately with empty storage
// version hash.
var NoStorageVersionHash = sets.NewString(
"v1/bindings",
"v1/componentstatuses",
"authentication.k8s.io/v1/tokenreviews",
"authorization.k8s.io/v1/localsubjectaccessreviews",
"authorization.k8s.io/v1/selfsubjectaccessreviews",
"authorization.k8s.io/v1/selfsubjectrulesreviews",
"authorization.k8s.io/v1/subjectaccessreviews",
"authentication.k8s.io/v1beta1/tokenreviews",
"authorization.k8s.io/v1beta1/localsubjectaccessreviews",
"authorization.k8s.io/v1beta1/selfsubjectaccessreviews",
"authorization.k8s.io/v1beta1/selfsubjectrulesreviews",
"authorization.k8s.io/v1beta1/subjectaccessreviews",
"extensions/v1beta1/replicationcontrollers",
)
// GVRToStorageVersionHash shouldn't change unless we intentionally change the
// storage version of a resource.
var GVRToStorageVersionHash = map[string]string{
"v1/configmaps": "qFsyl6wFWjQ=",
"v1/endpoints": "fWeeMqaN/OA=",
"v1/events": "r2yiGXH7wu8=",
"v1/limitranges": "EBKMFVe6cwo=",
"v1/namespaces": "Q3oi5N2YM8M=",
"v1/nodes": "XwShjMxG9Fs=",
"v1/persistentvolumeclaims": "QWTyNDq0dC4=",
"v1/persistentvolumes": "HN/zwEC+JgM=",
"v1/pods": "xPOwRZ+Yhw8=",
"v1/podtemplates": "LIXB2x4IFpk=",
"v1/replicationcontrollers": "Jond2If31h0=",
"v1/resourcequotas": "8uhSgffRX6w=",
"v1/secrets": "S6u1pOWzb84=",
"v1/serviceaccounts": "pbx9ZvyFpBE=",
"v1/services": "0/CO1lhkEBI=",
"autoscaling/v1/horizontalpodautoscalers": "oQlkt7f5j/A=",
"autoscaling/v2beta1/horizontalpodautoscalers": "oQlkt7f5j/A=",
"autoscaling/v2beta2/horizontalpodautoscalers": "oQlkt7f5j/A=",
"batch/v1/jobs": "mudhfqk/qZY=",
"batch/v1beta1/cronjobs": "h/JlFAZkyyY=",
"certificates.k8s.io/v1beta1/certificatesigningrequests": "UQh3YTCDIf0=",
"coordination.k8s.io/v1beta1/leases": "/sY7hl8ol1U=",
"coordination.k8s.io/v1/leases": "/sY7hl8ol1U=",
"extensions/v1beta1/daemonsets": "dd7pWHUlMKQ=",
"extensions/v1beta1/deployments": "8aSe+NMegvE=",
"extensions/v1beta1/ingresses": "Ejja63IbU0E=",
"extensions/v1beta1/networkpolicies": "YpfwF18m1G8=",
"extensions/v1beta1/podsecuritypolicies": "khBLobUXkqA=",
"extensions/v1beta1/replicasets": "P1RzHs8/mWQ=",
"networking.k8s.io/v1/networkpolicies": "YpfwF18m1G8=",
"networking.k8s.io/v1beta1/ingresses": "Ejja63IbU0E=",
"node.k8s.io/v1beta1/runtimeclasses": "8nMHWqj34s0=",
"policy/v1beta1/poddisruptionbudgets": "6BGBu0kpHtk=",
"policy/v1beta1/podsecuritypolicies": "khBLobUXkqA=",
"rbac.authorization.k8s.io/v1/clusterrolebindings": "48tpQ8gZHFc=",
"rbac.authorization.k8s.io/v1/clusterroles": "bYE5ZWDrJ44=",
"rbac.authorization.k8s.io/v1/rolebindings": "eGsCzGH6b1g=",
"rbac.authorization.k8s.io/v1/roles": "7FuwZcIIItM=",
"rbac.authorization.k8s.io/v1beta1/clusterrolebindings": "48tpQ8gZHFc=",
"rbac.authorization.k8s.io/v1beta1/clusterroles": "bYE5ZWDrJ44=",
"rbac.authorization.k8s.io/v1beta1/rolebindings": "eGsCzGH6b1g=",
"rbac.authorization.k8s.io/v1beta1/roles": "7FuwZcIIItM=",
"scheduling.k8s.io/v1beta1/priorityclasses": "D3vHs+OgrtA=",
"scheduling.k8s.io/v1/priorityclasses": "D3vHs+OgrtA=",
"storage.k8s.io/v1/storageclasses": "K+m6uJwbjGY=",
"storage.k8s.io/v1/volumeattachments": "vQAqD28V4AY=",
"storage.k8s.io/v1beta1/csidrivers": "hL6j/rwBV5w=",
"storage.k8s.io/v1beta1/csinodes": "Pe62DkZtjuo=",
"storage.k8s.io/v1beta1/storageclasses": "K+m6uJwbjGY=",
"storage.k8s.io/v1beta1/volumeattachments": "vQAqD28V4AY=",
"apps/v1/controllerrevisions": "85nkx63pcBU=",
"apps/v1/daemonsets": "dd7pWHUlMKQ=",
"apps/v1/deployments": "8aSe+NMegvE=",
"apps/v1/replicasets": "P1RzHs8/mWQ=",
"apps/v1/statefulsets": "H+vl74LkKdo=",
"apps/v1beta2/controllerrevisions": "85nkx63pcBU=",
"apps/v1beta2/daemonsets": "dd7pWHUlMKQ=",
"apps/v1beta2/deployments": "8aSe+NMegvE=",
"apps/v1beta2/replicasets": "P1RzHs8/mWQ=",
"apps/v1beta2/statefulsets": "H+vl74LkKdo=",
"apps/v1beta1/controllerrevisions": "85nkx63pcBU=",
"apps/v1beta1/deployments": "8aSe+NMegvE=",
"apps/v1beta1/statefulsets": "H+vl74LkKdo=",
"admissionregistration.k8s.io/v1beta1/mutatingwebhookconfigurations": "yxW1cpLtfp8=",
"admissionregistration.k8s.io/v1beta1/validatingwebhookconfigurations": "P9NhrezfnWE=",
"events.k8s.io/v1beta1/events": "r2yiGXH7wu8=",
}

View File

@@ -226,6 +226,12 @@ func (r *REST) ShortNames() []string {
return []string{"ns"}
}
var _ rest.StorageVersionProvider = &REST{}
func (r *REST) StorageVersion() runtime.GroupVersioner {
return r.store.StorageVersion()
}
func (r *StatusREST) New() runtime.Object {
return r.store.New()
}

View File

@@ -77,6 +77,7 @@ type ServiceStorage interface {
rest.Watcher
rest.TableConvertor
rest.Exporter
rest.StorageVersionProvider
}
type EndpointsStorage interface {
@@ -108,11 +109,16 @@ func NewREST(
}
var (
_ ServiceStorage = &REST{}
_ rest.CategoriesProvider = &REST{}
_ rest.ShortNamesProvider = &REST{}
_ ServiceStorage = &REST{}
_ rest.CategoriesProvider = &REST{}
_ rest.ShortNamesProvider = &REST{}
_ rest.StorageVersionProvider = &REST{}
)
func (rs *REST) StorageVersion() runtime.GroupVersioner {
return rs.services.StorageVersion()
}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (rs *REST) ShortNames() []string {
return []string{"svc"}

View File

@@ -159,6 +159,10 @@ func (s *serviceStorage) Export(ctx context.Context, name string, opts metav1.Ex
panic("not implemented")
}
func (s *serviceStorage) StorageVersion() runtime.GroupVersioner {
panic("not implemented")
}
func generateRandomNodePort() int32 {
return int32(rand.IntnRange(30001, 30999))
}

View File

@@ -50,6 +50,16 @@ func (r *Storage) NamespaceScoped() bool {
return false
}
func (r *Storage) StorageVersion() runtime.GroupVersioner {
svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
if !ok {
return nil
}
return svp.StorageVersion()
}
var _ rest.StorageVersionProvider = &Storage{}
var fullAuthority = []rbac.PolicyRule{
rbac.NewRule("*").Groups("*").Resources("*").RuleOrDie(),
rbac.NewRule("*").URLs("*").RuleOrDie(),

View File

@@ -51,6 +51,16 @@ func (r *Storage) NamespaceScoped() bool {
return false
}
func (r *Storage) StorageVersion() runtime.GroupVersioner {
svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
if !ok {
return nil
}
return svp.StorageVersion()
}
var _ rest.StorageVersionProvider = &Storage{}
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidation, options)

View File

@@ -49,6 +49,16 @@ func (r *Storage) NamespaceScoped() bool {
return true
}
func (r *Storage) StorageVersion() runtime.GroupVersioner {
svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
if !ok {
return nil
}
return svp.StorageVersion()
}
var _ rest.StorageVersionProvider = &Storage{}
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) || rbacregistry.RoleEscalationAuthorized(ctx, s.authorizer) {
return s.StandardStorage.Create(ctx, obj, createValidation, options)

View File

@@ -52,6 +52,16 @@ func (r *Storage) NamespaceScoped() bool {
return true
}
func (r *Storage) StorageVersion() runtime.GroupVersioner {
svp, ok := r.StandardStorage.(rest.StorageVersionProvider)
if !ok {
return nil
}
return svp.StorageVersion()
}
var _ rest.StorageVersionProvider = &Storage{}
func (s *Storage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
if rbacregistry.EscalationAllowed(ctx) {
return s.StandardStorage.Create(ctx, obj, createValidation, options)