customcolumn_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  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 get
  14. import (
  15. "bytes"
  16. "reflect"
  17. "strings"
  18. "testing"
  19. corev1 "k8s.io/api/core/v1"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/runtime"
  22. "k8s.io/kubernetes/pkg/kubectl/scheme"
  23. "k8s.io/kubernetes/pkg/kubectl/util/printers"
  24. )
  25. // UniversalDecoder call must specify parameter versions; otherwise it will decode to internal versions.
  26. var decoder = scheme.Codecs.UniversalDecoder(scheme.Scheme.PrioritizedVersionsAllGroups()...)
  27. func TestMassageJSONPath(t *testing.T) {
  28. tests := []struct {
  29. input string
  30. expectedOutput string
  31. expectErr bool
  32. }{
  33. {input: "foo.bar", expectedOutput: "{.foo.bar}"},
  34. {input: "{foo.bar}", expectedOutput: "{.foo.bar}"},
  35. {input: ".foo.bar", expectedOutput: "{.foo.bar}"},
  36. {input: "{.foo.bar}", expectedOutput: "{.foo.bar}"},
  37. {input: "", expectedOutput: ""},
  38. {input: "{foo.bar", expectErr: true},
  39. {input: "foo.bar}", expectErr: true},
  40. {input: "{foo.bar}}", expectErr: true},
  41. {input: "{{foo.bar}", expectErr: true},
  42. }
  43. for _, test := range tests {
  44. t.Run(test.input, func(t *testing.T) {
  45. output, err := RelaxedJSONPathExpression(test.input)
  46. if err != nil && !test.expectErr {
  47. t.Errorf("unexpected error: %v", err)
  48. return
  49. }
  50. if test.expectErr {
  51. if err == nil {
  52. t.Error("unexpected non-error")
  53. }
  54. return
  55. }
  56. if output != test.expectedOutput {
  57. t.Errorf("input: %s, expected: %s, saw: %s", test.input, test.expectedOutput, output)
  58. }
  59. })
  60. }
  61. }
  62. func TestNewColumnPrinterFromSpec(t *testing.T) {
  63. tests := []struct {
  64. spec string
  65. expectedColumns []Column
  66. expectErr bool
  67. name string
  68. noHeaders bool
  69. }{
  70. {
  71. spec: "",
  72. expectErr: true,
  73. name: "empty",
  74. },
  75. {
  76. spec: "invalid",
  77. expectErr: true,
  78. name: "invalid1",
  79. },
  80. {
  81. spec: "invalid=foobar",
  82. expectErr: true,
  83. name: "invalid2",
  84. },
  85. {
  86. spec: "invalid,foobar:blah",
  87. expectErr: true,
  88. name: "invalid3",
  89. },
  90. {
  91. spec: "NAME:metadata.name,API_VERSION:apiVersion",
  92. name: "ok",
  93. expectedColumns: []Column{
  94. {
  95. Header: "NAME",
  96. FieldSpec: "{.metadata.name}",
  97. },
  98. {
  99. Header: "API_VERSION",
  100. FieldSpec: "{.apiVersion}",
  101. },
  102. },
  103. },
  104. {
  105. spec: "API_VERSION:apiVersion",
  106. name: "no-headers",
  107. noHeaders: true,
  108. },
  109. }
  110. for _, test := range tests {
  111. t.Run(test.name, func(t *testing.T) {
  112. printer, err := NewCustomColumnsPrinterFromSpec(test.spec, decoder, test.noHeaders)
  113. if test.expectErr {
  114. if err == nil {
  115. t.Errorf("[%s] unexpected non-error", test.name)
  116. }
  117. return
  118. }
  119. if !test.expectErr && err != nil {
  120. t.Errorf("[%s] unexpected error: %v", test.name, err)
  121. return
  122. }
  123. if test.noHeaders {
  124. buffer := &bytes.Buffer{}
  125. printer.PrintObj(&corev1.Pod{}, buffer)
  126. if err != nil {
  127. t.Fatalf("An error occurred printing Pod: %#v", err)
  128. }
  129. if contains(strings.Fields(buffer.String()), "API_VERSION") {
  130. t.Errorf("unexpected header API_VERSION")
  131. }
  132. } else if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
  133. t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
  134. }
  135. })
  136. }
  137. }
  138. func contains(arr []string, s string) bool {
  139. for i := range arr {
  140. if arr[i] == s {
  141. return true
  142. }
  143. }
  144. return false
  145. }
  146. const exampleTemplateOne = `NAME API_VERSION
  147. {metadata.name} {apiVersion}`
  148. const exampleTemplateTwo = `NAME API_VERSION
  149. {metadata.name} {apiVersion}`
  150. func TestNewColumnPrinterFromTemplate(t *testing.T) {
  151. tests := []struct {
  152. spec string
  153. expectedColumns []Column
  154. expectErr bool
  155. name string
  156. }{
  157. {
  158. spec: "",
  159. expectErr: true,
  160. name: "empty",
  161. },
  162. {
  163. spec: "invalid",
  164. expectErr: true,
  165. name: "invalid1",
  166. },
  167. {
  168. spec: "invalid=foobar",
  169. expectErr: true,
  170. name: "invalid2",
  171. },
  172. {
  173. spec: "invalid,foobar:blah",
  174. expectErr: true,
  175. name: "invalid3",
  176. },
  177. {
  178. spec: exampleTemplateOne,
  179. name: "ok",
  180. expectedColumns: []Column{
  181. {
  182. Header: "NAME",
  183. FieldSpec: "{.metadata.name}",
  184. },
  185. {
  186. Header: "API_VERSION",
  187. FieldSpec: "{.apiVersion}",
  188. },
  189. },
  190. },
  191. {
  192. spec: exampleTemplateTwo,
  193. name: "ok-2",
  194. expectedColumns: []Column{
  195. {
  196. Header: "NAME",
  197. FieldSpec: "{.metadata.name}",
  198. },
  199. {
  200. Header: "API_VERSION",
  201. FieldSpec: "{.apiVersion}",
  202. },
  203. },
  204. },
  205. }
  206. for _, test := range tests {
  207. t.Run(test.name, func(t *testing.T) {
  208. reader := bytes.NewBufferString(test.spec)
  209. printer, err := NewCustomColumnsPrinterFromTemplate(reader, decoder)
  210. if test.expectErr {
  211. if err == nil {
  212. t.Errorf("[%s] unexpected non-error", test.name)
  213. }
  214. return
  215. }
  216. if !test.expectErr && err != nil {
  217. t.Errorf("[%s] unexpected error: %v", test.name, err)
  218. return
  219. }
  220. if !reflect.DeepEqual(test.expectedColumns, printer.Columns) {
  221. t.Errorf("[%s]\nexpected:\n%v\nsaw:\n%v\n", test.name, test.expectedColumns, printer.Columns)
  222. }
  223. })
  224. }
  225. }
  226. func TestColumnPrint(t *testing.T) {
  227. tests := []struct {
  228. columns []Column
  229. obj runtime.Object
  230. expectedOutput string
  231. }{
  232. {
  233. columns: []Column{
  234. {
  235. Header: "NAME",
  236. FieldSpec: "{.metadata.name}",
  237. },
  238. },
  239. obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
  240. expectedOutput: `NAME
  241. foo
  242. `,
  243. },
  244. {
  245. columns: []Column{
  246. {
  247. Header: "NAME",
  248. FieldSpec: "{.metadata.name}",
  249. },
  250. },
  251. obj: &corev1.PodList{
  252. Items: []corev1.Pod{
  253. {ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
  254. {ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
  255. },
  256. },
  257. expectedOutput: `NAME
  258. foo
  259. bar
  260. `,
  261. },
  262. {
  263. columns: []Column{
  264. {
  265. Header: "NAME",
  266. FieldSpec: "{.metadata.name}",
  267. },
  268. {
  269. Header: "API_VERSION",
  270. FieldSpec: "{.apiVersion}",
  271. },
  272. },
  273. obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
  274. expectedOutput: `NAME API_VERSION
  275. foo baz
  276. `,
  277. },
  278. {
  279. columns: []Column{
  280. {
  281. Header: "NAME",
  282. FieldSpec: "{.metadata.name}",
  283. },
  284. {
  285. Header: "API_VERSION",
  286. FieldSpec: "{.apiVersion}",
  287. },
  288. {
  289. Header: "NOT_FOUND",
  290. FieldSpec: "{.notFound}",
  291. },
  292. },
  293. obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}},
  294. expectedOutput: `NAME API_VERSION NOT_FOUND
  295. foo baz <none>
  296. `,
  297. },
  298. }
  299. for _, test := range tests {
  300. t.Run(test.expectedOutput, func(t *testing.T) {
  301. printer := &CustomColumnsPrinter{
  302. Columns: test.columns,
  303. Decoder: decoder,
  304. }
  305. buffer := &bytes.Buffer{}
  306. if err := printer.PrintObj(test.obj, buffer); err != nil {
  307. t.Errorf("unexpected error: %v", err)
  308. }
  309. if buffer.String() != test.expectedOutput {
  310. t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", test.expectedOutput, buffer.String())
  311. }
  312. })
  313. }
  314. }
  315. // this mimics how resource/get.go calls the customcolumn printer
  316. func TestIndividualPrintObjOnExistingTabWriter(t *testing.T) {
  317. columns := []Column{
  318. {
  319. Header: "NAME",
  320. FieldSpec: "{.metadata.name}",
  321. },
  322. {
  323. Header: "LONG COLUMN NAME", // name is longer than all values of label1
  324. FieldSpec: "{.metadata.labels.label1}",
  325. },
  326. {
  327. Header: "LABEL 2",
  328. FieldSpec: "{.metadata.labels.label2}",
  329. },
  330. }
  331. objects := []*corev1.Pod{
  332. {ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"label1": "foo", "label2": "foo"}}},
  333. {ObjectMeta: metav1.ObjectMeta{Name: "bar", Labels: map[string]string{"label1": "bar", "label2": "bar"}}},
  334. }
  335. expectedOutput := `NAME LONG COLUMN NAME LABEL 2
  336. foo foo foo
  337. bar bar bar
  338. `
  339. buffer := &bytes.Buffer{}
  340. tabWriter := printers.GetNewTabWriter(buffer)
  341. printer := &CustomColumnsPrinter{
  342. Columns: columns,
  343. Decoder: decoder,
  344. }
  345. for _, obj := range objects {
  346. if err := printer.PrintObj(obj, tabWriter); err != nil {
  347. t.Errorf("unexpected error: %v", err)
  348. }
  349. }
  350. tabWriter.Flush()
  351. if buffer.String() != expectedOutput {
  352. t.Errorf("\nexpected:\n'%s'\nsaw\n'%s'\n", expectedOutput, buffer.String())
  353. }
  354. }
  355. func TestSliceColumnPrint(t *testing.T) {
  356. pod := &corev1.Pod{
  357. ObjectMeta: metav1.ObjectMeta{
  358. Name: "fake-name",
  359. Namespace: "fake-namespace",
  360. },
  361. Spec: corev1.PodSpec{
  362. Containers: []corev1.Container{
  363. {
  364. Name: "fake0",
  365. },
  366. {
  367. Name: "fake1",
  368. },
  369. {
  370. Name: "fake2",
  371. },
  372. {
  373. Name: "fake3",
  374. },
  375. },
  376. },
  377. }
  378. tests := []struct {
  379. name string
  380. spec string
  381. expectedOutput string
  382. expectErr bool
  383. }{
  384. {
  385. name: "containers[0]",
  386. spec: "CONTAINER:.spec.containers[0].name",
  387. expectedOutput: `CONTAINER
  388. fake0
  389. `,
  390. expectErr: false,
  391. },
  392. {
  393. name: "containers[3]",
  394. spec: "CONTAINER:.spec.containers[3].name",
  395. expectedOutput: `CONTAINER
  396. fake3
  397. `,
  398. expectErr: false,
  399. },
  400. {
  401. name: "containers[5], illegal expression because it is out of bounds",
  402. spec: "CONTAINER:.spec.containers[5].name",
  403. expectedOutput: "",
  404. expectErr: true,
  405. },
  406. {
  407. name: "containers[-1], it equals containers[3]",
  408. spec: "CONTAINER:.spec.containers[-1].name",
  409. expectedOutput: `CONTAINER
  410. fake3
  411. `,
  412. expectErr: false,
  413. },
  414. {
  415. name: "containers[-2], it equals containers[2]",
  416. spec: "CONTAINER:.spec.containers[-2].name",
  417. expectedOutput: `CONTAINER
  418. fake2
  419. `,
  420. expectErr: false,
  421. },
  422. {
  423. name: "containers[-4], it equals containers[0]",
  424. spec: "CONTAINER:.spec.containers[-4].name",
  425. expectedOutput: `CONTAINER
  426. fake0
  427. `,
  428. expectErr: false,
  429. },
  430. {
  431. name: "containers[-5], illegal expression because it is out of bounds",
  432. spec: "CONTAINER:.spec.containers[-5].name",
  433. expectedOutput: "",
  434. expectErr: true,
  435. },
  436. {
  437. name: "containers[0:0], it equals empty set",
  438. spec: "CONTAINER:.spec.containers[0:0].name",
  439. expectedOutput: `CONTAINER
  440. <none>
  441. `,
  442. expectErr: false,
  443. },
  444. {
  445. name: "containers[0:3]",
  446. spec: "CONTAINER:.spec.containers[0:3].name",
  447. expectedOutput: `CONTAINER
  448. fake0,fake1,fake2
  449. `,
  450. expectErr: false,
  451. },
  452. {
  453. name: "containers[1:]",
  454. spec: "CONTAINER:.spec.containers[1:].name",
  455. expectedOutput: `CONTAINER
  456. fake1,fake2,fake3
  457. `,
  458. expectErr: false,
  459. },
  460. {
  461. name: "containers[3:1], illegal expression because start index is greater than end index",
  462. spec: "CONTAINER:.spec.containers[3:1].name",
  463. expectedOutput: "",
  464. expectErr: true,
  465. },
  466. {
  467. name: "containers[0:-1], it equals containers[0:3]",
  468. spec: "CONTAINER:.spec.containers[0:-1].name",
  469. expectedOutput: `CONTAINER
  470. fake0,fake1,fake2
  471. `,
  472. expectErr: false,
  473. },
  474. {
  475. name: "containers[-1:], it equals containers[3:]",
  476. spec: "CONTAINER:.spec.containers[-1:].name",
  477. expectedOutput: `CONTAINER
  478. fake3
  479. `,
  480. expectErr: false,
  481. },
  482. {
  483. name: "containers[-4:], it equals containers[0:]",
  484. spec: "CONTAINER:.spec.containers[-4:].name",
  485. expectedOutput: `CONTAINER
  486. fake0,fake1,fake2,fake3
  487. `,
  488. expectErr: false,
  489. },
  490. {
  491. name: "containers[-3:-1], it equasl containers[1:3]",
  492. spec: "CONTAINER:.spec.containers[-3:-1].name",
  493. expectedOutput: `CONTAINER
  494. fake1,fake2
  495. `,
  496. expectErr: false,
  497. },
  498. {
  499. name: "containers[-2:-3], it equals containers[2:1], illegal expression because start index is greater than end index",
  500. spec: "CONTAINER:.spec.containers[-2:-3].name",
  501. expectedOutput: "",
  502. expectErr: true,
  503. },
  504. {
  505. name: "containers[4:4], it equals empty set",
  506. spec: "CONTAINER:.spec.containers[4:4].name",
  507. expectedOutput: `CONTAINER
  508. <none>
  509. `,
  510. expectErr: false,
  511. },
  512. {
  513. name: "containers[-5:-5], it equals empty set",
  514. spec: "CONTAINER:.spec.containers[-5:-5].name",
  515. expectedOutput: `CONTAINER
  516. <none>
  517. `,
  518. expectErr: false,
  519. },
  520. }
  521. for _, test := range tests {
  522. t.Run(test.name, func(t *testing.T) {
  523. printer, err := NewCustomColumnsPrinterFromSpec(test.spec, decoder, false)
  524. if err != nil {
  525. t.Errorf("test %s has unexpected error: %v", test.name, err)
  526. }
  527. buffer := &bytes.Buffer{}
  528. err = printer.PrintObj(pod, buffer)
  529. if test.expectErr {
  530. if err == nil {
  531. t.Errorf("test %s has unexpected error: %v", test.name, err)
  532. }
  533. } else {
  534. if err != nil {
  535. t.Errorf("test %s has unexpected error: %v", test.name, err)
  536. } else if buffer.String() != test.expectedOutput {
  537. t.Errorf("test %s has unexpected output:\nexpected: %s\nsaw: %s", test.name, test.expectedOutput, buffer.String())
  538. }
  539. }
  540. })
  541. }
  542. }