label_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430
  1. /*
  2. Copyright 2014 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 cmd
  14. import (
  15. "bytes"
  16. "net/http"
  17. "reflect"
  18. "strings"
  19. "testing"
  20. "k8s.io/kubernetes/pkg/api"
  21. "k8s.io/kubernetes/pkg/api/testapi"
  22. "k8s.io/kubernetes/pkg/client/restclient"
  23. "k8s.io/kubernetes/pkg/client/unversioned/fake"
  24. "k8s.io/kubernetes/pkg/runtime"
  25. )
  26. func TestValidateLabels(t *testing.T) {
  27. tests := []struct {
  28. meta *api.ObjectMeta
  29. labels map[string]string
  30. expectErr bool
  31. test string
  32. }{
  33. {
  34. meta: &api.ObjectMeta{
  35. Labels: map[string]string{
  36. "a": "b",
  37. "c": "d",
  38. },
  39. },
  40. labels: map[string]string{
  41. "a": "c",
  42. "d": "b",
  43. },
  44. test: "one shared",
  45. expectErr: true,
  46. },
  47. {
  48. meta: &api.ObjectMeta{
  49. Labels: map[string]string{
  50. "a": "b",
  51. "c": "d",
  52. },
  53. },
  54. labels: map[string]string{
  55. "b": "d",
  56. "c": "a",
  57. },
  58. test: "second shared",
  59. expectErr: true,
  60. },
  61. {
  62. meta: &api.ObjectMeta{
  63. Labels: map[string]string{
  64. "a": "b",
  65. "c": "d",
  66. },
  67. },
  68. labels: map[string]string{
  69. "b": "a",
  70. "d": "c",
  71. },
  72. test: "no overlap",
  73. },
  74. {
  75. meta: &api.ObjectMeta{},
  76. labels: map[string]string{
  77. "b": "a",
  78. "d": "c",
  79. },
  80. test: "no labels",
  81. },
  82. }
  83. for _, test := range tests {
  84. err := validateNoOverwrites(test.meta, test.labels)
  85. if test.expectErr && err == nil {
  86. t.Errorf("%s: unexpected non-error", test.test)
  87. }
  88. if !test.expectErr && err != nil {
  89. t.Errorf("%s: unexpected error: %v", test.test, err)
  90. }
  91. }
  92. }
  93. func TestParseLabels(t *testing.T) {
  94. tests := []struct {
  95. labels []string
  96. expected map[string]string
  97. expectedRemove []string
  98. expectErr bool
  99. }{
  100. {
  101. labels: []string{"a=b", "c=d"},
  102. expected: map[string]string{"a": "b", "c": "d"},
  103. },
  104. {
  105. labels: []string{},
  106. expected: map[string]string{},
  107. },
  108. {
  109. labels: []string{"a=b", "c=d", "e-"},
  110. expected: map[string]string{"a": "b", "c": "d"},
  111. expectedRemove: []string{"e"},
  112. },
  113. {
  114. labels: []string{"ab", "c=d"},
  115. expectErr: true,
  116. },
  117. {
  118. labels: []string{"a=b", "c=d", "a-"},
  119. expectErr: true,
  120. },
  121. {
  122. labels: []string{"a="},
  123. expectErr: true,
  124. },
  125. {
  126. labels: []string{"a=%^$"},
  127. expectErr: true,
  128. },
  129. }
  130. for _, test := range tests {
  131. labels, remove, err := parseLabels(test.labels)
  132. if test.expectErr && err == nil {
  133. t.Errorf("unexpected non-error: %v", test)
  134. }
  135. if !test.expectErr && err != nil {
  136. t.Errorf("unexpected error: %v %v", err, test)
  137. }
  138. if !reflect.DeepEqual(labels, test.expected) {
  139. t.Errorf("expected: %v, got %v", test.expected, labels)
  140. }
  141. if !reflect.DeepEqual(remove, test.expectedRemove) {
  142. t.Errorf("expected: %v, got %v", test.expectedRemove, remove)
  143. }
  144. }
  145. }
  146. func TestLabelFunc(t *testing.T) {
  147. tests := []struct {
  148. obj runtime.Object
  149. overwrite bool
  150. version string
  151. labels map[string]string
  152. remove []string
  153. expected runtime.Object
  154. expectErr bool
  155. }{
  156. {
  157. obj: &api.Pod{
  158. ObjectMeta: api.ObjectMeta{
  159. Labels: map[string]string{"a": "b"},
  160. },
  161. },
  162. labels: map[string]string{"a": "b"},
  163. expectErr: true,
  164. },
  165. {
  166. obj: &api.Pod{
  167. ObjectMeta: api.ObjectMeta{
  168. Labels: map[string]string{"a": "b"},
  169. },
  170. },
  171. labels: map[string]string{"a": "c"},
  172. overwrite: true,
  173. expected: &api.Pod{
  174. ObjectMeta: api.ObjectMeta{
  175. Labels: map[string]string{"a": "c"},
  176. },
  177. },
  178. },
  179. {
  180. obj: &api.Pod{
  181. ObjectMeta: api.ObjectMeta{
  182. Labels: map[string]string{"a": "b"},
  183. },
  184. },
  185. labels: map[string]string{"c": "d"},
  186. expected: &api.Pod{
  187. ObjectMeta: api.ObjectMeta{
  188. Labels: map[string]string{"a": "b", "c": "d"},
  189. },
  190. },
  191. },
  192. {
  193. obj: &api.Pod{
  194. ObjectMeta: api.ObjectMeta{
  195. Labels: map[string]string{"a": "b"},
  196. },
  197. },
  198. labels: map[string]string{"c": "d"},
  199. version: "2",
  200. expected: &api.Pod{
  201. ObjectMeta: api.ObjectMeta{
  202. Labels: map[string]string{"a": "b", "c": "d"},
  203. ResourceVersion: "2",
  204. },
  205. },
  206. },
  207. {
  208. obj: &api.Pod{
  209. ObjectMeta: api.ObjectMeta{
  210. Labels: map[string]string{"a": "b"},
  211. },
  212. },
  213. labels: map[string]string{},
  214. remove: []string{"a"},
  215. expected: &api.Pod{
  216. ObjectMeta: api.ObjectMeta{
  217. Labels: map[string]string{},
  218. },
  219. },
  220. },
  221. {
  222. obj: &api.Pod{
  223. ObjectMeta: api.ObjectMeta{
  224. Labels: map[string]string{"a": "b", "c": "d"},
  225. },
  226. },
  227. labels: map[string]string{"e": "f"},
  228. remove: []string{"a"},
  229. expected: &api.Pod{
  230. ObjectMeta: api.ObjectMeta{
  231. Labels: map[string]string{
  232. "c": "d",
  233. "e": "f",
  234. },
  235. },
  236. },
  237. },
  238. {
  239. obj: &api.Pod{
  240. ObjectMeta: api.ObjectMeta{},
  241. },
  242. labels: map[string]string{"a": "b"},
  243. expected: &api.Pod{
  244. ObjectMeta: api.ObjectMeta{
  245. Labels: map[string]string{"a": "b"},
  246. },
  247. },
  248. },
  249. }
  250. for _, test := range tests {
  251. err := labelFunc(test.obj, test.overwrite, test.version, test.labels, test.remove)
  252. if test.expectErr {
  253. if err == nil {
  254. t.Errorf("unexpected non-error: %v", test)
  255. }
  256. continue
  257. }
  258. if !test.expectErr && err != nil {
  259. t.Errorf("unexpected error: %v %v", err, test)
  260. }
  261. if !reflect.DeepEqual(test.obj, test.expected) {
  262. t.Errorf("expected: %v, got %v", test.expected, test.obj)
  263. }
  264. }
  265. }
  266. func TestLabelErrors(t *testing.T) {
  267. testCases := map[string]struct {
  268. args []string
  269. flags map[string]string
  270. errFn func(error) bool
  271. }{
  272. "no args": {
  273. args: []string{},
  274. errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
  275. },
  276. "not enough labels": {
  277. args: []string{"pods"},
  278. errFn: func(err error) bool { return strings.Contains(err.Error(), "at least one label update is required") },
  279. },
  280. "no resources": {
  281. args: []string{"pods-"},
  282. errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
  283. },
  284. "no resources 2": {
  285. args: []string{"pods=bar"},
  286. errFn: func(err error) bool { return strings.Contains(err.Error(), "one or more resources must be specified") },
  287. },
  288. "resources but no selectors": {
  289. args: []string{"pods", "app=bar"},
  290. errFn: func(err error) bool {
  291. return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
  292. },
  293. },
  294. "multiple resources but no selectors": {
  295. args: []string{"pods,deployments", "app=bar"},
  296. errFn: func(err error) bool {
  297. return strings.Contains(err.Error(), "resource(s) were provided, but no name, label selector, or --all flag specified")
  298. },
  299. },
  300. }
  301. for k, testCase := range testCases {
  302. f, tf, _, _ := NewAPIFactory()
  303. tf.Printer = &testPrinter{}
  304. tf.Namespace = "test"
  305. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  306. buf := bytes.NewBuffer([]byte{})
  307. cmd := NewCmdLabel(f, buf)
  308. cmd.SetOutput(buf)
  309. for k, v := range testCase.flags {
  310. cmd.Flags().Set(k, v)
  311. }
  312. err := RunLabel(f, buf, cmd, testCase.args, &LabelOptions{})
  313. if !testCase.errFn(err) {
  314. t.Errorf("%s: unexpected error: %v", k, err)
  315. continue
  316. }
  317. if tf.Printer.(*testPrinter).Objects != nil {
  318. t.Errorf("unexpected print to default printer")
  319. }
  320. if buf.Len() > 0 {
  321. t.Errorf("buffer should be empty: %s", string(buf.Bytes()))
  322. }
  323. }
  324. }
  325. func TestLabelForResourceFromFile(t *testing.T) {
  326. pods, _, _ := testData()
  327. f, tf, codec, ns := NewAPIFactory()
  328. tf.Client = &fake.RESTClient{
  329. NegotiatedSerializer: ns,
  330. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  331. switch req.Method {
  332. case "GET":
  333. switch req.URL.Path {
  334. case "/namespaces/test/replicationcontrollers/cassandra":
  335. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
  336. default:
  337. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  338. return nil, nil
  339. }
  340. case "PATCH":
  341. switch req.URL.Path {
  342. case "/namespaces/test/replicationcontrollers/cassandra":
  343. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
  344. default:
  345. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  346. return nil, nil
  347. }
  348. default:
  349. t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
  350. return nil, nil
  351. }
  352. }),
  353. }
  354. tf.Namespace = "test"
  355. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  356. buf := bytes.NewBuffer([]byte{})
  357. cmd := NewCmdLabel(f, buf)
  358. options := &LabelOptions{
  359. Filenames: []string{"../../../examples/storage/cassandra/cassandra-controller.yaml"},
  360. }
  361. err := RunLabel(f, buf, cmd, []string{"a=b"}, options)
  362. if err != nil {
  363. t.Fatalf("unexpected error: %v", err)
  364. }
  365. if !strings.Contains(buf.String(), "labeled") {
  366. t.Errorf("did not set labels: %s", buf.String())
  367. }
  368. }
  369. func TestLabelMultipleObjects(t *testing.T) {
  370. pods, _, _ := testData()
  371. f, tf, codec, ns := NewAPIFactory()
  372. tf.Client = &fake.RESTClient{
  373. NegotiatedSerializer: ns,
  374. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  375. switch req.Method {
  376. case "GET":
  377. switch req.URL.Path {
  378. case "/namespaces/test/pods":
  379. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
  380. default:
  381. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  382. return nil, nil
  383. }
  384. case "PATCH":
  385. switch req.URL.Path {
  386. case "/namespaces/test/pods/foo":
  387. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])}, nil
  388. case "/namespaces/test/pods/bar":
  389. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[1])}, nil
  390. default:
  391. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  392. return nil, nil
  393. }
  394. default:
  395. t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
  396. return nil, nil
  397. }
  398. }),
  399. }
  400. tf.Namespace = "test"
  401. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  402. buf := bytes.NewBuffer([]byte{})
  403. cmd := NewCmdLabel(f, buf)
  404. cmd.Flags().Set("all", "true")
  405. if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}, &LabelOptions{}); err != nil {
  406. t.Fatalf("unexpected error: %v", err)
  407. }
  408. if strings.Count(buf.String(), "labeled") != len(pods.Items) {
  409. t.Errorf("not all labels are set: %s", buf.String())
  410. }
  411. }