set_resources_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519
  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 set
  14. import (
  15. "fmt"
  16. "io/ioutil"
  17. "net/http"
  18. "strings"
  19. "testing"
  20. "github.com/stretchr/testify/assert"
  21. appsv1 "k8s.io/api/apps/v1"
  22. appsv1beta1 "k8s.io/api/apps/v1beta1"
  23. appsv1beta2 "k8s.io/api/apps/v1beta2"
  24. batchv1 "k8s.io/api/batch/v1"
  25. corev1 "k8s.io/api/core/v1"
  26. extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
  27. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  28. "k8s.io/apimachinery/pkg/runtime"
  29. "k8s.io/apimachinery/pkg/runtime/schema"
  30. "k8s.io/cli-runtime/pkg/genericclioptions"
  31. "k8s.io/cli-runtime/pkg/resource"
  32. restclient "k8s.io/client-go/rest"
  33. "k8s.io/client-go/rest/fake"
  34. cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
  35. "k8s.io/kubernetes/pkg/kubectl/scheme"
  36. )
  37. func TestResourcesLocal(t *testing.T) {
  38. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  39. defer tf.Cleanup()
  40. tf.Client = &fake.RESTClient{
  41. GroupVersion: schema.GroupVersion{Version: ""},
  42. NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
  43. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  44. t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
  45. return nil, nil
  46. }),
  47. }
  48. tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
  49. outputFormat := "name"
  50. streams, _, buf, _ := genericclioptions.NewTestIOStreams()
  51. cmd := NewCmdResources(tf, streams)
  52. cmd.SetOutput(buf)
  53. cmd.Flags().Set("output", outputFormat)
  54. cmd.Flags().Set("local", "true")
  55. opts := SetResourcesOptions{
  56. PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
  57. FilenameOptions: resource.FilenameOptions{
  58. Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}},
  59. Local: true,
  60. Limits: "cpu=200m,memory=512Mi",
  61. Requests: "cpu=200m,memory=512Mi",
  62. ContainerSelector: "*",
  63. IOStreams: streams,
  64. }
  65. err := opts.Complete(tf, cmd, []string{})
  66. if err == nil {
  67. err = opts.Validate()
  68. }
  69. if err == nil {
  70. err = opts.Run()
  71. }
  72. if err != nil {
  73. t.Fatalf("unexpected error: %v", err)
  74. }
  75. if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
  76. t.Errorf("did not set resources: %s", buf.String())
  77. }
  78. }
  79. func TestSetMultiResourcesLimitsLocal(t *testing.T) {
  80. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  81. defer tf.Cleanup()
  82. tf.Client = &fake.RESTClient{
  83. GroupVersion: schema.GroupVersion{Version: ""},
  84. NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
  85. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  86. t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
  87. return nil, nil
  88. }),
  89. }
  90. tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
  91. outputFormat := "name"
  92. streams, _, buf, _ := genericclioptions.NewTestIOStreams()
  93. cmd := NewCmdResources(tf, streams)
  94. cmd.SetOutput(buf)
  95. cmd.Flags().Set("output", outputFormat)
  96. cmd.Flags().Set("local", "true")
  97. opts := SetResourcesOptions{
  98. PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
  99. FilenameOptions: resource.FilenameOptions{
  100. Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
  101. Local: true,
  102. Limits: "cpu=200m,memory=512Mi",
  103. Requests: "cpu=200m,memory=512Mi",
  104. ContainerSelector: "*",
  105. IOStreams: streams,
  106. }
  107. err := opts.Complete(tf, cmd, []string{})
  108. if err == nil {
  109. err = opts.Validate()
  110. }
  111. if err == nil {
  112. err = opts.Run()
  113. }
  114. if err != nil {
  115. t.Fatalf("unexpected error: %v", err)
  116. }
  117. expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
  118. if buf.String() != expectedOut {
  119. t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
  120. }
  121. }
  122. func TestSetResourcesRemote(t *testing.T) {
  123. inputs := []struct {
  124. name string
  125. object runtime.Object
  126. groupVersion schema.GroupVersion
  127. path string
  128. args []string
  129. }{
  130. {
  131. name: "set image extensionsv1beta1 ReplicaSet",
  132. object: &extensionsv1beta1.ReplicaSet{
  133. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  134. Spec: extensionsv1beta1.ReplicaSetSpec{
  135. Template: corev1.PodTemplateSpec{
  136. Spec: corev1.PodSpec{
  137. Containers: []corev1.Container{
  138. {
  139. Name: "nginx",
  140. Image: "nginx",
  141. },
  142. },
  143. },
  144. },
  145. },
  146. },
  147. groupVersion: extensionsv1beta1.SchemeGroupVersion,
  148. path: "/namespaces/test/replicasets/nginx",
  149. args: []string{"replicaset", "nginx"},
  150. },
  151. {
  152. name: "set image appsv1beta2 ReplicaSet",
  153. object: &appsv1beta2.ReplicaSet{
  154. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  155. Spec: appsv1beta2.ReplicaSetSpec{
  156. Template: corev1.PodTemplateSpec{
  157. Spec: corev1.PodSpec{
  158. Containers: []corev1.Container{
  159. {
  160. Name: "nginx",
  161. Image: "nginx",
  162. },
  163. },
  164. },
  165. },
  166. },
  167. },
  168. groupVersion: appsv1beta2.SchemeGroupVersion,
  169. path: "/namespaces/test/replicasets/nginx",
  170. args: []string{"replicaset", "nginx"},
  171. },
  172. {
  173. name: "set image appsv1 ReplicaSet",
  174. object: &appsv1.ReplicaSet{
  175. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  176. Spec: appsv1.ReplicaSetSpec{
  177. Template: corev1.PodTemplateSpec{
  178. Spec: corev1.PodSpec{
  179. Containers: []corev1.Container{
  180. {
  181. Name: "nginx",
  182. Image: "nginx",
  183. },
  184. },
  185. },
  186. },
  187. },
  188. },
  189. groupVersion: appsv1.SchemeGroupVersion,
  190. path: "/namespaces/test/replicasets/nginx",
  191. args: []string{"replicaset", "nginx"},
  192. },
  193. {
  194. name: "set image extensionsv1beta1 DaemonSet",
  195. object: &extensionsv1beta1.DaemonSet{
  196. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  197. Spec: extensionsv1beta1.DaemonSetSpec{
  198. Template: corev1.PodTemplateSpec{
  199. Spec: corev1.PodSpec{
  200. Containers: []corev1.Container{
  201. {
  202. Name: "nginx",
  203. Image: "nginx",
  204. },
  205. },
  206. },
  207. },
  208. },
  209. },
  210. groupVersion: extensionsv1beta1.SchemeGroupVersion,
  211. path: "/namespaces/test/daemonsets/nginx",
  212. args: []string{"daemonset", "nginx"},
  213. },
  214. {
  215. name: "set image appsv1beta2 DaemonSet",
  216. object: &appsv1beta2.DaemonSet{
  217. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  218. Spec: appsv1beta2.DaemonSetSpec{
  219. Template: corev1.PodTemplateSpec{
  220. Spec: corev1.PodSpec{
  221. Containers: []corev1.Container{
  222. {
  223. Name: "nginx",
  224. Image: "nginx",
  225. },
  226. },
  227. },
  228. },
  229. },
  230. },
  231. groupVersion: appsv1beta2.SchemeGroupVersion,
  232. path: "/namespaces/test/daemonsets/nginx",
  233. args: []string{"daemonset", "nginx"},
  234. },
  235. {
  236. name: "set image appsv1 DaemonSet",
  237. object: &appsv1.DaemonSet{
  238. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  239. Spec: appsv1.DaemonSetSpec{
  240. Template: corev1.PodTemplateSpec{
  241. Spec: corev1.PodSpec{
  242. Containers: []corev1.Container{
  243. {
  244. Name: "nginx",
  245. Image: "nginx",
  246. },
  247. },
  248. },
  249. },
  250. },
  251. },
  252. groupVersion: appsv1.SchemeGroupVersion,
  253. path: "/namespaces/test/daemonsets/nginx",
  254. args: []string{"daemonset", "nginx"},
  255. },
  256. {
  257. name: "set image extensionsv1beta1 Deployment",
  258. object: &extensionsv1beta1.Deployment{
  259. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  260. Spec: extensionsv1beta1.DeploymentSpec{
  261. Template: corev1.PodTemplateSpec{
  262. Spec: corev1.PodSpec{
  263. Containers: []corev1.Container{
  264. {
  265. Name: "nginx",
  266. Image: "nginx",
  267. },
  268. },
  269. },
  270. },
  271. },
  272. },
  273. groupVersion: extensionsv1beta1.SchemeGroupVersion,
  274. path: "/namespaces/test/deployments/nginx",
  275. args: []string{"deployment", "nginx"},
  276. },
  277. {
  278. name: "set image appsv1beta1 Deployment",
  279. object: &appsv1beta1.Deployment{
  280. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  281. Spec: appsv1beta1.DeploymentSpec{
  282. Template: corev1.PodTemplateSpec{
  283. Spec: corev1.PodSpec{
  284. Containers: []corev1.Container{
  285. {
  286. Name: "nginx",
  287. Image: "nginx",
  288. },
  289. },
  290. },
  291. },
  292. },
  293. },
  294. groupVersion: appsv1beta1.SchemeGroupVersion,
  295. path: "/namespaces/test/deployments/nginx",
  296. args: []string{"deployment", "nginx"},
  297. },
  298. {
  299. name: "set image appsv1beta2 Deployment",
  300. object: &appsv1beta2.Deployment{
  301. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  302. Spec: appsv1beta2.DeploymentSpec{
  303. Template: corev1.PodTemplateSpec{
  304. Spec: corev1.PodSpec{
  305. Containers: []corev1.Container{
  306. {
  307. Name: "nginx",
  308. Image: "nginx",
  309. },
  310. },
  311. },
  312. },
  313. },
  314. },
  315. groupVersion: appsv1beta2.SchemeGroupVersion,
  316. path: "/namespaces/test/deployments/nginx",
  317. args: []string{"deployment", "nginx"},
  318. },
  319. {
  320. name: "set image appsv1 Deployment",
  321. object: &appsv1.Deployment{
  322. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  323. Spec: appsv1.DeploymentSpec{
  324. Template: corev1.PodTemplateSpec{
  325. Spec: corev1.PodSpec{
  326. Containers: []corev1.Container{
  327. {
  328. Name: "nginx",
  329. Image: "nginx",
  330. },
  331. },
  332. },
  333. },
  334. },
  335. },
  336. groupVersion: appsv1.SchemeGroupVersion,
  337. path: "/namespaces/test/deployments/nginx",
  338. args: []string{"deployment", "nginx"},
  339. },
  340. {
  341. name: "set image appsv1beta1 StatefulSet",
  342. object: &appsv1beta1.StatefulSet{
  343. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  344. Spec: appsv1beta1.StatefulSetSpec{
  345. Template: corev1.PodTemplateSpec{
  346. Spec: corev1.PodSpec{
  347. Containers: []corev1.Container{
  348. {
  349. Name: "nginx",
  350. Image: "nginx",
  351. },
  352. },
  353. },
  354. },
  355. },
  356. },
  357. groupVersion: appsv1beta1.SchemeGroupVersion,
  358. path: "/namespaces/test/statefulsets/nginx",
  359. args: []string{"statefulset", "nginx"},
  360. },
  361. {
  362. name: "set image appsv1beta2 StatefulSet",
  363. object: &appsv1beta2.StatefulSet{
  364. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  365. Spec: appsv1beta2.StatefulSetSpec{
  366. Template: corev1.PodTemplateSpec{
  367. Spec: corev1.PodSpec{
  368. Containers: []corev1.Container{
  369. {
  370. Name: "nginx",
  371. Image: "nginx",
  372. },
  373. },
  374. },
  375. },
  376. },
  377. },
  378. groupVersion: appsv1beta2.SchemeGroupVersion,
  379. path: "/namespaces/test/statefulsets/nginx",
  380. args: []string{"statefulset", "nginx"},
  381. },
  382. {
  383. name: "set image appsv1 StatefulSet",
  384. object: &appsv1.StatefulSet{
  385. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  386. Spec: appsv1.StatefulSetSpec{
  387. Template: corev1.PodTemplateSpec{
  388. Spec: corev1.PodSpec{
  389. Containers: []corev1.Container{
  390. {
  391. Name: "nginx",
  392. Image: "nginx",
  393. },
  394. },
  395. },
  396. },
  397. },
  398. },
  399. groupVersion: appsv1.SchemeGroupVersion,
  400. path: "/namespaces/test/statefulsets/nginx",
  401. args: []string{"statefulset", "nginx"},
  402. },
  403. {
  404. name: "set image batchv1 Job",
  405. object: &batchv1.Job{
  406. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  407. Spec: batchv1.JobSpec{
  408. Template: corev1.PodTemplateSpec{
  409. Spec: corev1.PodSpec{
  410. Containers: []corev1.Container{
  411. {
  412. Name: "nginx",
  413. Image: "nginx",
  414. },
  415. },
  416. },
  417. },
  418. },
  419. },
  420. groupVersion: batchv1.SchemeGroupVersion,
  421. path: "/namespaces/test/jobs/nginx",
  422. args: []string{"job", "nginx"},
  423. },
  424. {
  425. name: "set image corev1.ReplicationController",
  426. object: &corev1.ReplicationController{
  427. ObjectMeta: metav1.ObjectMeta{Name: "nginx"},
  428. Spec: corev1.ReplicationControllerSpec{
  429. Template: &corev1.PodTemplateSpec{
  430. Spec: corev1.PodSpec{
  431. Containers: []corev1.Container{
  432. {
  433. Name: "nginx",
  434. Image: "nginx",
  435. },
  436. },
  437. },
  438. },
  439. },
  440. },
  441. groupVersion: corev1.SchemeGroupVersion,
  442. path: "/namespaces/test/replicationcontrollers/nginx",
  443. args: []string{"replicationcontroller", "nginx"},
  444. },
  445. }
  446. for i, input := range inputs {
  447. t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
  448. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  449. defer tf.Cleanup()
  450. tf.Client = &fake.RESTClient{
  451. GroupVersion: input.groupVersion,
  452. NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
  453. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  454. switch p, m := req.URL.Path, req.Method; {
  455. case p == input.path && m == http.MethodGet:
  456. return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
  457. case p == input.path && m == http.MethodPatch:
  458. stream, err := req.GetBody()
  459. if err != nil {
  460. return nil, err
  461. }
  462. bytes, err := ioutil.ReadAll(stream)
  463. if err != nil {
  464. return nil, err
  465. }
  466. assert.Contains(t, string(bytes), "200m", fmt.Sprintf("resources not updated for %#v", input.object))
  467. return &http.Response{StatusCode: http.StatusOK, Header: cmdtesting.DefaultHeader(), Body: objBody(input.object)}, nil
  468. default:
  469. t.Errorf("%s: unexpected request: %s %#v\n%#v", "resources", req.Method, req.URL, req)
  470. return nil, fmt.Errorf("unexpected request")
  471. }
  472. }),
  473. }
  474. outputFormat := "yaml"
  475. streams := genericclioptions.NewTestIOStreamsDiscard()
  476. cmd := NewCmdResources(tf, streams)
  477. cmd.Flags().Set("output", outputFormat)
  478. opts := SetResourcesOptions{
  479. PrintFlags: genericclioptions.NewPrintFlags("").WithDefaultOutput(outputFormat).WithTypeSetter(scheme.Scheme),
  480. Limits: "cpu=200m,memory=512Mi",
  481. ContainerSelector: "*",
  482. IOStreams: streams,
  483. }
  484. err := opts.Complete(tf, cmd, input.args)
  485. if err == nil {
  486. err = opts.Validate()
  487. }
  488. if err == nil {
  489. err = opts.Run()
  490. }
  491. assert.NoError(t, err)
  492. })
  493. }
  494. }