get_test.go 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  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. encjson "encoding/json"
  17. "fmt"
  18. "io"
  19. "io/ioutil"
  20. "net/http"
  21. "reflect"
  22. "strings"
  23. "testing"
  24. "k8s.io/kubernetes/pkg/api"
  25. "k8s.io/kubernetes/pkg/api/meta"
  26. "k8s.io/kubernetes/pkg/api/testapi"
  27. apitesting "k8s.io/kubernetes/pkg/api/testing"
  28. "k8s.io/kubernetes/pkg/api/unversioned"
  29. "k8s.io/kubernetes/pkg/client/restclient"
  30. "k8s.io/kubernetes/pkg/client/typed/discovery"
  31. "k8s.io/kubernetes/pkg/client/unversioned/fake"
  32. "k8s.io/kubernetes/pkg/runtime"
  33. "k8s.io/kubernetes/pkg/runtime/serializer"
  34. "k8s.io/kubernetes/pkg/runtime/serializer/json"
  35. "k8s.io/kubernetes/pkg/runtime/serializer/streaming"
  36. "k8s.io/kubernetes/pkg/util/diff"
  37. "k8s.io/kubernetes/pkg/watch"
  38. "k8s.io/kubernetes/pkg/watch/versioned"
  39. )
  40. func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
  41. pods := &api.PodList{
  42. ListMeta: unversioned.ListMeta{
  43. ResourceVersion: "15",
  44. },
  45. Items: []api.Pod{
  46. {
  47. ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
  48. Spec: apitesting.DeepEqualSafePodSpec(),
  49. },
  50. {
  51. ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
  52. Spec: apitesting.DeepEqualSafePodSpec(),
  53. },
  54. },
  55. }
  56. svc := &api.ServiceList{
  57. ListMeta: unversioned.ListMeta{
  58. ResourceVersion: "16",
  59. },
  60. Items: []api.Service{
  61. {
  62. ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
  63. Spec: api.ServiceSpec{
  64. SessionAffinity: "None",
  65. Type: api.ServiceTypeClusterIP,
  66. },
  67. },
  68. },
  69. }
  70. rc := &api.ReplicationControllerList{
  71. ListMeta: unversioned.ListMeta{
  72. ResourceVersion: "17",
  73. },
  74. Items: []api.ReplicationController{
  75. {
  76. ObjectMeta: api.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"},
  77. Spec: api.ReplicationControllerSpec{
  78. Replicas: 1,
  79. },
  80. },
  81. },
  82. }
  83. return pods, svc, rc
  84. }
  85. func testDynamicResources() []*discovery.APIGroupResources {
  86. return []*discovery.APIGroupResources{
  87. {
  88. Group: unversioned.APIGroup{
  89. Versions: []unversioned.GroupVersionForDiscovery{
  90. {Version: "v1"},
  91. },
  92. PreferredVersion: unversioned.GroupVersionForDiscovery{Version: "v1"},
  93. },
  94. VersionedResources: map[string][]unversioned.APIResource{
  95. "v1": {
  96. {Name: "pods", Namespaced: true, Kind: "Pod"},
  97. {Name: "services", Namespaced: true, Kind: "Service"},
  98. {Name: "replicationcontrollers", Namespaced: true, Kind: "ReplicationController"},
  99. },
  100. },
  101. },
  102. }
  103. }
  104. func testComponentStatusData() *api.ComponentStatusList {
  105. good := api.ComponentStatus{
  106. Conditions: []api.ComponentCondition{
  107. {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok"},
  108. },
  109. ObjectMeta: api.ObjectMeta{Name: "servergood"},
  110. }
  111. bad := api.ComponentStatus{
  112. Conditions: []api.ComponentCondition{
  113. {Type: api.ComponentHealthy, Status: api.ConditionFalse, Message: "", Error: "bad status: 500"},
  114. },
  115. ObjectMeta: api.ObjectMeta{Name: "serverbad"},
  116. }
  117. unknown := api.ComponentStatus{
  118. Conditions: []api.ComponentCondition{
  119. {Type: api.ComponentHealthy, Status: api.ConditionUnknown, Message: "", Error: "fizzbuzz error"},
  120. },
  121. ObjectMeta: api.ObjectMeta{Name: "serverunknown"},
  122. }
  123. return &api.ComponentStatusList{
  124. Items: []api.ComponentStatus{good, bad, unknown},
  125. }
  126. }
  127. // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
  128. func TestGetUnknownSchemaObject(t *testing.T) {
  129. f, tf, codec, ns := NewTestFactory()
  130. tf.Printer = &testPrinter{}
  131. tf.Client = &fake.RESTClient{
  132. NegotiatedSerializer: ns,
  133. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &internalType{Name: "foo"})},
  134. }
  135. tf.Namespace = "test"
  136. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  137. buf := bytes.NewBuffer([]byte{})
  138. errBuf := bytes.NewBuffer([]byte{})
  139. cmd := NewCmdGet(f, buf, errBuf)
  140. cmd.SetOutput(buf)
  141. cmd.Run(cmd, []string{"type", "foo"})
  142. expected := &internalType{Name: "foo"}
  143. actual := tf.Printer.(*testPrinter).Objects[0]
  144. if !reflect.DeepEqual(expected, actual) {
  145. t.Errorf("unexpected object: %#v", actual)
  146. }
  147. if buf.String() != fmt.Sprintf("%#v", expected) {
  148. t.Errorf("unexpected output: %s", buf.String())
  149. }
  150. }
  151. // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
  152. // Because api.List is part of the Kube API, resource.Builder has to perform a conversion on
  153. // api.Scheme, which may not have access to all objects, and not all objects are at the same
  154. // internal versioning scheme. This test verifies that two isolated schemes (Test, and api.Scheme)
  155. // can be conjoined into a single output object.
  156. //
  157. // The expected behavior of the `kubectl get` command is:
  158. // 1. objects using unrecognized schemes will always be returned using that scheme/version, "unlikelyversion" in this test;
  159. // 2. if the specified output-version is a recognized, valid Scheme, then the list should use that scheme, and otherwise it will default to the client version, testapi.Default.GroupVersion().String() in this test;
  160. // 3a. if the specified output-version is a recognized, valid Scheme, in which the requested object (replicationcontroller) can be represented, then the object should be returned using that version;
  161. // 3b. otherwise if the specified output-version is unrecognized, but the requested object (replicationcontroller) is recognized by the client's codec, then it will be converted to the client version, testapi.Default.GroupVersion().String() in this test.
  162. func TestGetUnknownSchemaObjectListGeneric(t *testing.T) {
  163. testCases := map[string]struct {
  164. outputVersion string
  165. listVersion string
  166. testtypeVersion string
  167. rcVersion string
  168. }{
  169. "handles specific version": {
  170. outputVersion: testapi.Default.GroupVersion().String(),
  171. listVersion: testapi.Default.GroupVersion().String(),
  172. testtypeVersion: unlikelyGV.String(),
  173. rcVersion: testapi.Default.GroupVersion().String(),
  174. },
  175. "handles second specific version": {
  176. outputVersion: "unlikely.group/unlikelyversion",
  177. listVersion: testapi.Default.GroupVersion().String(),
  178. testtypeVersion: unlikelyGV.String(),
  179. rcVersion: testapi.Default.GroupVersion().String(), // see expected behavior 3b
  180. },
  181. "handles common version": {
  182. outputVersion: testapi.Default.GroupVersion().String(),
  183. listVersion: testapi.Default.GroupVersion().String(),
  184. testtypeVersion: unlikelyGV.String(),
  185. rcVersion: testapi.Default.GroupVersion().String(),
  186. },
  187. }
  188. for k, test := range testCases {
  189. apiCodec := testapi.Default.Codec()
  190. apiNegotiatedSerializer := testapi.Default.NegotiatedSerializer()
  191. regularClient := &fake.RESTClient{
  192. NegotiatedSerializer: apiNegotiatedSerializer,
  193. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  194. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(apiCodec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})}, nil
  195. }),
  196. }
  197. f, tf, codec := NewMixedFactory(regularClient)
  198. negotiatedSerializer := serializer.NegotiatedSerializerWrapper(
  199. runtime.SerializerInfo{Serializer: codec},
  200. runtime.StreamSerializerInfo{})
  201. tf.Printer = &testPrinter{}
  202. tf.Client = &fake.RESTClient{
  203. NegotiatedSerializer: negotiatedSerializer,
  204. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  205. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &internalType{Name: "foo"})}, nil
  206. }),
  207. }
  208. tf.Namespace = "test"
  209. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  210. buf := bytes.NewBuffer([]byte{})
  211. errBuf := bytes.NewBuffer([]byte{})
  212. cmd := NewCmdGet(f, buf, errBuf)
  213. cmd.SetOutput(buf)
  214. cmd.Flags().Set("output", "json")
  215. cmd.Flags().Set("output-version", test.outputVersion)
  216. err := RunGet(f, buf, errBuf, cmd, []string{"type/foo", "replicationcontrollers/foo"}, &GetOptions{})
  217. if err != nil {
  218. t.Errorf("%s: unexpected error: %v", k, err)
  219. continue
  220. }
  221. out := make(map[string]interface{})
  222. if err := encjson.Unmarshal(buf.Bytes(), &out); err != nil {
  223. t.Errorf("%s: unexpected error: %v\n%s", k, err, buf.String())
  224. continue
  225. }
  226. if out["apiVersion"] != test.listVersion {
  227. t.Errorf("%s: unexpected list: %#v", k, out)
  228. }
  229. arr := out["items"].([]interface{})
  230. if arr[0].(map[string]interface{})["apiVersion"] != test.testtypeVersion {
  231. t.Errorf("%s: unexpected list: %#v", k, out)
  232. }
  233. if arr[1].(map[string]interface{})["apiVersion"] != test.rcVersion {
  234. t.Errorf("%s: unexpected list: %#v", k, out)
  235. }
  236. }
  237. }
  238. // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
  239. func TestGetSchemaObject(t *testing.T) {
  240. f, tf, _, _ := NewTestFactory()
  241. tf.Mapper = testapi.Default.RESTMapper()
  242. tf.Typer = api.Scheme
  243. codec := testapi.Default.Codec()
  244. ns := testapi.Default.NegotiatedSerializer()
  245. tf.Printer = &testPrinter{}
  246. tf.Client = &fake.RESTClient{
  247. NegotiatedSerializer: ns,
  248. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.ReplicationController{ObjectMeta: api.ObjectMeta{Name: "foo"}})},
  249. }
  250. tf.Namespace = "test"
  251. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: "v1"}}}
  252. buf := bytes.NewBuffer([]byte{})
  253. errBuf := bytes.NewBuffer([]byte{})
  254. cmd := NewCmdGet(f, buf, errBuf)
  255. cmd.Run(cmd, []string{"replicationcontrollers", "foo"})
  256. if !strings.Contains(buf.String(), "\"foo\"") {
  257. t.Errorf("unexpected output: %s", buf.String())
  258. }
  259. }
  260. func TestGetObjects(t *testing.T) {
  261. pods, _, _ := testData()
  262. f, tf, codec, ns := NewAPIFactory()
  263. tf.Printer = &testPrinter{}
  264. tf.Client = &fake.RESTClient{
  265. NegotiatedSerializer: ns,
  266. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
  267. }
  268. tf.Namespace = "test"
  269. buf := bytes.NewBuffer([]byte{})
  270. errBuf := bytes.NewBuffer([]byte{})
  271. cmd := NewCmdGet(f, buf, errBuf)
  272. cmd.SetOutput(buf)
  273. cmd.Run(cmd, []string{"pods", "foo"})
  274. expected := []runtime.Object{&pods.Items[0]}
  275. actual := tf.Printer.(*testPrinter).Objects
  276. if !reflect.DeepEqual(expected, actual) {
  277. t.Errorf("unexpected object: %#v", actual)
  278. }
  279. if len(buf.String()) == 0 {
  280. t.Errorf("unexpected empty output")
  281. }
  282. }
  283. func TestGetSortedObjects(t *testing.T) {
  284. pods := &api.PodList{
  285. ListMeta: unversioned.ListMeta{
  286. ResourceVersion: "15",
  287. },
  288. Items: []api.Pod{
  289. {
  290. ObjectMeta: api.ObjectMeta{Name: "c", Namespace: "test", ResourceVersion: "10"},
  291. Spec: apitesting.DeepEqualSafePodSpec(),
  292. },
  293. {
  294. ObjectMeta: api.ObjectMeta{Name: "b", Namespace: "test", ResourceVersion: "11"},
  295. Spec: apitesting.DeepEqualSafePodSpec(),
  296. },
  297. {
  298. ObjectMeta: api.ObjectMeta{Name: "a", Namespace: "test", ResourceVersion: "9"},
  299. Spec: apitesting.DeepEqualSafePodSpec(),
  300. },
  301. },
  302. }
  303. f, tf, codec, ns := NewAPIFactory()
  304. tf.Printer = &testPrinter{}
  305. tf.Client = &fake.RESTClient{
  306. NegotiatedSerializer: ns,
  307. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
  308. }
  309. tf.Namespace = "test"
  310. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &unversioned.GroupVersion{Version: "v1"}}}
  311. buf := bytes.NewBuffer([]byte{})
  312. errBuf := bytes.NewBuffer([]byte{})
  313. cmd := NewCmdGet(f, buf, errBuf)
  314. cmd.SetOutput(buf)
  315. // sorting with metedata.name
  316. cmd.Flags().Set("sort-by", ".metadata.name")
  317. cmd.Run(cmd, []string{"pods"})
  318. // expect sorted: a,b,c
  319. expected := []runtime.Object{&pods.Items[2], &pods.Items[1], &pods.Items[0]}
  320. actual := tf.Printer.(*testPrinter).Objects
  321. if !reflect.DeepEqual(expected, actual) {
  322. t.Errorf("unexpected object: %#v", actual)
  323. }
  324. if len(buf.String()) == 0 {
  325. t.Errorf("unexpected empty output")
  326. }
  327. }
  328. func TestGetObjectsIdentifiedByFile(t *testing.T) {
  329. pods, _, _ := testData()
  330. f, tf, codec, ns := NewAPIFactory()
  331. tf.Printer = &testPrinter{}
  332. tf.Client = &fake.RESTClient{
  333. NegotiatedSerializer: ns,
  334. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
  335. }
  336. tf.Namespace = "test"
  337. buf := bytes.NewBuffer([]byte{})
  338. errBuf := bytes.NewBuffer([]byte{})
  339. cmd := NewCmdGet(f, buf, errBuf)
  340. cmd.SetOutput(buf)
  341. cmd.Flags().Set("filename", "../../../examples/storage/cassandra/cassandra-controller.yaml")
  342. cmd.Run(cmd, []string{})
  343. expected := []runtime.Object{&pods.Items[0]}
  344. actual := tf.Printer.(*testPrinter).Objects
  345. if !reflect.DeepEqual(expected, actual) {
  346. t.Errorf("unexpected object: %#v", actual)
  347. }
  348. if len(buf.String()) == 0 {
  349. t.Errorf("unexpected empty output")
  350. }
  351. }
  352. func TestGetListObjects(t *testing.T) {
  353. pods, _, _ := testData()
  354. f, tf, codec, ns := NewAPIFactory()
  355. tf.Printer = &testPrinter{}
  356. tf.Client = &fake.RESTClient{
  357. NegotiatedSerializer: ns,
  358. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
  359. }
  360. tf.Namespace = "test"
  361. buf := bytes.NewBuffer([]byte{})
  362. errBuf := bytes.NewBuffer([]byte{})
  363. cmd := NewCmdGet(f, buf, errBuf)
  364. cmd.SetOutput(buf)
  365. cmd.Run(cmd, []string{"pods"})
  366. expected, err := extractResourceList([]runtime.Object{pods})
  367. if err != nil {
  368. t.Fatalf("unexpected error: %v", err)
  369. }
  370. actual := tf.Printer.(*testPrinter).Objects
  371. if !reflect.DeepEqual(expected, actual) {
  372. t.Errorf("unexpected object: expected %#v, got %#v", expected, actual)
  373. }
  374. if len(buf.String()) == 0 {
  375. t.Errorf("unexpected empty output")
  376. }
  377. }
  378. func extractResourceList(objs []runtime.Object) ([]runtime.Object, error) {
  379. finalObjs := []runtime.Object{}
  380. for _, obj := range objs {
  381. items, err := meta.ExtractList(obj)
  382. if err != nil {
  383. return nil, err
  384. }
  385. for _, item := range items {
  386. finalObjs = append(finalObjs, item)
  387. }
  388. }
  389. return finalObjs, nil
  390. }
  391. func TestGetAllListObjects(t *testing.T) {
  392. pods, _, _ := testData()
  393. f, tf, codec, ns := NewAPIFactory()
  394. tf.Printer = &testPrinter{}
  395. tf.Client = &fake.RESTClient{
  396. NegotiatedSerializer: ns,
  397. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)},
  398. }
  399. tf.Namespace = "test"
  400. buf := bytes.NewBuffer([]byte{})
  401. errBuf := bytes.NewBuffer([]byte{})
  402. cmd := NewCmdGet(f, buf, errBuf)
  403. cmd.SetOutput(buf)
  404. cmd.Flags().Set("show-all", "true")
  405. cmd.Run(cmd, []string{"pods"})
  406. expected, err := extractResourceList([]runtime.Object{pods})
  407. if err != nil {
  408. t.Fatalf("unexpected error: %v", err)
  409. }
  410. actual := tf.Printer.(*testPrinter).Objects
  411. if !reflect.DeepEqual(expected, actual) {
  412. t.Errorf("unexpected object: %#v %#v", expected, actual)
  413. }
  414. if len(buf.String()) == 0 {
  415. t.Errorf("unexpected empty output")
  416. }
  417. }
  418. func TestGetListComponentStatus(t *testing.T) {
  419. statuses := testComponentStatusData()
  420. f, tf, codec, ns := NewAPIFactory()
  421. tf.Printer = &testPrinter{}
  422. tf.Client = &fake.RESTClient{
  423. NegotiatedSerializer: ns,
  424. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, statuses)},
  425. }
  426. tf.Namespace = "test"
  427. buf := bytes.NewBuffer([]byte{})
  428. errBuf := bytes.NewBuffer([]byte{})
  429. cmd := NewCmdGet(f, buf, errBuf)
  430. cmd.SetOutput(buf)
  431. cmd.Run(cmd, []string{"componentstatuses"})
  432. expected, err := extractResourceList([]runtime.Object{statuses})
  433. if err != nil {
  434. t.Fatalf("unexpected error: %v", err)
  435. }
  436. actual := tf.Printer.(*testPrinter).Objects
  437. if !reflect.DeepEqual(expected, actual) {
  438. t.Errorf("unexpected object: expected %#v, got %#v", expected, actual)
  439. }
  440. if len(buf.String()) == 0 {
  441. t.Errorf("unexpected empty output")
  442. }
  443. }
  444. func TestGetMultipleTypeObjects(t *testing.T) {
  445. pods, svc, _ := testData()
  446. f, tf, codec, ns := NewAPIFactory()
  447. tf.Printer = &testPrinter{}
  448. tf.Client = &fake.RESTClient{
  449. NegotiatedSerializer: ns,
  450. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  451. switch req.URL.Path {
  452. case "/namespaces/test/pods":
  453. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
  454. case "/namespaces/test/services":
  455. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
  456. default:
  457. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  458. return nil, nil
  459. }
  460. }),
  461. }
  462. tf.Namespace = "test"
  463. buf := bytes.NewBuffer([]byte{})
  464. errBuf := bytes.NewBuffer([]byte{})
  465. cmd := NewCmdGet(f, buf, errBuf)
  466. cmd.SetOutput(buf)
  467. cmd.Run(cmd, []string{"pods,services"})
  468. expected, err := extractResourceList([]runtime.Object{pods, svc})
  469. if err != nil {
  470. t.Fatalf("unexpected error: %v", err)
  471. }
  472. actual := tf.Printer.(*testPrinter).Objects
  473. if !reflect.DeepEqual(expected, actual) {
  474. t.Errorf("unexpected object: %#v", actual)
  475. }
  476. if len(buf.String()) == 0 {
  477. t.Errorf("unexpected empty output")
  478. }
  479. }
  480. func TestGetMultipleTypeObjectsAsList(t *testing.T) {
  481. pods, svc, _ := testData()
  482. f, tf, codec, ns := NewAPIFactory()
  483. tf.Printer = &testPrinter{}
  484. tf.Client = &fake.RESTClient{
  485. NegotiatedSerializer: ns,
  486. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  487. switch req.URL.Path {
  488. case "/namespaces/test/pods":
  489. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
  490. case "/namespaces/test/services":
  491. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
  492. default:
  493. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  494. return nil, nil
  495. }
  496. }),
  497. }
  498. tf.Namespace = "test"
  499. tf.ClientConfig = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: testapi.Default.GroupVersion()}}
  500. buf := bytes.NewBuffer([]byte{})
  501. errBuf := bytes.NewBuffer([]byte{})
  502. cmd := NewCmdGet(f, buf, errBuf)
  503. cmd.SetOutput(buf)
  504. cmd.Flags().Set("output", "json")
  505. cmd.Run(cmd, []string{"pods,services"})
  506. if tf.Printer.(*testPrinter).Objects != nil {
  507. t.Errorf("unexpected print to default printer")
  508. }
  509. out, err := runtime.Decode(codec, buf.Bytes())
  510. if err != nil {
  511. t.Fatalf("unexpected error: %v", err)
  512. }
  513. list, err := meta.ExtractList(out)
  514. if err != nil {
  515. t.Fatalf("unexpected error: %v", err)
  516. }
  517. if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
  518. t.Fatalf("unexpected error: %v", errs)
  519. }
  520. if err := meta.SetList(out, list); err != nil {
  521. t.Fatalf("unexpected error: %v", err)
  522. }
  523. expected := &api.List{
  524. Items: []runtime.Object{
  525. &pods.Items[0],
  526. &pods.Items[1],
  527. &svc.Items[0],
  528. },
  529. }
  530. if !reflect.DeepEqual(expected, out) {
  531. t.Errorf("unexpected output: %#v", out)
  532. }
  533. }
  534. func TestGetMultipleTypeObjectsWithSelector(t *testing.T) {
  535. pods, svc, _ := testData()
  536. f, tf, codec, ns := NewAPIFactory()
  537. tf.Printer = &testPrinter{}
  538. tf.Client = &fake.RESTClient{
  539. NegotiatedSerializer: ns,
  540. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  541. if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" {
  542. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  543. }
  544. switch req.URL.Path {
  545. case "/namespaces/test/pods":
  546. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, pods)}, nil
  547. case "/namespaces/test/services":
  548. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, svc)}, nil
  549. default:
  550. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  551. return nil, nil
  552. }
  553. }),
  554. }
  555. tf.Namespace = "test"
  556. buf := bytes.NewBuffer([]byte{})
  557. errBuf := bytes.NewBuffer([]byte{})
  558. cmd := NewCmdGet(f, buf, errBuf)
  559. cmd.SetOutput(buf)
  560. cmd.Flags().Set("selector", "a=b")
  561. cmd.Run(cmd, []string{"pods,services"})
  562. expected, err := extractResourceList([]runtime.Object{pods, svc})
  563. if err != nil {
  564. t.Fatalf("unexpected error: %v", err)
  565. }
  566. actual := tf.Printer.(*testPrinter).Objects
  567. if !reflect.DeepEqual(expected, actual) {
  568. t.Errorf("unexpected object: %#v", actual)
  569. }
  570. if len(buf.String()) == 0 {
  571. t.Errorf("unexpected empty output")
  572. }
  573. }
  574. func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
  575. _, svc, _ := testData()
  576. node := &api.Node{
  577. ObjectMeta: api.ObjectMeta{
  578. Name: "foo",
  579. },
  580. Spec: api.NodeSpec{
  581. ExternalID: "ext",
  582. },
  583. }
  584. f, tf, codec, ns := NewAPIFactory()
  585. tf.Printer = &testPrinter{}
  586. tf.Client = &fake.RESTClient{
  587. NegotiatedSerializer: ns,
  588. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  589. switch req.URL.Path {
  590. case "/nodes/foo":
  591. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, node)}, nil
  592. case "/namespaces/test/services/bar":
  593. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &svc.Items[0])}, nil
  594. default:
  595. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  596. return nil, nil
  597. }
  598. }),
  599. }
  600. tf.Namespace = "test"
  601. buf := bytes.NewBuffer([]byte{})
  602. errBuf := bytes.NewBuffer([]byte{})
  603. cmd := NewCmdGet(f, buf, errBuf)
  604. cmd.SetOutput(buf)
  605. cmd.Run(cmd, []string{"services/bar", "node/foo"})
  606. expected := []runtime.Object{&svc.Items[0], node}
  607. actual := tf.Printer.(*testPrinter).Objects
  608. if !api.Semantic.DeepEqual(expected, actual) {
  609. t.Errorf("unexpected object: %s", diff.ObjectDiff(expected, actual))
  610. }
  611. if len(buf.String()) == 0 {
  612. t.Errorf("unexpected empty output")
  613. }
  614. }
  615. func TestGetByNameForcesFlag(t *testing.T) {
  616. pods, _, _ := testData()
  617. f, tf, codec, ns := NewAPIFactory()
  618. tf.Printer = &testPrinter{}
  619. tf.Client = &fake.RESTClient{
  620. NegotiatedSerializer: ns,
  621. Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
  622. }
  623. tf.Namespace = "test"
  624. buf := bytes.NewBuffer([]byte{})
  625. errBuf := bytes.NewBuffer([]byte{})
  626. cmd := NewCmdGet(f, buf, errBuf)
  627. cmd.SetOutput(buf)
  628. cmd.Run(cmd, []string{"pods", "foo"})
  629. showAllFlag, _ := cmd.Flags().GetBool("show-all")
  630. if !showAllFlag {
  631. t.Errorf("expected showAll to be true when getting resource by name")
  632. }
  633. }
  634. func watchTestData() ([]api.Pod, []watch.Event) {
  635. pods := []api.Pod{
  636. {
  637. ObjectMeta: api.ObjectMeta{
  638. Name: "bar",
  639. Namespace: "test",
  640. ResourceVersion: "9",
  641. },
  642. Spec: apitesting.DeepEqualSafePodSpec(),
  643. },
  644. {
  645. ObjectMeta: api.ObjectMeta{
  646. Name: "foo",
  647. Namespace: "test",
  648. ResourceVersion: "10",
  649. },
  650. Spec: apitesting.DeepEqualSafePodSpec(),
  651. },
  652. }
  653. events := []watch.Event{
  654. // current state events
  655. {
  656. Type: watch.Added,
  657. Object: &api.Pod{
  658. ObjectMeta: api.ObjectMeta{
  659. Name: "bar",
  660. Namespace: "test",
  661. ResourceVersion: "9",
  662. },
  663. Spec: apitesting.DeepEqualSafePodSpec(),
  664. },
  665. },
  666. {
  667. Type: watch.Added,
  668. Object: &api.Pod{
  669. ObjectMeta: api.ObjectMeta{
  670. Name: "foo",
  671. Namespace: "test",
  672. ResourceVersion: "10",
  673. },
  674. Spec: apitesting.DeepEqualSafePodSpec(),
  675. },
  676. },
  677. // resource events
  678. {
  679. Type: watch.Modified,
  680. Object: &api.Pod{
  681. ObjectMeta: api.ObjectMeta{
  682. Name: "foo",
  683. Namespace: "test",
  684. ResourceVersion: "11",
  685. },
  686. Spec: apitesting.DeepEqualSafePodSpec(),
  687. },
  688. },
  689. {
  690. Type: watch.Deleted,
  691. Object: &api.Pod{
  692. ObjectMeta: api.ObjectMeta{
  693. Name: "foo",
  694. Namespace: "test",
  695. ResourceVersion: "12",
  696. },
  697. Spec: apitesting.DeepEqualSafePodSpec(),
  698. },
  699. },
  700. }
  701. return pods, events
  702. }
  703. func TestWatchSelector(t *testing.T) {
  704. pods, events := watchTestData()
  705. f, tf, codec, ns := NewAPIFactory()
  706. tf.Printer = &testPrinter{}
  707. podList := &api.PodList{
  708. Items: pods,
  709. ListMeta: unversioned.ListMeta{
  710. ResourceVersion: "10",
  711. },
  712. }
  713. tf.Client = &fake.RESTClient{
  714. NegotiatedSerializer: ns,
  715. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  716. if req.URL.Query().Get(unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())) != "a=b" {
  717. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  718. }
  719. switch req.URL.Path {
  720. case "/namespaces/test/pods":
  721. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil
  722. case "/watch/namespaces/test/pods":
  723. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[2:])}, nil
  724. default:
  725. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  726. return nil, nil
  727. }
  728. }),
  729. }
  730. tf.Namespace = "test"
  731. buf := bytes.NewBuffer([]byte{})
  732. errBuf := bytes.NewBuffer([]byte{})
  733. cmd := NewCmdGet(f, buf, errBuf)
  734. cmd.SetOutput(buf)
  735. cmd.Flags().Set("watch", "true")
  736. cmd.Flags().Set("selector", "a=b")
  737. cmd.Run(cmd, []string{"pods"})
  738. expected := []runtime.Object{podList, events[2].Object, events[3].Object}
  739. actual := tf.Printer.(*testPrinter).Objects
  740. if !reflect.DeepEqual(expected, actual) {
  741. t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected, actual)
  742. }
  743. if len(buf.String()) == 0 {
  744. t.Errorf("unexpected empty output")
  745. }
  746. }
  747. func TestWatchResource(t *testing.T) {
  748. pods, events := watchTestData()
  749. f, tf, codec, ns := NewAPIFactory()
  750. tf.Printer = &testPrinter{}
  751. tf.Client = &fake.RESTClient{
  752. NegotiatedSerializer: ns,
  753. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  754. switch req.URL.Path {
  755. case "/namespaces/test/pods/foo":
  756. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods[1])}, nil
  757. case "/watch/namespaces/test/pods/foo":
  758. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[1:])}, nil
  759. default:
  760. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  761. return nil, nil
  762. }
  763. }),
  764. }
  765. tf.Namespace = "test"
  766. buf := bytes.NewBuffer([]byte{})
  767. errBuf := bytes.NewBuffer([]byte{})
  768. cmd := NewCmdGet(f, buf, errBuf)
  769. cmd.SetOutput(buf)
  770. cmd.Flags().Set("watch", "true")
  771. cmd.Run(cmd, []string{"pods", "foo"})
  772. expected := []runtime.Object{&pods[1], events[2].Object, events[3].Object}
  773. actual := tf.Printer.(*testPrinter).Objects
  774. if !reflect.DeepEqual(expected, actual) {
  775. t.Errorf("unexpected object:\nExpected: %#v\n\nGot: %#v\n\n", expected, actual)
  776. }
  777. if len(buf.String()) == 0 {
  778. t.Errorf("unexpected empty output")
  779. }
  780. }
  781. func TestWatchResourceIdentifiedByFile(t *testing.T) {
  782. pods, events := watchTestData()
  783. f, tf, codec, ns := NewAPIFactory()
  784. tf.Printer = &testPrinter{}
  785. tf.Client = &fake.RESTClient{
  786. NegotiatedSerializer: ns,
  787. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  788. switch req.URL.Path {
  789. case "/namespaces/test/replicationcontrollers/cassandra":
  790. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods[1])}, nil
  791. case "/watch/namespaces/test/replicationcontrollers/cassandra":
  792. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[1:])}, nil
  793. default:
  794. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  795. return nil, nil
  796. }
  797. }),
  798. }
  799. tf.Namespace = "test"
  800. buf := bytes.NewBuffer([]byte{})
  801. errBuf := bytes.NewBuffer([]byte{})
  802. cmd := NewCmdGet(f, buf, errBuf)
  803. cmd.SetOutput(buf)
  804. cmd.Flags().Set("watch", "true")
  805. cmd.Flags().Set("filename", "../../../examples/storage/cassandra/cassandra-controller.yaml")
  806. cmd.Run(cmd, []string{})
  807. expected := []runtime.Object{&pods[1], events[2].Object, events[3].Object}
  808. actual := tf.Printer.(*testPrinter).Objects
  809. if !reflect.DeepEqual(expected, actual) {
  810. t.Errorf("expected object: %#v unexpected object: %#v", expected, actual)
  811. }
  812. if len(buf.String()) == 0 {
  813. t.Errorf("unexpected empty output")
  814. }
  815. }
  816. func TestWatchOnlyResource(t *testing.T) {
  817. pods, events := watchTestData()
  818. f, tf, codec, ns := NewAPIFactory()
  819. tf.Printer = &testPrinter{}
  820. tf.Client = &fake.RESTClient{
  821. NegotiatedSerializer: ns,
  822. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  823. switch req.URL.Path {
  824. case "/namespaces/test/pods/foo":
  825. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods[1])}, nil
  826. case "/watch/namespaces/test/pods/foo":
  827. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[1:])}, nil
  828. default:
  829. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  830. return nil, nil
  831. }
  832. }),
  833. }
  834. tf.Namespace = "test"
  835. buf := bytes.NewBuffer([]byte{})
  836. errBuf := bytes.NewBuffer([]byte{})
  837. cmd := NewCmdGet(f, buf, errBuf)
  838. cmd.SetOutput(buf)
  839. cmd.Flags().Set("watch-only", "true")
  840. cmd.Run(cmd, []string{"pods", "foo"})
  841. expected := []runtime.Object{events[2].Object, events[3].Object}
  842. actual := tf.Printer.(*testPrinter).Objects
  843. if !reflect.DeepEqual(expected, actual) {
  844. t.Errorf("unexpected object: %#v", actual)
  845. }
  846. if len(buf.String()) == 0 {
  847. t.Errorf("unexpected empty output")
  848. }
  849. }
  850. func TestWatchOnlyList(t *testing.T) {
  851. pods, events := watchTestData()
  852. f, tf, codec, ns := NewAPIFactory()
  853. tf.Printer = &testPrinter{}
  854. podList := &api.PodList{
  855. Items: pods,
  856. ListMeta: unversioned.ListMeta{
  857. ResourceVersion: "10",
  858. },
  859. }
  860. tf.Client = &fake.RESTClient{
  861. NegotiatedSerializer: ns,
  862. Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
  863. switch req.URL.Path {
  864. case "/namespaces/test/pods":
  865. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil
  866. case "/watch/namespaces/test/pods":
  867. return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[2:])}, nil
  868. default:
  869. t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
  870. return nil, nil
  871. }
  872. }),
  873. }
  874. tf.Namespace = "test"
  875. buf := bytes.NewBuffer([]byte{})
  876. errBuf := bytes.NewBuffer([]byte{})
  877. cmd := NewCmdGet(f, buf, errBuf)
  878. cmd.SetOutput(buf)
  879. cmd.Flags().Set("watch-only", "true")
  880. cmd.Run(cmd, []string{"pods"})
  881. expected := []runtime.Object{events[2].Object, events[3].Object}
  882. actual := tf.Printer.(*testPrinter).Objects
  883. if !reflect.DeepEqual(expected, actual) {
  884. t.Errorf("unexpected object: %#v", actual)
  885. }
  886. if len(buf.String()) == 0 {
  887. t.Errorf("unexpected empty output")
  888. }
  889. }
  890. func watchBody(codec runtime.Codec, events []watch.Event) io.ReadCloser {
  891. buf := bytes.NewBuffer([]byte{})
  892. enc := versioned.NewEncoder(streaming.NewEncoder(buf, codec), codec)
  893. for i := range events {
  894. enc.Encode(&events[i])
  895. }
  896. return json.Framer.NewFrameReader(ioutil.NopCloser(buf))
  897. }