scale_test.go 9.1 KB

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