k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/scale/scale_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package scale 18 19 import ( 20 "context" 21 "encoding/json" 22 "io" 23 "path" 24 "strings" 25 "testing" 26 27 "google.golang.org/grpc/grpclog" 28 29 appsv1 "k8s.io/api/apps/v1" 30 corev1 "k8s.io/api/core/v1" 31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 33 "k8s.io/apimachinery/pkg/runtime/schema" 34 "k8s.io/client-go/kubernetes" 35 apitesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" 36 "k8s.io/kubernetes/test/integration/framework" 37 ) 38 39 func makeGVR(group, version, resource string) schema.GroupVersionResource { 40 return schema.GroupVersionResource{Group: group, Version: version, Resource: resource} 41 } 42 func makeGVK(group, version, kind string) schema.GroupVersionKind { 43 return schema.GroupVersionKind{Group: group, Version: version, Kind: kind} 44 } 45 46 func TestMain(m *testing.M) { 47 framework.EtcdMain(m.Run) 48 } 49 50 func TestScaleSubresources(t *testing.T) { 51 clientSet, tearDown := setupWithOptions(t, nil, []string{ 52 "--runtime-config", 53 "api/all=true", 54 }) 55 defer tearDown() 56 57 _, resourceLists, err := clientSet.Discovery().ServerGroupsAndResources() 58 if err != nil { 59 t.Fatal(err) 60 } 61 62 expectedScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{ 63 makeGVR("", "v1", "replicationcontrollers/scale"): makeGVK("autoscaling", "v1", "Scale"), 64 65 makeGVR("apps", "v1", "deployments/scale"): makeGVK("autoscaling", "v1", "Scale"), 66 makeGVR("apps", "v1", "replicasets/scale"): makeGVK("autoscaling", "v1", "Scale"), 67 makeGVR("apps", "v1", "statefulsets/scale"): makeGVK("autoscaling", "v1", "Scale"), 68 } 69 70 autoscalingGVK := schema.GroupVersionKind{Group: "autoscaling", Version: "v1", Kind: "Scale"} 71 72 discoveredScaleSubresources := map[schema.GroupVersionResource]schema.GroupVersionKind{} 73 for _, resourceList := range resourceLists { 74 containingGV, err := schema.ParseGroupVersion(resourceList.GroupVersion) 75 if err != nil { 76 t.Fatalf("error getting group version for %#v: %v", resourceList, err) 77 } 78 79 for _, resource := range resourceList.APIResources { 80 if !strings.HasSuffix(resource.Name, "/scale") { 81 continue 82 } 83 84 gvr := containingGV.WithResource(resource.Name) 85 if _, exists := discoveredScaleSubresources[gvr]; exists { 86 t.Errorf("scale subresource %#v listed multiple times in discovery", gvr) 87 continue 88 } 89 90 gvk := containingGV.WithKind(resource.Kind) 91 if resource.Group != "" { 92 gvk.Group = resource.Group 93 } 94 if resource.Version != "" { 95 gvk.Version = resource.Version 96 } 97 discoveredScaleSubresources[gvr] = gvk 98 } 99 } 100 101 // Ensure nothing is missing 102 for gvr, gvk := range expectedScaleSubresources { 103 if _, ok := discoveredScaleSubresources[gvr]; !ok { 104 t.Errorf("expected scale subresource %#v of kind %#v was missing from discovery", gvr, gvk) 105 } 106 } 107 108 // Ensure discovery lists expected types 109 for gvr, gvk := range discoveredScaleSubresources { 110 if expectedGVK, expected := expectedScaleSubresources[gvr]; !expected { 111 if gvk == autoscalingGVK { 112 t.Errorf("unexpected scale subresource %#v of kind %#v. new scale subresource should be added to expectedScaleSubresources", gvr, gvk) 113 } else { 114 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) 115 } 116 continue 117 } else if expectedGVK != gvk { 118 t.Errorf("scale subresource %#v should be of kind %#v, but %#v was listed in discovery", gvr, expectedGVK, gvk) 119 continue 120 } 121 } 122 123 // Create objects required to exercise scale subresources 124 if _, err := clientSet.CoreV1().ReplicationControllers("default").Create(context.TODO(), rcStub, metav1.CreateOptions{}); err != nil { 125 t.Fatal(err) 126 } 127 if _, err := clientSet.AppsV1().ReplicaSets("default").Create(context.TODO(), rsStub, metav1.CreateOptions{}); err != nil { 128 t.Fatal(err) 129 } 130 if _, err := clientSet.AppsV1().Deployments("default").Create(context.TODO(), deploymentStub, metav1.CreateOptions{}); err != nil { 131 t.Fatal(err) 132 } 133 if _, err := clientSet.AppsV1().StatefulSets("default").Create(context.TODO(), ssStub, metav1.CreateOptions{}); err != nil { 134 t.Fatal(err) 135 } 136 137 // Ensure scale subresources return and accept expected kinds 138 for gvr, gvk := range discoveredScaleSubresources { 139 prefix := "/apis" 140 if gvr.Group == corev1.GroupName { 141 prefix = "/api" 142 } 143 144 resourceParts := strings.SplitN(gvr.Resource, "/", 2) 145 146 urlPath := path.Join(prefix, gvr.Group, gvr.Version, "namespaces", "default", resourceParts[0], "test", resourceParts[1]) 147 obj := &unstructured.Unstructured{} 148 149 getData, err := clientSet.CoreV1().RESTClient().Get().AbsPath(urlPath).DoRaw(context.TODO()) 150 if err != nil { 151 t.Errorf("error fetching %s: %v", urlPath, err) 152 continue 153 } 154 if err := json.Unmarshal(getData, obj); err != nil { 155 t.Errorf("error decoding %s: %v", urlPath, err) 156 t.Log(string(getData)) 157 continue 158 } 159 160 if obj.GetObjectKind().GroupVersionKind() != gvk { 161 t.Errorf("expected %#v, got %#v from %s", gvk, obj.GetObjectKind().GroupVersionKind(), urlPath) 162 t.Log(string(getData)) 163 continue 164 } 165 166 updateData, err := clientSet.CoreV1().RESTClient().Put().AbsPath(urlPath).Body(getData).DoRaw(context.TODO()) 167 if err != nil { 168 t.Errorf("error putting to %s: %v", urlPath, err) 169 t.Log(string(getData)) 170 t.Log(string(updateData)) 171 continue 172 } 173 } 174 } 175 176 var ( 177 replicas = int32(1) 178 179 podStub = corev1.PodTemplateSpec{ 180 ObjectMeta: metav1.ObjectMeta{Labels: map[string]string{"foo": "bar"}}, 181 Spec: corev1.PodSpec{Containers: []corev1.Container{{Name: "test", Image: "busybox"}}}, 182 } 183 184 rcStub = &corev1.ReplicationController{ 185 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 186 Spec: corev1.ReplicationControllerSpec{Selector: podStub.Labels, Replicas: &replicas, Template: &podStub}, 187 } 188 189 rsStub = &appsv1.ReplicaSet{ 190 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 191 Spec: appsv1.ReplicaSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub}, 192 } 193 194 deploymentStub = &appsv1.Deployment{ 195 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 196 Spec: appsv1.DeploymentSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub}, 197 } 198 199 ssStub = &appsv1.StatefulSet{ 200 ObjectMeta: metav1.ObjectMeta{Name: "test"}, 201 Spec: appsv1.StatefulSetSpec{Selector: &metav1.LabelSelector{MatchLabels: podStub.Labels}, Replicas: &replicas, Template: podStub}, 202 } 203 ) 204 205 func setupWithOptions(t *testing.T, instanceOptions *apitesting.TestServerInstanceOptions, flags []string) (client kubernetes.Interface, tearDown func()) { 206 grpclog.SetLoggerV2(grpclog.NewLoggerV2(io.Discard, io.Discard, io.Discard)) 207 208 result := apitesting.StartTestServerOrDie(t, instanceOptions, flags, framework.SharedEtcd()) 209 result.ClientConfig.AcceptContentTypes = "" 210 result.ClientConfig.ContentType = "" 211 result.ClientConfig.NegotiatedSerializer = nil 212 clientSet, err := kubernetes.NewForConfig(result.ClientConfig) 213 if err != nil { 214 t.Fatalf("error creating clientset: %v", err) 215 } 216 217 return clientSet, result.TearDownFn 218 }