scale_test.go 8.0 KB

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