get.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. "fmt"
  16. "io"
  17. "github.com/renstrom/dedent"
  18. "github.com/spf13/cobra"
  19. "k8s.io/kubernetes/pkg/api/meta"
  20. "k8s.io/kubernetes/pkg/kubectl"
  21. cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
  22. "k8s.io/kubernetes/pkg/kubectl/resource"
  23. "k8s.io/kubernetes/pkg/runtime"
  24. utilerrors "k8s.io/kubernetes/pkg/util/errors"
  25. "k8s.io/kubernetes/pkg/watch"
  26. )
  27. // GetOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
  28. // referencing the cmd.Flags()
  29. type GetOptions struct {
  30. Filenames []string
  31. Recursive bool
  32. Raw string
  33. }
  34. var (
  35. get_long = dedent.Dedent(`
  36. Display one or many resources.
  37. `) + valid_resources + dedent.Dedent(`
  38. This command will hide resources that have completed. For instance, pods that are in the Succeeded or Failed phases.
  39. You can see the full results for any resource by providing the '--show-all' flag.
  40. By specifying the output as 'template' and providing a Go template as the value
  41. of the --template flag, you can filter the attributes of the fetched resource(s).`)
  42. get_example = dedent.Dedent(`
  43. # List all pods in ps output format.
  44. kubectl get pods
  45. # List all pods in ps output format with more information (such as node name).
  46. kubectl get pods -o wide
  47. # List a single replication controller with specified NAME in ps output format.
  48. kubectl get replicationcontroller web
  49. # List a single pod in JSON output format.
  50. kubectl get -o json pod web-pod-13je7
  51. # List a pod identified by type and name specified in "pod.yaml" in JSON output format.
  52. kubectl get -f pod.yaml -o json
  53. # Return only the phase value of the specified pod.
  54. kubectl get -o template pod/web-pod-13je7 --template={{.status.phase}}
  55. # List all replication controllers and services together in ps output format.
  56. kubectl get rc,services
  57. # List one or more resources by their type and names.
  58. kubectl get rc/web service/frontend pods/web-pod-13je7`)
  59. )
  60. // NewCmdGet creates a command object for the generic "get" action, which
  61. // retrieves one or more resources from a server.
  62. func NewCmdGet(f *cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
  63. options := &GetOptions{}
  64. // retrieve a list of handled resources from printer as valid args
  65. validArgs, argAliases := []string{}, []string{}
  66. p, err := f.Printer(nil, kubectl.PrintOptions{
  67. ColumnLabels: []string{},
  68. })
  69. cmdutil.CheckErr(err)
  70. if p != nil {
  71. validArgs = p.HandledResources()
  72. argAliases = kubectl.ResourceAliases(validArgs)
  73. }
  74. cmd := &cobra.Command{
  75. Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags]",
  76. Short: "Display one or many resources",
  77. Long: get_long,
  78. Example: get_example,
  79. Run: func(cmd *cobra.Command, args []string) {
  80. err := RunGet(f, out, errOut, cmd, args, options)
  81. cmdutil.CheckErr(err)
  82. },
  83. SuggestFor: []string{"list", "ps"},
  84. ValidArgs: validArgs,
  85. ArgAliases: argAliases,
  86. }
  87. cmdutil.AddPrinterFlags(cmd)
  88. cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
  89. cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
  90. cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.")
  91. cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).")
  92. cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
  93. cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
  94. cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
  95. usage := "Filename, directory, or URL to a file identifying the resource to get from a server."
  96. kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
  97. cmdutil.AddRecursiveFlag(cmd, &options.Recursive)
  98. cmdutil.AddInclude3rdPartyFlags(cmd)
  99. cmd.Flags().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
  100. return cmd
  101. }
  102. // RunGet implements the generic Get command
  103. // TODO: convert all direct flag accessors to a struct and pass that instead of cmd
  104. func RunGet(f *cmdutil.Factory, out io.Writer, errOut io.Writer, cmd *cobra.Command, args []string, options *GetOptions) error {
  105. if len(options.Raw) > 0 {
  106. client, err := f.Client()
  107. if err != nil {
  108. return err
  109. }
  110. stream, err := client.RESTClient.Get().RequestURI(options.Raw).Stream()
  111. if err != nil {
  112. return err
  113. }
  114. defer stream.Close()
  115. for {
  116. buffer := make([]byte, 1024, 1024)
  117. bytesRead, err := stream.Read(buffer)
  118. if bytesRead > 0 {
  119. fmt.Printf("%s", string(buffer[:bytesRead]))
  120. }
  121. if err == io.EOF {
  122. return nil
  123. }
  124. if err != nil {
  125. return err
  126. }
  127. }
  128. }
  129. selector := cmdutil.GetFlagString(cmd, "selector")
  130. allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces")
  131. showKind := cmdutil.GetFlagBool(cmd, "show-kind")
  132. mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd))
  133. cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
  134. if err != nil {
  135. return err
  136. }
  137. if allNamespaces {
  138. enforceNamespace = false
  139. }
  140. if len(args) == 0 && len(options.Filenames) == 0 {
  141. fmt.Fprint(errOut, "You must specify the type of resource to get. ", valid_resources)
  142. return cmdutil.UsageError(cmd, "Required resource not specified.")
  143. }
  144. // always show resources when getting by name or filename
  145. argsHasNames, err := resource.HasNames(args)
  146. if err != nil {
  147. return err
  148. }
  149. if len(options.Filenames) > 0 || argsHasNames {
  150. cmd.Flag("show-all").Value.Set("true")
  151. }
  152. export := cmdutil.GetFlagBool(cmd, "export")
  153. // handle watch separately since we cannot watch multiple resource types
  154. isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only")
  155. if isWatch || isWatchOnly {
  156. r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
  157. NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
  158. FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
  159. SelectorParam(selector).
  160. ExportParam(export).
  161. ResourceTypeOrNameArgs(true, args...).
  162. SingleResourceType().
  163. Latest().
  164. Do()
  165. err := r.Err()
  166. if err != nil {
  167. return err
  168. }
  169. infos, err := r.Infos()
  170. if err != nil {
  171. return err
  172. }
  173. if len(infos) != 1 {
  174. return fmt.Errorf("watch is only supported on individual resources and resource collections - %d resources were found", len(infos))
  175. }
  176. info := infos[0]
  177. mapping := info.ResourceMapping()
  178. printer, err := f.PrinterForMapping(cmd, mapping, allNamespaces)
  179. if err != nil {
  180. return err
  181. }
  182. obj, err := r.Object()
  183. if err != nil {
  184. return err
  185. }
  186. // watching from resourceVersion 0, starts the watch at ~now and
  187. // will return an initial watch event. Starting form ~now, rather
  188. // the rv of the object will insure that we start the watch from
  189. // inside the watch window, which the rv of the object might not be.
  190. rv := "0"
  191. isList := meta.IsListType(obj)
  192. if isList {
  193. // the resourceVersion of list objects is ~now but won't return
  194. // an initial watch event
  195. rv, err = mapping.MetadataAccessor.ResourceVersion(obj)
  196. if err != nil {
  197. return err
  198. }
  199. }
  200. // print the current object
  201. if !isWatchOnly {
  202. if err := printer.PrintObj(obj, out); err != nil {
  203. return fmt.Errorf("unable to output the provided object: %v", err)
  204. }
  205. printer.FinishPrint(errOut, mapping.Resource)
  206. }
  207. // print watched changes
  208. w, err := r.Watch(rv)
  209. if err != nil {
  210. return err
  211. }
  212. first := true
  213. kubectl.WatchLoop(w, func(e watch.Event) error {
  214. if !isList && first {
  215. // drop the initial watch event in the single resource case
  216. first = false
  217. return nil
  218. }
  219. err := printer.PrintObj(e.Object, out)
  220. if err == nil {
  221. printer.FinishPrint(errOut, mapping.Resource)
  222. }
  223. return err
  224. })
  225. return nil
  226. }
  227. r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
  228. NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
  229. FilenameParam(enforceNamespace, options.Recursive, options.Filenames...).
  230. SelectorParam(selector).
  231. ExportParam(export).
  232. ResourceTypeOrNameArgs(true, args...).
  233. ContinueOnError().
  234. Latest().
  235. Flatten().
  236. Do()
  237. err = r.Err()
  238. if err != nil {
  239. return err
  240. }
  241. printer, generic, err := cmdutil.PrinterForCommand(cmd)
  242. if err != nil {
  243. return err
  244. }
  245. if generic {
  246. clientConfig, err := f.ClientConfig()
  247. if err != nil {
  248. return err
  249. }
  250. allErrs := []error{}
  251. singular := false
  252. infos, err := r.IntoSingular(&singular).Infos()
  253. if err != nil {
  254. if singular {
  255. return err
  256. }
  257. allErrs = append(allErrs, err)
  258. }
  259. // the outermost object will be converted to the output-version, but inner
  260. // objects can use their mappings
  261. version, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
  262. if err != nil {
  263. return err
  264. }
  265. res := ""
  266. if len(infos) > 0 {
  267. res = infos[0].ResourceMapping().Resource
  268. }
  269. obj, err := resource.AsVersionedObject(infos, !singular, version, f.JSONEncoder())
  270. if err != nil {
  271. return err
  272. }
  273. if err := printer.PrintObj(obj, out); err != nil {
  274. allErrs = append(allErrs, err)
  275. }
  276. printer.FinishPrint(errOut, res)
  277. return utilerrors.NewAggregate(allErrs)
  278. }
  279. allErrs := []error{}
  280. infos, err := r.Infos()
  281. if err != nil {
  282. allErrs = append(allErrs, err)
  283. }
  284. objs := make([]runtime.Object, len(infos))
  285. for ix := range infos {
  286. objs[ix] = infos[ix].Object
  287. }
  288. sorting, err := cmd.Flags().GetString("sort-by")
  289. if err != nil {
  290. return err
  291. }
  292. var sorter *kubectl.RuntimeSort
  293. if len(sorting) > 0 && len(objs) > 1 {
  294. clientConfig, err := f.ClientConfig()
  295. if err != nil {
  296. return err
  297. }
  298. version, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
  299. if err != nil {
  300. return err
  301. }
  302. for ix := range infos {
  303. objs[ix], err = infos[ix].Mapping.ConvertToVersion(infos[ix].Object, version)
  304. if err != nil {
  305. allErrs = append(allErrs, err)
  306. continue
  307. }
  308. }
  309. // TODO: questionable
  310. if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil {
  311. return err
  312. }
  313. }
  314. // use the default printer for each object
  315. printer = nil
  316. var lastMapping *meta.RESTMapping
  317. w := kubectl.GetNewTabWriter(out)
  318. if mustPrintWithKinds(objs, infos, sorter) {
  319. showKind = true
  320. }
  321. for ix := range objs {
  322. var mapping *meta.RESTMapping
  323. var original runtime.Object
  324. if sorter != nil {
  325. mapping = infos[sorter.OriginalPosition(ix)].Mapping
  326. original = infos[sorter.OriginalPosition(ix)].Object
  327. } else {
  328. mapping = infos[ix].Mapping
  329. original = infos[ix].Object
  330. }
  331. if printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource {
  332. if printer != nil {
  333. w.Flush()
  334. printer.FinishPrint(errOut, lastMapping.Resource)
  335. }
  336. printer, err = f.PrinterForMapping(cmd, mapping, allNamespaces)
  337. if err != nil {
  338. allErrs = append(allErrs, err)
  339. continue
  340. }
  341. lastMapping = mapping
  342. }
  343. if resourcePrinter, found := printer.(*kubectl.HumanReadablePrinter); found {
  344. resourceName := resourcePrinter.GetResourceKind()
  345. if mapping != nil {
  346. if resourceName == "" {
  347. resourceName = mapping.Resource
  348. }
  349. if alias, ok := kubectl.ResourceShortFormFor(mapping.Resource); ok {
  350. resourceName = alias
  351. } else if resourceName == "" {
  352. resourceName = "none"
  353. }
  354. } else {
  355. resourceName = "none"
  356. }
  357. if showKind {
  358. resourcePrinter.EnsurePrintWithKind(resourceName)
  359. }
  360. if err := printer.PrintObj(original, w); err != nil {
  361. allErrs = append(allErrs, err)
  362. }
  363. continue
  364. }
  365. if err := printer.PrintObj(original, w); err != nil {
  366. allErrs = append(allErrs, err)
  367. continue
  368. }
  369. }
  370. w.Flush()
  371. if printer != nil {
  372. printer.FinishPrint(errOut, lastMapping.Resource)
  373. }
  374. return utilerrors.NewAggregate(allErrs)
  375. }
  376. // mustPrintWithKinds determines if printer is dealing
  377. // with multiple resource kinds, in which case it will
  378. // return true, indicating resource kind will be
  379. // included as part of printer output
  380. func mustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *kubectl.RuntimeSort) bool {
  381. var lastMap *meta.RESTMapping
  382. for ix := range objs {
  383. var mapping *meta.RESTMapping
  384. if sorter != nil {
  385. mapping = infos[sorter.OriginalPosition(ix)].Mapping
  386. } else {
  387. mapping = infos[ix].Mapping
  388. }
  389. // display "kind" only if we have mixed resources
  390. if lastMap != nil && mapping.Resource != lastMap.Resource {
  391. return true
  392. }
  393. lastMap = mapping
  394. }
  395. return false
  396. }