patch_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. Copyright 2015 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 patch
  14. import (
  15. "net/http"
  16. "strings"
  17. "testing"
  18. "k8s.io/cli-runtime/pkg/genericclioptions"
  19. "k8s.io/cli-runtime/pkg/resource"
  20. "k8s.io/client-go/rest/fake"
  21. cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
  22. "k8s.io/kubernetes/pkg/kubectl/scheme"
  23. )
  24. func TestPatchObject(t *testing.T) {
  25. _, svc, _ := cmdtesting.TestData()
  26. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  27. defer tf.Cleanup()
  28. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  29. tf.UnstructuredClient = &fake.RESTClient{
  30. NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
  31. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  32. switch p, m := req.URL.Path, req.Method; {
  33. case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"):
  34. obj := svc.Items[0]
  35. // ensure patched object reflects successful
  36. // patch edits from the client
  37. if m == "PATCH" {
  38. obj.Spec.Type = "NodePort"
  39. }
  40. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &obj)}, nil
  41. default:
  42. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  43. return nil, nil
  44. }
  45. }),
  46. }
  47. stream, _, buf, _ := genericclioptions.NewTestIOStreams()
  48. cmd := NewCmdPatch(tf, stream)
  49. cmd.Flags().Set("namespace", "test")
  50. cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
  51. cmd.Flags().Set("output", "name")
  52. cmd.Run(cmd, []string{"services/frontend"})
  53. // uses the name from the response
  54. if buf.String() != "service/baz\n" {
  55. t.Errorf("unexpected output: %s", buf.String())
  56. }
  57. }
  58. func TestPatchObjectFromFile(t *testing.T) {
  59. _, svc, _ := cmdtesting.TestData()
  60. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  61. defer tf.Cleanup()
  62. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  63. tf.UnstructuredClient = &fake.RESTClient{
  64. NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
  65. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  66. switch p, m := req.URL.Path, req.Method; {
  67. case p == "/namespaces/test/services/frontend" && (m == "PATCH" || m == "GET"):
  68. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
  69. default:
  70. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  71. return nil, nil
  72. }
  73. }),
  74. }
  75. stream, _, buf, _ := genericclioptions.NewTestIOStreams()
  76. cmd := NewCmdPatch(tf, stream)
  77. cmd.Flags().Set("namespace", "test")
  78. cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
  79. cmd.Flags().Set("output", "name")
  80. cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml")
  81. cmd.Run(cmd, []string{})
  82. // uses the name from the response
  83. if buf.String() != "service/baz\n" {
  84. t.Errorf("unexpected output: %s", buf.String())
  85. }
  86. }
  87. func TestPatchNoop(t *testing.T) {
  88. _, svc, _ := cmdtesting.TestData()
  89. getObject := &svc.Items[0]
  90. patchObject := &svc.Items[0]
  91. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  92. defer tf.Cleanup()
  93. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  94. tf.UnstructuredClient = &fake.RESTClient{
  95. NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
  96. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  97. switch p, m := req.URL.Path, req.Method; {
  98. case p == "/namespaces/test/services/frontend" && m == "PATCH":
  99. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, patchObject)}, nil
  100. case p == "/namespaces/test/services/frontend" && m == "GET":
  101. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, getObject)}, nil
  102. default:
  103. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  104. return nil, nil
  105. }
  106. }),
  107. }
  108. // Patched
  109. {
  110. patchObject = patchObject.DeepCopy()
  111. if patchObject.Annotations == nil {
  112. patchObject.Annotations = map[string]string{}
  113. }
  114. patchObject.Annotations["foo"] = "bar"
  115. stream, _, buf, _ := genericclioptions.NewTestIOStreams()
  116. cmd := NewCmdPatch(tf, stream)
  117. cmd.Flags().Set("namespace", "test")
  118. cmd.Flags().Set("patch", `{"metadata":{"annotations":{"foo":"bar"}}}`)
  119. cmd.Run(cmd, []string{"services", "frontend"})
  120. if buf.String() != "service/baz patched\n" {
  121. t.Errorf("unexpected output: %s", buf.String())
  122. }
  123. }
  124. }
  125. func TestPatchObjectFromFileOutput(t *testing.T) {
  126. _, svc, _ := cmdtesting.TestData()
  127. svcCopy := svc.Items[0].DeepCopy()
  128. if svcCopy.Labels == nil {
  129. svcCopy.Labels = map[string]string{}
  130. }
  131. svcCopy.Labels["post-patch"] = "post-patch-value"
  132. tf := cmdtesting.NewTestFactory().WithNamespace("test")
  133. defer tf.Cleanup()
  134. codec := scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  135. tf.UnstructuredClient = &fake.RESTClient{
  136. NegotiatedSerializer: resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer,
  137. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  138. switch p, m := req.URL.Path, req.Method; {
  139. case p == "/namespaces/test/services/frontend" && m == "GET":
  140. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, &svc.Items[0])}, nil
  141. case p == "/namespaces/test/services/frontend" && m == "PATCH":
  142. return &http.Response{StatusCode: 200, Header: cmdtesting.DefaultHeader(), Body: cmdtesting.ObjBody(codec, svcCopy)}, nil
  143. default:
  144. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  145. return nil, nil
  146. }
  147. }),
  148. }
  149. stream, _, buf, _ := genericclioptions.NewTestIOStreams()
  150. cmd := NewCmdPatch(tf, stream)
  151. cmd.Flags().Set("namespace", "test")
  152. cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
  153. cmd.Flags().Set("output", "yaml")
  154. cmd.Flags().Set("filename", "../../../../test/e2e/testing-manifests/guestbook/frontend-service.yaml")
  155. cmd.Run(cmd, []string{})
  156. t.Log(buf.String())
  157. // make sure the value returned by the server is used
  158. if !strings.Contains(buf.String(), "post-patch: post-patch-value") {
  159. t.Errorf("unexpected output: %s", buf.String())
  160. }
  161. }