cmd_test.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859
  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. "errors"
  17. "fmt"
  18. "io"
  19. "io/ioutil"
  20. "net/http"
  21. "os"
  22. "reflect"
  23. "testing"
  24. "time"
  25. "k8s.io/kubernetes/pkg/api"
  26. "k8s.io/kubernetes/pkg/api/meta"
  27. "k8s.io/kubernetes/pkg/api/testapi"
  28. "k8s.io/kubernetes/pkg/api/unversioned"
  29. "k8s.io/kubernetes/pkg/api/validation"
  30. "k8s.io/kubernetes/pkg/client/restclient"
  31. "k8s.io/kubernetes/pkg/client/typed/discovery"
  32. client "k8s.io/kubernetes/pkg/client/unversioned"
  33. "k8s.io/kubernetes/pkg/client/unversioned/fake"
  34. "k8s.io/kubernetes/pkg/kubectl"
  35. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  36. "k8s.io/kubernetes/pkg/kubectl/resource"
  37. "k8s.io/kubernetes/pkg/runtime"
  38. "k8s.io/kubernetes/pkg/runtime/serializer"
  39. "k8s.io/kubernetes/pkg/util/strings"
  40. )
  41. func initTestErrorHandler(t *testing.T) {
  42. cmdutil.BehaviorOnFatal(func(str string, code int) {
  43. t.Errorf("Error running command (exit code %d): %s", code, str)
  44. })
  45. }
  46. func defaultHeader() http.Header {
  47. header := http.Header{}
  48. header.Set("Content-Type", runtime.ContentTypeJSON)
  49. return header
  50. }
  51. func defaultClientConfig() *restclient.Config {
  52. return &restclient.Config{
  53. ContentConfig: restclient.ContentConfig{
  54. ContentType: runtime.ContentTypeJSON,
  55. GroupVersion: testapi.Default.GroupVersion(),
  56. },
  57. }
  58. }
  59. type internalType struct {
  60. Kind string
  61. APIVersion string
  62. Name string
  63. }
  64. type externalType struct {
  65. Kind string `json:"kind"`
  66. APIVersion string `json:"apiVersion"`
  67. Name string `json:"name"`
  68. }
  69. type ExternalType2 struct {
  70. Kind string `json:"kind"`
  71. APIVersion string `json:"apiVersion"`
  72. Name string `json:"name"`
  73. }
  74. func (obj *internalType) GetObjectKind() unversioned.ObjectKind { return obj }
  75. func (obj *internalType) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
  76. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  77. }
  78. func (obj *internalType) GroupVersionKind() unversioned.GroupVersionKind {
  79. return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  80. }
  81. func (obj *externalType) GetObjectKind() unversioned.ObjectKind { return obj }
  82. func (obj *externalType) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
  83. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  84. }
  85. func (obj *externalType) GroupVersionKind() unversioned.GroupVersionKind {
  86. return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  87. }
  88. func (obj *ExternalType2) GetObjectKind() unversioned.ObjectKind { return obj }
  89. func (obj *ExternalType2) SetGroupVersionKind(gvk unversioned.GroupVersionKind) {
  90. obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
  91. }
  92. func (obj *ExternalType2) GroupVersionKind() unversioned.GroupVersionKind {
  93. return unversioned.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
  94. }
  95. var versionErr = errors.New("not a version")
  96. func versionErrIfFalse(b bool) error {
  97. if b {
  98. return nil
  99. }
  100. return versionErr
  101. }
  102. var validVersion = testapi.Default.GroupVersion().Version
  103. var internalGV = unversioned.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
  104. var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
  105. var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion}
  106. func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
  107. scheme := runtime.NewScheme()
  108. scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{})
  109. scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{})
  110. //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
  111. scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{})
  112. codecs := serializer.NewCodecFactory(scheme)
  113. codec := codecs.LegacyCodec(unlikelyGV)
  114. mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
  115. return &meta.VersionInterfaces{
  116. ObjectConvertor: scheme,
  117. MetadataAccessor: meta.NewAccessor(),
  118. }, versionErrIfFalse(version == validVersionGV || version == unlikelyGV)
  119. })
  120. for _, gv := range []unversioned.GroupVersion{unlikelyGV, validVersionGV} {
  121. for kind := range scheme.KnownTypes(gv) {
  122. gvk := gv.WithKind(kind)
  123. scope := meta.RESTScopeNamespace
  124. mapper.Add(gvk, scope)
  125. }
  126. }
  127. return scheme, mapper, codec
  128. }
  129. type testPrinter struct {
  130. Objects []runtime.Object
  131. Err error
  132. }
  133. func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
  134. t.Objects = append(t.Objects, obj)
  135. fmt.Fprintf(out, "%#v", obj)
  136. return t.Err
  137. }
  138. // TODO: implement HandledResources()
  139. func (t *testPrinter) HandledResources() []string {
  140. return []string{}
  141. }
  142. func (t *testPrinter) FinishPrint(output io.Writer, res string) error {
  143. return nil
  144. }
  145. type testDescriber struct {
  146. Name, Namespace string
  147. Settings kubectl.DescriberSettings
  148. Output string
  149. Err error
  150. }
  151. func (t *testDescriber) Describe(namespace, name string, describerSettings kubectl.DescriberSettings) (output string, err error) {
  152. t.Namespace, t.Name = namespace, name
  153. t.Settings = describerSettings
  154. return t.Output, t.Err
  155. }
  156. type testFactory struct {
  157. Mapper meta.RESTMapper
  158. Typer runtime.ObjectTyper
  159. Client kubectl.RESTClient
  160. Describer kubectl.Describer
  161. Printer kubectl.ResourcePrinter
  162. Validator validation.Schema
  163. Namespace string
  164. ClientConfig *restclient.Config
  165. Err error
  166. }
  167. func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec, runtime.NegotiatedSerializer) {
  168. scheme, mapper, codec := newExternalScheme()
  169. t := &testFactory{
  170. Validator: validation.NullSchema{},
  171. Mapper: mapper,
  172. Typer: scheme,
  173. }
  174. negotiatedSerializer := serializer.NegotiatedSerializerWrapper(
  175. runtime.SerializerInfo{Serializer: codec},
  176. runtime.StreamSerializerInfo{})
  177. return &cmdutil.Factory{
  178. Object: func(discovery bool) (meta.RESTMapper, runtime.ObjectTyper) {
  179. priorityRESTMapper := meta.PriorityRESTMapper{
  180. Delegate: t.Mapper,
  181. ResourcePriority: []unversioned.GroupVersionResource{
  182. {Group: meta.AnyGroup, Version: "v1", Resource: meta.AnyResource},
  183. },
  184. KindPriority: []unversioned.GroupVersionKind{
  185. {Group: meta.AnyGroup, Version: "v1", Kind: meta.AnyKind},
  186. },
  187. }
  188. return priorityRESTMapper, t.Typer
  189. },
  190. ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) {
  191. return t.Client, t.Err
  192. },
  193. Decoder: func(bool) runtime.Decoder {
  194. return codec
  195. },
  196. JSONEncoder: func() runtime.Encoder {
  197. return codec
  198. },
  199. Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
  200. return t.Describer, t.Err
  201. },
  202. Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
  203. return t.Printer, t.Err
  204. },
  205. Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
  206. return t.Validator, t.Err
  207. },
  208. DefaultNamespace: func() (string, bool, error) {
  209. return t.Namespace, false, t.Err
  210. },
  211. ClientConfig: func() (*restclient.Config, error) {
  212. return t.ClientConfig, t.Err
  213. },
  214. }, t, codec, negotiatedSerializer
  215. }
  216. func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFactory, runtime.Codec) {
  217. f, t, c, _ := NewTestFactory()
  218. var multiRESTMapper meta.MultiRESTMapper
  219. multiRESTMapper = append(multiRESTMapper, t.Mapper)
  220. multiRESTMapper = append(multiRESTMapper, testapi.Default.RESTMapper())
  221. f.Object = func(discovery bool) (meta.RESTMapper, runtime.ObjectTyper) {
  222. priorityRESTMapper := meta.PriorityRESTMapper{
  223. Delegate: multiRESTMapper,
  224. ResourcePriority: []unversioned.GroupVersionResource{
  225. {Group: meta.AnyGroup, Version: "v1", Resource: meta.AnyResource},
  226. },
  227. KindPriority: []unversioned.GroupVersionKind{
  228. {Group: meta.AnyGroup, Version: "v1", Kind: meta.AnyKind},
  229. },
  230. }
  231. return priorityRESTMapper, runtime.MultiObjectTyper{t.Typer, api.Scheme}
  232. }
  233. f.ClientForMapping = func(m *meta.RESTMapping) (resource.RESTClient, error) {
  234. if m.ObjectConvertor == api.Scheme {
  235. return apiClient, t.Err
  236. }
  237. return t.Client, t.Err
  238. }
  239. return f, t, c
  240. }
  241. func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec, runtime.NegotiatedSerializer) {
  242. t := &testFactory{
  243. Validator: validation.NullSchema{},
  244. }
  245. f := &cmdutil.Factory{
  246. Object: func(discovery bool) (meta.RESTMapper, runtime.ObjectTyper) {
  247. return testapi.Default.RESTMapper(), api.Scheme
  248. },
  249. UnstructuredObject: func() (meta.RESTMapper, runtime.ObjectTyper, error) {
  250. groupResources := testDynamicResources()
  251. mapper := discovery.NewRESTMapper(groupResources, meta.InterfacesForUnstructured)
  252. typer := discovery.NewUnstructuredObjectTyper(groupResources)
  253. return kubectl.ShortcutExpander{RESTMapper: mapper}, typer, nil
  254. },
  255. Client: func() (*client.Client, error) {
  256. // Swap out the HTTP client out of the client with the fake's version.
  257. fakeClient := t.Client.(*fake.RESTClient)
  258. c := client.NewOrDie(t.ClientConfig)
  259. c.Client = fakeClient.Client
  260. c.ExtensionsClient.Client = fakeClient.Client
  261. return c, t.Err
  262. },
  263. ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) {
  264. return t.Client, t.Err
  265. },
  266. UnstructuredClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) {
  267. return t.Client, t.Err
  268. },
  269. Decoder: func(bool) runtime.Decoder {
  270. return testapi.Default.Codec()
  271. },
  272. JSONEncoder: func() runtime.Encoder {
  273. return testapi.Default.Codec()
  274. },
  275. Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
  276. return t.Describer, t.Err
  277. },
  278. Printer: func(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
  279. return t.Printer, t.Err
  280. },
  281. Validator: func(validate bool, cacheDir string) (validation.Schema, error) {
  282. return t.Validator, t.Err
  283. },
  284. DefaultNamespace: func() (string, bool, error) {
  285. return t.Namespace, false, t.Err
  286. },
  287. ClientConfig: func() (*restclient.Config, error) {
  288. return t.ClientConfig, t.Err
  289. },
  290. Generators: func(cmdName string) map[string]kubectl.Generator {
  291. return cmdutil.DefaultGenerators(cmdName)
  292. },
  293. LogsForObject: func(object, options runtime.Object) (*restclient.Request, error) {
  294. fakeClient := t.Client.(*fake.RESTClient)
  295. c := client.NewOrDie(t.ClientConfig)
  296. c.Client = fakeClient.Client
  297. switch t := object.(type) {
  298. case *api.Pod:
  299. opts, ok := options.(*api.PodLogOptions)
  300. if !ok {
  301. return nil, errors.New("provided options object is not a PodLogOptions")
  302. }
  303. return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil
  304. default:
  305. fqKinds, _, err := api.Scheme.ObjectKinds(object)
  306. if err != nil {
  307. return nil, err
  308. }
  309. return nil, fmt.Errorf("cannot get the logs from %v", fqKinds[0])
  310. }
  311. },
  312. }
  313. rf := cmdutil.NewFactory(nil)
  314. f.MapBasedSelectorForObject = rf.MapBasedSelectorForObject
  315. f.PortsForObject = rf.PortsForObject
  316. f.ProtocolsForObject = rf.ProtocolsForObject
  317. f.LabelsForObject = rf.LabelsForObject
  318. f.CanBeExposed = rf.CanBeExposed
  319. f.PrintObjectSpecificMessage = rf.PrintObjectSpecificMessage
  320. return f, t, testapi.Default.Codec(), testapi.Default.NegotiatedSerializer()
  321. }
  322. func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
  323. return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
  324. }
  325. func stringBody(body string) io.ReadCloser {
  326. return ioutil.NopCloser(bytes.NewReader([]byte(body)))
  327. }
  328. // TODO(jlowdermilk): refactor the Factory so we can test client versions properly,
  329. // with different client/server version skew scenarios.
  330. // Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
  331. //func TestClientVersions(t *testing.T) {
  332. // f := cmdutil.NewFactory(nil)
  333. //
  334. // version := testapi.Default.Version()
  335. // mapping := &meta.RESTMapping{
  336. // APIVersion: version,
  337. // }
  338. // c, err := f.ClientForMapping(mapping)
  339. // if err != nil {
  340. // t.Errorf("unexpected error: %v", err)
  341. // }
  342. // client := c.(*client.RESTClient)
  343. // if client.APIVersion() != version {
  344. // t.Errorf("unexpected Client APIVersion: %s %v", client.APIVersion, client)
  345. // }
  346. //}
  347. func Example_printReplicationControllerWithNamespace() {
  348. f, tf, _, ns := NewAPIFactory()
  349. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  350. WithNamespace: true,
  351. ColumnLabels: []string{},
  352. })
  353. tf.Client = &fake.RESTClient{
  354. NegotiatedSerializer: ns,
  355. Client: nil,
  356. }
  357. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  358. ctrl := &api.ReplicationController{
  359. ObjectMeta: api.ObjectMeta{
  360. Name: "foo",
  361. Namespace: "beep",
  362. Labels: map[string]string{"foo": "bar"},
  363. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  364. },
  365. Spec: api.ReplicationControllerSpec{
  366. Replicas: 1,
  367. Selector: map[string]string{"foo": "bar"},
  368. Template: &api.PodTemplateSpec{
  369. ObjectMeta: api.ObjectMeta{
  370. Labels: map[string]string{"foo": "bar"},
  371. },
  372. Spec: api.PodSpec{
  373. Containers: []api.Container{
  374. {
  375. Name: "foo",
  376. Image: "someimage",
  377. },
  378. },
  379. },
  380. },
  381. },
  382. Status: api.ReplicationControllerStatus{
  383. Replicas: 1,
  384. ReadyReplicas: 1,
  385. },
  386. }
  387. mapper, _ := f.Object(false)
  388. err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
  389. if err != nil {
  390. fmt.Printf("Unexpected error: %v", err)
  391. }
  392. // Output:
  393. // NAMESPACE NAME DESIRED CURRENT READY AGE
  394. // beep foo 1 1 1 10y
  395. }
  396. func Example_printMultiContainersReplicationControllerWithWide() {
  397. f, tf, _, ns := NewAPIFactory()
  398. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  399. Wide: true,
  400. ColumnLabels: []string{},
  401. })
  402. tf.Client = &fake.RESTClient{
  403. NegotiatedSerializer: ns,
  404. Client: nil,
  405. }
  406. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  407. ctrl := &api.ReplicationController{
  408. ObjectMeta: api.ObjectMeta{
  409. Name: "foo",
  410. Labels: map[string]string{"foo": "bar"},
  411. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  412. },
  413. Spec: api.ReplicationControllerSpec{
  414. Replicas: 1,
  415. Selector: map[string]string{"foo": "bar"},
  416. Template: &api.PodTemplateSpec{
  417. ObjectMeta: api.ObjectMeta{
  418. Labels: map[string]string{"foo": "bar"},
  419. },
  420. Spec: api.PodSpec{
  421. Containers: []api.Container{
  422. {
  423. Name: "foo",
  424. Image: "someimage",
  425. },
  426. {
  427. Name: "foo2",
  428. Image: "someimage2",
  429. },
  430. },
  431. },
  432. },
  433. },
  434. Status: api.ReplicationControllerStatus{
  435. Replicas: 1,
  436. },
  437. }
  438. mapper, _ := f.Object(false)
  439. err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
  440. if err != nil {
  441. fmt.Printf("Unexpected error: %v", err)
  442. }
  443. // Output:
  444. // NAME DESIRED CURRENT READY AGE CONTAINER(S) IMAGE(S) SELECTOR
  445. // foo 1 1 0 10y foo,foo2 someimage,someimage2 foo=bar
  446. }
  447. func Example_printReplicationController() {
  448. f, tf, _, ns := NewAPIFactory()
  449. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  450. ColumnLabels: []string{},
  451. })
  452. tf.Client = &fake.RESTClient{
  453. NegotiatedSerializer: ns,
  454. Client: nil,
  455. }
  456. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  457. ctrl := &api.ReplicationController{
  458. ObjectMeta: api.ObjectMeta{
  459. Name: "foo",
  460. Labels: map[string]string{"foo": "bar"},
  461. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  462. },
  463. Spec: api.ReplicationControllerSpec{
  464. Replicas: 1,
  465. Selector: map[string]string{"foo": "bar"},
  466. Template: &api.PodTemplateSpec{
  467. ObjectMeta: api.ObjectMeta{
  468. Labels: map[string]string{"foo": "bar"},
  469. },
  470. Spec: api.PodSpec{
  471. Containers: []api.Container{
  472. {
  473. Name: "foo",
  474. Image: "someimage",
  475. },
  476. {
  477. Name: "foo2",
  478. Image: "someimage",
  479. },
  480. },
  481. },
  482. },
  483. },
  484. Status: api.ReplicationControllerStatus{
  485. Replicas: 1,
  486. },
  487. }
  488. mapper, _ := f.Object(false)
  489. err := f.PrintObject(cmd, mapper, ctrl, os.Stdout)
  490. if err != nil {
  491. fmt.Printf("Unexpected error: %v", err)
  492. }
  493. // Output:
  494. // NAME DESIRED CURRENT READY AGE
  495. // foo 1 1 0 10y
  496. }
  497. func Example_printPodWithWideFormat() {
  498. f, tf, _, ns := NewAPIFactory()
  499. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  500. Wide: true,
  501. ColumnLabels: []string{},
  502. })
  503. tf.Client = &fake.RESTClient{
  504. NegotiatedSerializer: ns,
  505. Client: nil,
  506. }
  507. nodeName := "kubernetes-minion-abcd"
  508. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  509. pod := &api.Pod{
  510. ObjectMeta: api.ObjectMeta{
  511. Name: "test1",
  512. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  513. },
  514. Spec: api.PodSpec{
  515. Containers: make([]api.Container, 2),
  516. NodeName: nodeName,
  517. },
  518. Status: api.PodStatus{
  519. Phase: "podPhase",
  520. ContainerStatuses: []api.ContainerStatus{
  521. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  522. {RestartCount: 3},
  523. },
  524. PodIP: "10.1.1.3",
  525. },
  526. }
  527. mapper, _ := f.Object(false)
  528. err := f.PrintObject(cmd, mapper, pod, os.Stdout)
  529. if err != nil {
  530. fmt.Printf("Unexpected error: %v", err)
  531. }
  532. // Output:
  533. // NAME READY STATUS RESTARTS AGE IP NODE
  534. // test1 1/2 podPhase 6 10y 10.1.1.3 kubernetes-minion-abcd
  535. }
  536. func Example_printPodWithShowLabels() {
  537. f, tf, _, ns := NewAPIFactory()
  538. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  539. ShowLabels: true,
  540. ColumnLabels: []string{},
  541. })
  542. tf.Client = &fake.RESTClient{
  543. NegotiatedSerializer: ns,
  544. Client: nil,
  545. }
  546. nodeName := "kubernetes-minion-abcd"
  547. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  548. pod := &api.Pod{
  549. ObjectMeta: api.ObjectMeta{
  550. Name: "test1",
  551. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  552. Labels: map[string]string{
  553. "l1": "key",
  554. "l2": "value",
  555. },
  556. },
  557. Spec: api.PodSpec{
  558. Containers: make([]api.Container, 2),
  559. NodeName: nodeName,
  560. },
  561. Status: api.PodStatus{
  562. Phase: "podPhase",
  563. ContainerStatuses: []api.ContainerStatus{
  564. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  565. {RestartCount: 3},
  566. },
  567. },
  568. }
  569. mapper, _ := f.Object(false)
  570. err := f.PrintObject(cmd, mapper, pod, os.Stdout)
  571. if err != nil {
  572. fmt.Printf("Unexpected error: %v", err)
  573. }
  574. // Output:
  575. // NAME READY STATUS RESTARTS AGE LABELS
  576. // test1 1/2 podPhase 6 10y l1=key,l2=value
  577. }
  578. func newAllPhasePodList() *api.PodList {
  579. nodeName := "kubernetes-minion-abcd"
  580. return &api.PodList{
  581. Items: []api.Pod{
  582. {
  583. ObjectMeta: api.ObjectMeta{
  584. Name: "test1",
  585. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  586. },
  587. Spec: api.PodSpec{
  588. Containers: make([]api.Container, 2),
  589. NodeName: nodeName,
  590. },
  591. Status: api.PodStatus{
  592. Phase: api.PodPending,
  593. ContainerStatuses: []api.ContainerStatus{
  594. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  595. {RestartCount: 3},
  596. },
  597. },
  598. },
  599. {
  600. ObjectMeta: api.ObjectMeta{
  601. Name: "test2",
  602. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  603. },
  604. Spec: api.PodSpec{
  605. Containers: make([]api.Container, 2),
  606. NodeName: nodeName,
  607. },
  608. Status: api.PodStatus{
  609. Phase: api.PodRunning,
  610. ContainerStatuses: []api.ContainerStatus{
  611. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  612. {RestartCount: 3},
  613. },
  614. },
  615. },
  616. {
  617. ObjectMeta: api.ObjectMeta{
  618. Name: "test3",
  619. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  620. },
  621. Spec: api.PodSpec{
  622. Containers: make([]api.Container, 2),
  623. NodeName: nodeName,
  624. },
  625. Status: api.PodStatus{
  626. Phase: api.PodSucceeded,
  627. ContainerStatuses: []api.ContainerStatus{
  628. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  629. {RestartCount: 3},
  630. },
  631. },
  632. },
  633. {
  634. ObjectMeta: api.ObjectMeta{
  635. Name: "test4",
  636. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  637. },
  638. Spec: api.PodSpec{
  639. Containers: make([]api.Container, 2),
  640. NodeName: nodeName,
  641. },
  642. Status: api.PodStatus{
  643. Phase: api.PodFailed,
  644. ContainerStatuses: []api.ContainerStatus{
  645. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  646. {RestartCount: 3},
  647. },
  648. },
  649. },
  650. {
  651. ObjectMeta: api.ObjectMeta{
  652. Name: "test5",
  653. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  654. },
  655. Spec: api.PodSpec{
  656. Containers: make([]api.Container, 2),
  657. NodeName: nodeName,
  658. },
  659. Status: api.PodStatus{
  660. Phase: api.PodUnknown,
  661. ContainerStatuses: []api.ContainerStatus{
  662. {Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
  663. {RestartCount: 3},
  664. },
  665. },
  666. }},
  667. }
  668. }
  669. func Example_printPodHideTerminated() {
  670. f, tf, _, ns := NewAPIFactory()
  671. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  672. ColumnLabels: []string{},
  673. })
  674. tf.Client = &fake.RESTClient{
  675. NegotiatedSerializer: ns,
  676. Client: nil,
  677. }
  678. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  679. podList := newAllPhasePodList()
  680. mapper, _ := f.Object(false)
  681. err := f.PrintObject(cmd, mapper, podList, os.Stdout)
  682. if err != nil {
  683. fmt.Printf("Unexpected error: %v", err)
  684. }
  685. // Output:
  686. // NAME READY STATUS RESTARTS AGE
  687. // test1 1/2 Pending 6 10y
  688. // test2 1/2 Running 6 10y
  689. // test5 1/2 Unknown 6 10y
  690. }
  691. func Example_printPodShowAll() {
  692. f, tf, _, ns := NewAPIFactory()
  693. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  694. ShowAll: true,
  695. ColumnLabels: []string{},
  696. })
  697. tf.Client = &fake.RESTClient{
  698. NegotiatedSerializer: ns,
  699. Client: nil,
  700. }
  701. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  702. podList := newAllPhasePodList()
  703. mapper, _ := f.Object(false)
  704. err := f.PrintObject(cmd, mapper, podList, os.Stdout)
  705. if err != nil {
  706. fmt.Printf("Unexpected error: %v", err)
  707. }
  708. // Output:
  709. // NAME READY STATUS RESTARTS AGE
  710. // test1 1/2 Pending 6 10y
  711. // test2 1/2 Running 6 10y
  712. // test3 1/2 Succeeded 6 10y
  713. // test4 1/2 Failed 6 10y
  714. // test5 1/2 Unknown 6 10y
  715. }
  716. func Example_printServiceWithNamespacesAndLabels() {
  717. f, tf, _, ns := NewAPIFactory()
  718. tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
  719. WithNamespace: true,
  720. ColumnLabels: []string{"l1"},
  721. })
  722. tf.Client = &fake.RESTClient{
  723. NegotiatedSerializer: ns,
  724. Client: nil,
  725. }
  726. cmd := NewCmdRun(f, os.Stdin, os.Stdout, os.Stderr)
  727. svc := &api.ServiceList{
  728. Items: []api.Service{
  729. {
  730. ObjectMeta: api.ObjectMeta{
  731. Name: "svc1",
  732. Namespace: "ns1",
  733. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  734. Labels: map[string]string{
  735. "l1": "value",
  736. },
  737. },
  738. Spec: api.ServiceSpec{
  739. Ports: []api.ServicePort{
  740. {Protocol: "UDP", Port: 53},
  741. {Protocol: "TCP", Port: 53},
  742. },
  743. Selector: map[string]string{
  744. "s": "magic",
  745. },
  746. ClusterIP: "10.1.1.1",
  747. },
  748. Status: api.ServiceStatus{},
  749. },
  750. {
  751. ObjectMeta: api.ObjectMeta{
  752. Name: "svc2",
  753. Namespace: "ns2",
  754. CreationTimestamp: unversioned.Time{Time: time.Now().AddDate(-10, 0, 0)},
  755. Labels: map[string]string{
  756. "l1": "dolla-bill-yall",
  757. },
  758. },
  759. Spec: api.ServiceSpec{
  760. Ports: []api.ServicePort{
  761. {Protocol: "TCP", Port: 80},
  762. {Protocol: "TCP", Port: 8080},
  763. },
  764. Selector: map[string]string{
  765. "s": "kazam",
  766. },
  767. ClusterIP: "10.1.1.2",
  768. },
  769. Status: api.ServiceStatus{},
  770. }},
  771. }
  772. ld := strings.NewLineDelimiter(os.Stdout, "|")
  773. defer ld.Flush()
  774. mapper, _ := f.Object(false)
  775. err := f.PrintObject(cmd, mapper, svc, ld)
  776. if err != nil {
  777. fmt.Printf("Unexpected error: %v", err)
  778. }
  779. // Output:
  780. // |NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE L1|
  781. // |ns1 svc1 10.1.1.1 <unknown> 53/UDP,53/TCP 10y value|
  782. // |ns2 svc2 10.1.1.2 <unknown> 80/TCP,8080/TCP 10y dolla-bill-yall|
  783. // ||
  784. }
  785. func TestNormalizationFuncGlobalExistence(t *testing.T) {
  786. // This test can be safely deleted when we will not support multiple flag formats
  787. root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
  788. if root.Parent() != nil {
  789. t.Fatal("We expect the root command to be returned")
  790. }
  791. if root.GlobalNormalizationFunc() == nil {
  792. t.Fatal("We expect that root command has a global normalization function")
  793. }
  794. if reflect.ValueOf(root.GlobalNormalizationFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
  795. t.Fatal("root command seems to have a wrong normalization function")
  796. }
  797. sub := root
  798. for sub.HasSubCommands() {
  799. sub = sub.Commands()[0]
  800. }
  801. // In case of failure of this test check this PR: spf13/cobra#110
  802. if reflect.ValueOf(sub.Flags().GetNormalizeFunc()).Pointer() != reflect.ValueOf(root.Flags().GetNormalizeFunc()).Pointer() {
  803. t.Fatal("child and root commands should have the same normalization functions")
  804. }
  805. }