mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-10-31 13:50:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			252 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			252 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
| 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/etcd/etcdserver/api/v3rpc" // Force package logger init.
 | |
| 	"github.com/coreos/pkg/capnslog"
 | |
| 
 | |
| 	appsv1 "k8s.io/api/apps/v1"
 | |
| 	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"
 | |
| 	"k8s.io/kubernetes/test/integration/framework"
 | |
| )
 | |
| 
 | |
| 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 TestMain(m *testing.M) {
 | |
| 	framework.EtcdMain(m.Run)
 | |
| }
 | |
| 
 | |
| func TestScaleSubresources(t *testing.T) {
 | |
| 	clientSet, tearDown := setupWithOptions(t, nil, []string{
 | |
| 		"--runtime-config",
 | |
| 		// TODO(liggitt): remove these once apps/v1beta1, apps/v1beta2, and extensions/v1beta1 can no longer be served
 | |
| 		"api/all=true,extensions/v1beta1/deployments=true,extensions/v1beta1/replicationcontrollers=true,extensions/v1beta1/replicasets=true",
 | |
| 	})
 | |
| 	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"),
 | |
| 
 | |
| 		// TODO(liggitt): remove these once apps/v1beta1, apps/v1beta2, and extensions/v1beta1 can no longer be served
 | |
| 		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.AppsV1().ReplicaSets("default").Create(rsStub); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if _, err := clientSet.AppsV1().Deployments("default").Create(deploymentStub); err != nil {
 | |
| 		t.Fatal(err)
 | |
| 	}
 | |
| 	if _, err := clientSet.AppsV1().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 = &appsv1.ReplicaSet{
 | |
| 		ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | |
| 		Spec:       appsv1.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
 | |
| 	}
 | |
| 
 | |
| 	deploymentStub = &appsv1.Deployment{
 | |
| 		ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | |
| 		Spec:       appsv1.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
 | |
| 	}
 | |
| 
 | |
| 	ssStub = &appsv1.StatefulSet{
 | |
| 		ObjectMeta: metav1.ObjectMeta{Name: "test"},
 | |
| 		Spec:       appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub},
 | |
| 	}
 | |
| )
 | |
| 
 | |
| func setup(t *testing.T) (client kubernetes.Interface, tearDown func()) {
 | |
| 	return setupWithOptions(t, nil, nil)
 | |
| }
 | |
| 
 | |
| func setupWithOptions(t *testing.T, instanceOptions *apitesting.TestServerInstanceOptions, flags []string) (client kubernetes.Interface, tearDown func()) {
 | |
| 	result := apitesting.StartTestServerOrDie(t, instanceOptions, flags, framework.SharedEtcd())
 | |
| 
 | |
| 	// 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,
 | |
| 	})
 | |
| 
 | |
| 	result.ClientConfig.AcceptContentTypes = ""
 | |
| 	result.ClientConfig.ContentType = ""
 | |
| 	result.ClientConfig.NegotiatedSerializer = nil
 | |
| 	clientSet, err := kubernetes.NewForConfig(result.ClientConfig)
 | |
| 	if err != nil {
 | |
| 		t.Fatalf("error creating clientset: %v", err)
 | |
| 	}
 | |
| 
 | |
| 	return clientSet, result.TearDownFn
 | |
| }
 |