1
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
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
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
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
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 }
219
View as plain text