From 1a8bed4b7e86beff1ae8a8c8cf50d38300700f44 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Fri, 27 Oct 2017 22:01:32 -0400 Subject: [PATCH] Test scale subresource discovery --- test/integration/BUILD | 1 + test/integration/scale/BUILD | 37 +++++ test/integration/scale/scale_test.go | 236 +++++++++++++++++++++++++++ 3 files changed, 274 insertions(+) create mode 100644 test/integration/scale/BUILD create mode 100644 test/integration/scale/scale_test.go diff --git a/test/integration/BUILD b/test/integration/BUILD index 706d30a8b87..045c0f38c23 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -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", diff --git a/test/integration/scale/BUILD b/test/integration/scale/BUILD new file mode 100644 index 00000000000..6f55ba0de37 --- /dev/null +++ b/test/integration/scale/BUILD @@ -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"], +) diff --git a/test/integration/scale/scale_test.go b/test/integration/scale/scale_test.go new file mode 100644 index 00000000000..c08c1575fe3 --- /dev/null +++ b/test/integration/scale/scale_test.go @@ -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 +}