mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 03:41:45 +00:00
Merge pull request #54683 from liggitt/subresource-gvk
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>. Fix subresource discovery and versioning Fixes https://github.com/kubernetes/kubernetes/issues/54684 Related to https://github.com/kubernetes/kubernetes/pull/54586 Allows distinct subresource group/version/kind to be used for each version (gives us a path to move to autoscaling/v1 for apps, or policy/v1 for eviction, etc) Added tests to ensure scale subresources have expected discovery info, and that the object returned matches discovery, and that the endpoint accepts the advertised version ```release-note Fixes discovery information for scale subresources in the apps API group ```
This commit is contained in:
commit
0c1f25fc1b
@ -35,6 +35,9 @@ go_library(
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/apps/statefulset/storage",
|
||||
deps = [
|
||||
"//pkg/apis/apps:go_default_library",
|
||||
"//pkg/apis/apps/v1beta1:go_default_library",
|
||||
"//pkg/apis/apps/v1beta2:go_default_library",
|
||||
"//pkg/apis/autoscaling/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/extensions/validation:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
|
@ -28,6 +28,9 @@ import (
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/apis/apps"
|
||||
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
|
||||
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
@ -125,8 +128,15 @@ type ScaleREST struct {
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
var _ = rest.GroupVersionKindProvider(&ScaleREST{})
|
||||
|
||||
func (r *ScaleREST) GroupVersionKind() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Scale"}
|
||||
func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
switch containingGV {
|
||||
case appsv1beta1.SchemeGroupVersion:
|
||||
return appsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
case appsv1beta2.SchemeGroupVersion:
|
||||
return appsv1beta2.SchemeGroupVersion.WithKind("Scale")
|
||||
default:
|
||||
return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Scale object
|
||||
|
@ -60,6 +60,7 @@ go_library(
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
|
@ -24,6 +24,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
@ -64,6 +65,12 @@ type EvictionREST struct {
|
||||
}
|
||||
|
||||
var _ = rest.Creater(&EvictionREST{})
|
||||
var _ = rest.GroupVersionKindProvider(&EvictionREST{})
|
||||
|
||||
// GroupVersionKind specifies a particular GroupVersionKind to discovery
|
||||
func (r *EvictionREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "Eviction"}
|
||||
}
|
||||
|
||||
// New creates a new eviction resource
|
||||
func (r *EvictionREST) New() runtime.Object {
|
||||
|
@ -36,7 +36,9 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api:go_default_library",
|
||||
"//pkg/apis/autoscaling:go_default_library",
|
||||
"//pkg/apis/autoscaling/v1:go_default_library",
|
||||
"//pkg/apis/autoscaling/validation:go_default_library",
|
||||
"//pkg/apis/extensions/v1beta1:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
"//pkg/printers/storage:go_default_library",
|
||||
|
@ -32,7 +32,9 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling"
|
||||
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/autoscaling/validation"
|
||||
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
|
||||
@ -129,8 +131,13 @@ type ScaleREST struct {
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
var _ = rest.GroupVersionKindProvider(&ScaleREST{})
|
||||
|
||||
func (r *ScaleREST) GroupVersionKind() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "Scale"}
|
||||
func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
switch containingGV {
|
||||
case extensionsv1beta1.SchemeGroupVersion:
|
||||
return extensionsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
default:
|
||||
return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Scale object
|
||||
|
@ -93,19 +93,13 @@ func (c LegacyRESTStorageProvider) NewLegacyRESTStorage(restOptionsGetter generi
|
||||
apiGroupInfo := genericapiserver.APIGroupInfo{
|
||||
GroupMeta: *legacyscheme.Registry.GroupOrDie(api.GroupName),
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
|
||||
Scheme: legacyscheme.Scheme,
|
||||
ParameterCodec: legacyscheme.ParameterCodec,
|
||||
NegotiatedSerializer: legacyscheme.Codecs,
|
||||
SubresourceGroupVersionKind: map[string]schema.GroupVersionKind{},
|
||||
}
|
||||
if autoscalingGroupVersion := (schema.GroupVersion{Group: "autoscaling", Version: "v1"}); legacyscheme.Registry.IsEnabledVersion(autoscalingGroupVersion) {
|
||||
apiGroupInfo.SubresourceGroupVersionKind["replicationcontrollers/scale"] = autoscalingGroupVersion.WithKind("Scale")
|
||||
Scheme: legacyscheme.Scheme,
|
||||
ParameterCodec: legacyscheme.ParameterCodec,
|
||||
NegotiatedSerializer: legacyscheme.Codecs,
|
||||
}
|
||||
|
||||
var podDisruptionClient policyclient.PodDisruptionBudgetsGetter
|
||||
if policyGroupVersion := (schema.GroupVersion{Group: "policy", Version: "v1beta1"}); legacyscheme.Registry.IsEnabledVersion(policyGroupVersion) {
|
||||
apiGroupInfo.SubresourceGroupVersionKind["pods/eviction"] = policyGroupVersion.WithKind("Eviction")
|
||||
|
||||
var err error
|
||||
podDisruptionClient, err = policyclient.NewForConfig(c.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
|
@ -36,7 +36,11 @@ go_library(
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/extensions/deployment/storage",
|
||||
deps = [
|
||||
"//pkg/apis/apps/v1beta1:go_default_library",
|
||||
"//pkg/apis/apps/v1beta2:go_default_library",
|
||||
"//pkg/apis/autoscaling/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/extensions/v1beta1:go_default_library",
|
||||
"//pkg/apis/extensions/validation:go_default_library",
|
||||
"//pkg/registry/extensions/deployment:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
|
@ -30,7 +30,11 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/storage"
|
||||
storeerr "k8s.io/apiserver/pkg/storage/errors"
|
||||
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
|
||||
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/registry/extensions/deployment"
|
||||
)
|
||||
@ -193,8 +197,17 @@ type ScaleREST struct {
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
var _ = rest.GroupVersionKindProvider(&ScaleREST{})
|
||||
|
||||
func (r *ScaleREST) GroupVersionKind() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Scale"}
|
||||
func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
switch containingGV {
|
||||
case extensionsv1beta1.SchemeGroupVersion:
|
||||
return extensionsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
case appsv1beta1.SchemeGroupVersion:
|
||||
return appsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
case appsv1beta2.SchemeGroupVersion:
|
||||
return appsv1beta2.SchemeGroupVersion.WithKind("Scale")
|
||||
default:
|
||||
return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Scale object
|
||||
|
@ -34,7 +34,11 @@ go_library(
|
||||
srcs = ["storage.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/registry/extensions/replicaset/storage",
|
||||
deps = [
|
||||
"//pkg/apis/apps/v1beta1:go_default_library",
|
||||
"//pkg/apis/apps/v1beta2:go_default_library",
|
||||
"//pkg/apis/autoscaling/v1:go_default_library",
|
||||
"//pkg/apis/extensions:go_default_library",
|
||||
"//pkg/apis/extensions/v1beta1:go_default_library",
|
||||
"//pkg/apis/extensions/validation:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
|
@ -29,7 +29,11 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
appsv1beta1 "k8s.io/kubernetes/pkg/apis/apps/v1beta1"
|
||||
appsv1beta2 "k8s.io/kubernetes/pkg/apis/apps/v1beta2"
|
||||
autoscalingv1 "k8s.io/kubernetes/pkg/apis/autoscaling/v1"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
|
||||
extvalidation "k8s.io/kubernetes/pkg/apis/extensions/validation"
|
||||
"k8s.io/kubernetes/pkg/printers"
|
||||
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
|
||||
@ -127,8 +131,17 @@ type ScaleREST struct {
|
||||
var _ = rest.Patcher(&ScaleREST{})
|
||||
var _ = rest.GroupVersionKindProvider(&ScaleREST{})
|
||||
|
||||
func (r *ScaleREST) GroupVersionKind() schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: "extensions", Version: "v1beta1", Kind: "Scale"}
|
||||
func (r *ScaleREST) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
switch containingGV {
|
||||
case extensionsv1beta1.SchemeGroupVersion:
|
||||
return extensionsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
case appsv1beta1.SchemeGroupVersion:
|
||||
return appsv1beta1.SchemeGroupVersion.WithKind("Scale")
|
||||
case appsv1beta2.SchemeGroupVersion:
|
||||
return appsv1beta2.SchemeGroupVersion.WithKind("Scale")
|
||||
default:
|
||||
return autoscalingv1.SchemeGroupVersion.WithKind("Scale")
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new Scale object
|
||||
|
@ -3867,7 +3867,8 @@ func TestUpdateChecksAPIVersion(t *testing.T) {
|
||||
}
|
||||
|
||||
type SimpleXGSubresourceRESTStorage struct {
|
||||
item genericapitesting.SimpleXGSubresource
|
||||
item genericapitesting.SimpleXGSubresource
|
||||
itemGVK schema.GroupVersionKind
|
||||
}
|
||||
|
||||
func (storage *SimpleXGSubresourceRESTStorage) New() runtime.Object {
|
||||
@ -3878,6 +3879,12 @@ func (storage *SimpleXGSubresourceRESTStorage) Get(ctx request.Context, id strin
|
||||
return storage.item.DeepCopyObject(), nil
|
||||
}
|
||||
|
||||
var _ = rest.GroupVersionKindProvider(&SimpleXGSubresourceRESTStorage{})
|
||||
|
||||
func (storage *SimpleXGSubresourceRESTStorage) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
|
||||
return storage.itemGVK
|
||||
}
|
||||
|
||||
func TestXGSubresource(t *testing.T) {
|
||||
container := restful.NewContainer()
|
||||
container.Router(restful.CurlyRouter{})
|
||||
@ -3888,6 +3895,7 @@ func TestXGSubresource(t *testing.T) {
|
||||
item: genericapitesting.SimpleXGSubresource{
|
||||
SubresourceInfo: "foo",
|
||||
},
|
||||
itemGVK: testGroup2Version.WithKind("SimpleXGSubresource"),
|
||||
}
|
||||
storage := map[string]rest.Storage{
|
||||
"simple": &SimpleRESTStorage{},
|
||||
@ -3913,10 +3921,6 @@ func TestXGSubresource(t *testing.T) {
|
||||
GroupVersion: testGroupVersion,
|
||||
OptionsExternalVersion: &testGroupVersion,
|
||||
Serializer: codecs,
|
||||
|
||||
SubresourceGroupVersionKind: map[string]schema.GroupVersionKind{
|
||||
"simple/subsimple": testGroup2Version.WithKind("SimpleXGSubresource"),
|
||||
},
|
||||
}
|
||||
|
||||
if err := (&group).InstallREST(container); err != nil {
|
||||
|
@ -75,12 +75,6 @@ type APIGroupVersion struct {
|
||||
|
||||
MinRequestTimeout time.Duration
|
||||
|
||||
// SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is
|
||||
// accessible from this API group version. The GroupVersionKind is that of the external version of
|
||||
// the subresource. The key of this map should be the path of the subresource. The keys here should
|
||||
// match the keys in the Storage map above for subresources.
|
||||
SubresourceGroupVersionKind map[string]schema.GroupVersionKind
|
||||
|
||||
// EnableAPIResponseCompression indicates whether API Responses should support compression
|
||||
// if the client requests it via Accept-Encoding
|
||||
EnableAPIResponseCompression bool
|
||||
|
@ -144,8 +144,9 @@ func (a *APIInstaller) newWebService() *restful.WebService {
|
||||
// object. If the storage object is a subresource and has an override supplied for it, it returns
|
||||
// the group version kind supplied in the override.
|
||||
func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schema.GroupVersionKind, error) {
|
||||
if fqKindToRegister, ok := a.group.SubresourceGroupVersionKind[path]; ok {
|
||||
return fqKindToRegister, nil
|
||||
// Let the storage tell us exactly what GVK it has
|
||||
if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok {
|
||||
return gvkProvider.GroupVersionKind(a.group.GroupVersion), nil
|
||||
}
|
||||
|
||||
object := storage.New()
|
||||
@ -162,12 +163,6 @@ func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (schem
|
||||
fqKindToRegister = a.group.GroupVersion.WithKind(fqKind.Kind)
|
||||
break
|
||||
}
|
||||
|
||||
// TODO: keep rid of extensions api group dependency here
|
||||
// This keeps it doing what it was doing before, but it doesn't feel right.
|
||||
if fqKind.Group == "extensions" && fqKind.Kind == "ThirdPartyResourceData" {
|
||||
fqKindToRegister = a.group.GroupVersion.WithKind(fqKind.Kind)
|
||||
}
|
||||
}
|
||||
if fqKindToRegister.Empty() {
|
||||
return schema.GroupVersionKind{}, fmt.Errorf("unable to locate fully qualified kind for %v: found %v when registering for %v", reflect.TypeOf(object), fqKinds, a.group.GroupVersion)
|
||||
@ -878,7 +873,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag
|
||||
apiResource.Categories = categoriesProvider.Categories()
|
||||
}
|
||||
if gvkProvider, ok := storage.(rest.GroupVersionKindProvider); ok {
|
||||
gvk := gvkProvider.GroupVersionKind()
|
||||
gvk := gvkProvider.GroupVersionKind(a.group.GroupVersion)
|
||||
apiResource.Group = gvk.Group
|
||||
apiResource.Version = gvk.Version
|
||||
apiResource.Kind = gvk.Kind
|
||||
|
@ -82,7 +82,7 @@ type CategoriesProvider interface {
|
||||
// This trumps KindProvider since it is capable of providing the information required.
|
||||
// TODO KindProvider (only used by federation) should be removed and replaced with this, but that presents greater risk late in 1.8.
|
||||
type GroupVersionKindProvider interface {
|
||||
GroupVersionKind() schema.GroupVersionKind
|
||||
GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// Lister is an object that can retrieve resources that match the provided field and label criteria.
|
||||
|
@ -70,12 +70,6 @@ type APIGroupInfo struct {
|
||||
NegotiatedSerializer runtime.NegotiatedSerializer
|
||||
// ParameterCodec performs conversions for query parameters passed to API calls
|
||||
ParameterCodec runtime.ParameterCodec
|
||||
|
||||
// SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is
|
||||
// accessible from this API group version. The GroupVersionKind is that of the external version of
|
||||
// the subresource. The key of this map should be the path of the subresource. The keys here should
|
||||
// match the keys in the Storage map above for subresources.
|
||||
SubresourceGroupVersionKind map[string]schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// GenericAPIServer contains state for a Kubernetes cluster api server.
|
||||
@ -435,9 +429,8 @@ func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupV
|
||||
UnsafeConvertor: runtime.UnsafeObjectConvertor(apiGroupInfo.Scheme),
|
||||
Defaulter: apiGroupInfo.Scheme,
|
||||
Typer: apiGroupInfo.Scheme,
|
||||
SubresourceGroupVersionKind: apiGroupInfo.SubresourceGroupVersionKind,
|
||||
Linker: apiGroupInfo.GroupMeta.SelfLinker,
|
||||
Mapper: apiGroupInfo.GroupMeta.RESTMapper,
|
||||
Linker: apiGroupInfo.GroupMeta.SelfLinker,
|
||||
Mapper: apiGroupInfo.GroupMeta.RESTMapper,
|
||||
|
||||
Admit: s.admissionControl,
|
||||
Context: s.RequestContextMapper(),
|
||||
|
@ -51,6 +51,7 @@ filegroup(
|
||||
"//test/integration/quota:all-srcs",
|
||||
"//test/integration/replicaset:all-srcs",
|
||||
"//test/integration/replicationcontroller:all-srcs",
|
||||
"//test/integration/scale:all-srcs",
|
||||
"//test/integration/scheduler:all-srcs",
|
||||
"//test/integration/scheduler_perf:all-srcs",
|
||||
"//test/integration/secrets:all-srcs",
|
||||
|
37
test/integration/scale/BUILD
Normal file
37
test/integration/scale/BUILD
Normal file
@ -0,0 +1,37 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
size = "large",
|
||||
srcs = ["scale_test.go"],
|
||||
importpath = "k8s.io/kubernetes/test/integration/scale",
|
||||
tags = ["integration"],
|
||||
deps = [
|
||||
"//cmd/kube-apiserver/app/testing:go_default_library",
|
||||
"//vendor/github.com/coreos/pkg/capnslog:go_default_library",
|
||||
"//vendor/k8s.io/api/apps/v1beta2:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
236
test/integration/scale/scale_test.go
Normal file
236
test/integration/scale/scale_test.go
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
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 scale
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/coreos/pkg/capnslog"
|
||||
|
||||
appsv1beta2 "k8s.io/api/apps/v1beta2"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
apitesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing"
|
||||
)
|
||||
|
||||
type subresourceTest struct {
|
||||
resource schema.GroupVersionResource
|
||||
kind schema.GroupVersionKind
|
||||
}
|
||||
|
||||
func makeGVR(group, version, resource string) schema.GroupVersionResource {
|
||||
return schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
|
||||
}
|
||||
func makeGVK(group, version, kind string) schema.GroupVersionKind {
|
||||
return schema.GroupVersionKind{Group: group, Version: version, Kind: kind}
|
||||
}
|
||||
|
||||
func TestScaleSubresources(t *testing.T) {
|
||||
clientSet, tearDown := setup(t)
|
||||
defer tearDown()
|
||||
|
||||
resourceLists, err := clientSet.Discovery().ServerResources()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{
|
||||
makeGVR("", "v1", "replicationcontrollers/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
|
||||
makeGVR("extensions", "v1beta1", "deployments/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
makeGVR("extensions", "v1beta1", "replicationcontrollers/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
makeGVR("extensions", "v1beta1", "replicasets/scale"): makeGVK("extensions", "v1beta1", "Scale"),
|
||||
|
||||
makeGVR("apps", "v1beta1", "deployments/scale"): makeGVK("apps", "v1beta1", "Scale"),
|
||||
makeGVR("apps", "v1beta1", "statefulsets/scale"): makeGVK("apps", "v1beta1", "Scale"),
|
||||
|
||||
makeGVR("apps", "v1beta2", "deployments/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
makeGVR("apps", "v1beta2", "replicasets/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
makeGVR("apps", "v1beta2", "statefulsets/scale"): makeGVK("apps", "v1beta2", "Scale"),
|
||||
|
||||
// makeGVR("apps", "v1", "deployments/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
// makeGVR("apps", "v1", "replicasets/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
// makeGVR("apps", "v1", "statefulsets/scale"): makeGVK("autoscaling", "v1", "Scale"),
|
||||
}
|
||||
|
||||
autoscalingGVK := schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "Scale"}
|
||||
|
||||
discoveredScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{}
|
||||
for _, resourceList := range resourceLists {
|
||||
containingGV, err := schema.ParseGroupVersion(resourceList.GroupVersion)
|
||||
if err != nil {
|
||||
t.Fatalf("error getting group version for %#v: %v", resourceList, err)
|
||||
}
|
||||
|
||||
for _, resource := range resourceList.APIResources {
|
||||
if !strings.HasSuffix(resource.Name, "/scale") {
|
||||
continue
|
||||
}
|
||||
|
||||
gvr := containingGV.WithResource(resource.Name)
|
||||
if _, exists := discoveredScaleSubresources[gvr]; exists {
|
||||
t.Errorf("scale subresource %#v listed multiple times in discovery", gvr)
|
||||
continue
|
||||
}
|
||||
|
||||
gvk := containingGV.WithKind(resource.Kind)
|
||||
if resource.Group != "" {
|
||||
gvk.Group = resource.Group
|
||||
}
|
||||
if resource.Version != "" {
|
||||
gvk.Version = resource.Version
|
||||
}
|
||||
discoveredScaleSubresources[gvr] = gvk
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure nothing is missing
|
||||
for gvr, gvk := range expectedScaleSubresources {
|
||||
if _, ok := discoveredScaleSubresources[gvr]; !ok {
|
||||
t.Errorf("expected scale subresource %#v of kind %#v was missing from discovery", gvr, gvk)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure discovery lists expected types
|
||||
for gvr, gvk := range discoveredScaleSubresources {
|
||||
if expectedGVK, expected := expectedScaleSubresources[gvr]; !expected {
|
||||
if gvk == autoscalingGVK {
|
||||
t.Errorf("unexpected scale subresource %#v of kind %#v. new scale subresource should be added to expectedScaleSubresources", gvr, gvk)
|
||||
} else {
|
||||
t.Errorf("unexpected scale subresource %#v of kind %#v. new scale resources are expected to use Scale from the autoscaling/v1 API group", gvr, gvk)
|
||||
}
|
||||
continue
|
||||
} else if expectedGVK != gvk {
|
||||
t.Errorf("scale subresource %#v should be of kind %#v, but %#v was listed in discovery", gvr, expectedGVK, gvk)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Create objects required to exercise scale subresources
|
||||
if _, err := clientSet.CoreV1().ReplicationControllers("default").Create(rcStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().ReplicaSets("default").Create(rsStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().Deployments("default").Create(deploymentStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := clientSet.AppsV1beta2().StatefulSets("default").Create(ssStub); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Ensure scale subresources return and accept expected kinds
|
||||
for gvr, gvk := range discoveredScaleSubresources {
|
||||
prefix := "/apis"
|
||||
if gvr.Group == corev1.GroupName {
|
||||
prefix = "/api"
|
||||
}
|
||||
|
||||
resourceParts := strings.SplitN(gvr.Resource, "/", 2)
|
||||
|
||||
urlPath := path.Join(prefix, gvr.Group, gvr.Version, "namespaces", "default", resourceParts[0], "test", resourceParts[1])
|
||||
obj := &unstructured.Unstructured{}
|
||||
|
||||
getData, err := clientSet.CoreV1().RESTClient().Get().AbsPath(urlPath).DoRaw()
|
||||
if err != nil {
|
||||
t.Errorf("error fetching %s: %v", urlPath, err)
|
||||
continue
|
||||
}
|
||||
if err := json.Unmarshal(getData, obj); err != nil {
|
||||
t.Errorf("error decoding %s: %v", urlPath, err)
|
||||
t.Log(string(getData))
|
||||
continue
|
||||
}
|
||||
|
||||
if obj.GetObjectKind().GroupVersionKind() != gvk {
|
||||
t.Errorf("expected %#v, got %#v from %s", gvk, obj.GetObjectKind().GroupVersionKind(), urlPath)
|
||||
t.Log(string(getData))
|
||||
continue
|
||||
}
|
||||
|
||||
updateData, err := clientSet.CoreV1().RESTClient().Put().AbsPath(urlPath).Body(getData).DoRaw()
|
||||
if err != nil {
|
||||
t.Errorf("error putting to %s: %v", urlPath, err)
|
||||
t.Log(string(getData))
|
||||
t.Log(string(updateData))
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
replicas = int32(1)
|
||||
|
||||
podStub = corev1.PodTemplateSpec{
|
||||
ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}},
|
||||
Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "busybox"}}},
|
||||
}
|
||||
|
||||
rcStub = &corev1.ReplicationController{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: corev1.ReplicationControllerSpec{Selector: podStub.Labels, Replicas: &replicas, Template: &podStub},
|
||||
}
|
||||
|
||||
rsStub = &appsv1beta2.ReplicaSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
|
||||
deploymentStub = &appsv1beta2.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
|
||||
ssStub = &appsv1beta2.StatefulSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test"},
|
||||
Spec: appsv1beta2.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
|
||||
}
|
||||
)
|
||||
|
||||
func setup(t *testing.T) (client kubernetes.Interface, tearDown func()) {
|
||||
masterConfig, tearDownMaster := apitesting.StartTestServerOrDie(t)
|
||||
|
||||
// TODO: Disable logging here until we resolve teardown issues which result in
|
||||
// massive log spam. Another path forward would be to refactor
|
||||
// StartTestServerOrDie to work with the etcd instance already started by the
|
||||
// integration test scripts.
|
||||
// See https://github.com/kubernetes/kubernetes/issues/49489.
|
||||
repo, err := capnslog.GetRepoLogger("github.com/coreos/etcd")
|
||||
if err != nil {
|
||||
t.Fatalf("couldn't configure logging: %v", err)
|
||||
}
|
||||
repo.SetLogLevel(map[string]capnslog.LogLevel{
|
||||
"etcdserver/api/v3rpc": capnslog.CRITICAL,
|
||||
})
|
||||
|
||||
masterConfig.AcceptContentTypes = ""
|
||||
masterConfig.ContentType = ""
|
||||
masterConfig.NegotiatedSerializer = nil
|
||||
clientSet, err := kubernetes.NewForConfig(masterConfig)
|
||||
if err != nil {
|
||||
t.Fatalf("error creating clientset: %v", err)
|
||||
}
|
||||
|
||||
return clientSet, tearDownMaster
|
||||
}
|
Loading…
Reference in New Issue
Block a user