diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 6cd0c9ea4c9..36c7ff5803e 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -229,6 +229,7 @@ _kubectl_get() flags_with_completion=() flags_completion=() + flags+=("--all-namespaces") flags+=("--help") flags+=("-h") flags+=("--no-headers") diff --git a/docs/kubectl.md b/docs/kubectl.md index 047f422548a..02412911d28 100644 --- a/docs/kubectl.md +++ b/docs/kubectl.md @@ -65,6 +65,6 @@ kubectl * [kubectl update](kubectl_update.md) - Update a resource by filename or stdin. * [kubectl version](kubectl_version.md) - Print the client and server version information. -###### Auto generated by spf13/cobra at 2015-05-15 00:05:04.556347262 +0000 UTC +###### Auto generated by spf13/cobra at 2015-05-22 14:24:30.1784975 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl.md?pixel)]() diff --git a/docs/kubectl_get.md b/docs/kubectl_get.md index 069835871c3..9e94863c178 100644 --- a/docs/kubectl_get.md +++ b/docs/kubectl_get.md @@ -42,6 +42,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 ### Options ``` + --all-namespaces=false: If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace. -h, --help=false: help for get --no-headers=false: When using the default output, don't print headers. -o, --output="": Output format. One of: json|yaml|template|templatefile. @@ -84,6 +85,6 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7 ### SEE ALSO * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra at 2015-05-20 23:52:21.968486735 +0000 UTC +###### Auto generated by spf13/cobra at 2015-05-22 14:24:30.17132893 +0000 UTC [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/kubectl_get.md?pixel)]() diff --git a/docs/man/man1/kubectl-get.1 b/docs/man/man1/kubectl-get.1 index 6c8d6100670..3644ab9a061 100644 --- a/docs/man/man1/kubectl-get.1 +++ b/docs/man/man1/kubectl-get.1 @@ -25,6 +25,10 @@ of the \-\-template flag, you can filter the attributes of the fetched resource( .SH OPTIONS +.PP +\fB\-\-all\-namespaces\fP=false + If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with \-\-namespace. + .PP \fB\-h\fP, \fB\-\-help\fP=false help for get diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index a4e5e2d9e57..777a39d673e 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -138,7 +138,7 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, - Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func() (validation.Schema, error) { @@ -192,7 +192,7 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { return t.Describer, t.Err }, - Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) { + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) { return t.Printer, t.Err }, Validator: func() (validation.Schema, error) { @@ -239,7 +239,7 @@ func TestClientVersions(t *testing.T) { func ExamplePrintReplicationController() { f, tf, codec := NewAPIFactory() - tf.Printer = kubectl.NewHumanReadablePrinter(false) + tf.Printer = kubectl.NewHumanReadablePrinter(false, false) tf.Client = &client.FakeRESTClient{ Codec: codec, Client: nil, diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index bcf854757fe..d09a90c6abd 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -58,7 +58,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7` // NewCmdGet creates a command object for the generic "get" action, which // retrieves one or more resources from a server. func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { - p := kubectl.NewHumanReadablePrinter(false) + p := kubectl.NewHumanReadablePrinter(false, false) validArgs := p.HandledResources() cmd := &cobra.Command{ @@ -76,6 +76,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") + 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.") return cmd } @@ -83,6 +84,7 @@ func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command { // TODO: convert all direct flag accessors to a struct and pass that instead of cmd func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error { selector := cmdutil.GetFlagString(cmd, "selector") + allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces") mapper, typer := f.Object() cmdNamespace, err := f.DefaultNamespace() @@ -94,7 +96,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") if isWatch || isWatchOnly { r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). - NamespaceParam(cmdNamespace).DefaultNamespace(). + NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). SingleResourceType(). @@ -108,7 +110,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string return err } - printer, err := f.PrinterForMapping(cmd, mapping) + printer, err := f.PrinterForMapping(cmd, mapping, allNamespaces) if err != nil { return err } @@ -143,7 +145,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string } b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). - NamespaceParam(cmdNamespace).DefaultNamespace(). + NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). SelectorParam(selector). ResourceTypeOrNameArgs(true, args...). ContinueOnError(). @@ -180,7 +182,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string // use the default printer for each object return b.Do().Visit(func(r *resource.Info) error { - printer, err := f.PrinterForMapping(cmd, r.Mapping) + printer, err := f.PrinterForMapping(cmd, r.Mapping, allNamespaces) if err != nil { return err } diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 5cbfd49348c..44648c39354 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -220,7 +220,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri return err } - printer, err := f.PrinterForMapping(cmd, info.Mapping) + printer, err := f.PrinterForMapping(cmd, info.Mapping, false) if err != nil { return err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index dc89d7625fc..c92b57a885b 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -64,7 +64,7 @@ type Factory struct { // Returns a Describer for displaying the specified RESTMapping type or an error. Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error) // Returns a Printer for formatting objects of the given type or an error. - Printer func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) + Printer func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) // Returns a Resizer for changing the size of the specified RESTMapping type or an error Resizer func(mapping *meta.RESTMapping) (kubectl.Resizer, error) // Returns a Reaper for gracefully shutting down resources. @@ -143,8 +143,8 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return describer, nil }, - Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) { - return kubectl.NewHumanReadablePrinter(noHeaders), nil + Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool) (kubectl.ResourcePrinter, error) { + return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace), nil }, PodSelectorForObject: func(object runtime.Object) (string, error) { // TODO: replace with a swagger schema based approach (identify pod selector via schema introspection) @@ -343,7 +343,7 @@ func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Wri return err } - printer, err := f.PrinterForMapping(cmd, mapping) + printer, err := f.PrinterForMapping(cmd, mapping, false) if err != nil { return err } @@ -352,7 +352,7 @@ func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Wri // PrinterForMapping returns a printer suitable for displaying the provided resource type. // Requires that printer flags have been added to cmd (see AddPrinterFlags). -func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) { +func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) { printer, ok, err := PrinterForCommand(cmd) if err != nil { return nil, err @@ -373,7 +373,7 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin } printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.APIVersion) } else { - printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers")) + printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"), withNamespace) if err != nil { return nil, err } diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index e8504534dd3..c30eb8e1b79 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -23,6 +23,7 @@ import ( "os" "strings" + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" @@ -220,6 +221,15 @@ func (b *Builder) DefaultNamespace() *Builder { return b } +// AllNamespaces instructs the builder to use NamespaceAll as a namespace to request resources +// acroll all namespace. This overrides the namespace set by NamespaceParam(). +func (b *Builder) AllNamespaces(allNamespace bool) *Builder { + if allNamespace { + b.namespace = api.NamespaceAll + } + return b +} + // RequireNamespace instructs the builder to set the namespace value for any object found // to NamespaceParam() if empty, and if the value on the resource does not match // NamespaceParam() an error will be returned. diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index fec5fcf5433..b2acf1ff8ec 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -32,6 +32,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" + "github.com/GoogleCloudPlatform/kubernetes/pkg/types" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/volume" "github.com/docker/docker/pkg/units" @@ -177,16 +178,18 @@ type handlerEntry struct { // will only be printed if the object type changes. This makes it useful for printing items // recieved from watches. type HumanReadablePrinter struct { - handlerMap map[reflect.Type]*handlerEntry - noHeaders bool - lastType reflect.Type + handlerMap map[reflect.Type]*handlerEntry + noHeaders bool + withNamespace bool + lastType reflect.Type } // NewHumanReadablePrinter creates a HumanReadablePrinter. -func NewHumanReadablePrinter(noHeaders bool) *HumanReadablePrinter { +func NewHumanReadablePrinter(noHeaders, withNamespace bool) *HumanReadablePrinter { printer := &HumanReadablePrinter{ - handlerMap: make(map[reflect.Type]*handlerEntry), - noHeaders: noHeaders, + handlerMap: make(map[reflect.Type]*handlerEntry), + noHeaders: noHeaders, + withNamespace: withNamespace, } printer.addDefaultHandlers() return printer @@ -195,7 +198,7 @@ func NewHumanReadablePrinter(noHeaders bool) *HumanReadablePrinter { // Handler adds a print handler with a given set of columns to HumanReadablePrinter instance. // printFunc is the function that will be called to print an object. // It must be of the following type: -// func printFunc(object ObjectType, w io.Writer) error +// func printFunc(object ObjectType, w io.Writer, withNamespace bool) error // where ObjectType is the type of the object that will be printed. func (h *HumanReadablePrinter) Handler(columns []string, printFunc interface{}) error { printFuncValue := reflect.ValueOf(printFunc) @@ -216,14 +219,14 @@ func (h *HumanReadablePrinter) validatePrintHandlerFunc(printFunc reflect.Value) return fmt.Errorf("invalid print handler. %#v is not a function.", printFunc) } funcType := printFunc.Type() - if funcType.NumIn() != 2 || funcType.NumOut() != 1 { + if funcType.NumIn() != 3 || funcType.NumOut() != 1 { return fmt.Errorf("invalid print handler." + - "Must accept 2 parameters and return 1 value.") + "Must accept 3 parameters and return 1 value.") } if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() || funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() { return fmt.Errorf("invalid print handler. The expected signature is: "+ - "func handler(obj %v, w io.Writer) error", funcType.In(0)) + "func handler(obj %v, w io.Writer, withNamespace bool) error", funcType.In(0)) } return nil } @@ -391,9 +394,16 @@ func interpretContainerStatus(status *api.ContainerStatus) (string, string, stri return "", "", "", fmt.Errorf("unknown container state %#v", *state) } -func printPod(pod *api.Pod, w io.Writer) error { +func printPod(pod *api.Pod, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{pod.Namespace, pod.Name}.String() + } else { + name = pod.Name + } + _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", - pod.Name, + name, pod.Status.PodIP, "", "", podHostString(pod.Spec.Host, pod.Status.HostIP), @@ -411,8 +421,8 @@ func printPod(pod *api.Pod, w io.Writer) error { // Container status has not been reported yet. Print basic information // of the containers and exit the function. for _, container := range pod.Spec.Containers { - _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", - "", "", container.Name, container.Image, "", "", "", "") + _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + "", "", container.Name, container.Image, "", "", "", "", "") if err != nil { return err } @@ -442,23 +452,30 @@ func printPod(pod *api.Pod, w io.Writer) error { return nil } -func printPodList(podList *api.PodList, w io.Writer) error { +func printPodList(podList *api.PodList, w io.Writer, withNamespace bool) error { for _, pod := range podList.Items { - if err := printPod(&pod, w); err != nil { + if err := printPod(&pod, w, withNamespace); err != nil { return err } } return nil } -func printPodTemplate(pod *api.PodTemplate, w io.Writer) error { +func printPodTemplate(pod *api.PodTemplate, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{pod.Namespace, pod.Name}.String() + } else { + name = pod.Name + } + containers := pod.Template.Spec.Containers var firstContainer api.Container if len(containers) > 0 { firstContainer, containers = containers[0], containers[1:] } _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", - pod.Name, + name, firstContainer.Name, firstContainer.Image, formatLabels(pod.Template.Labels), @@ -476,23 +493,30 @@ func printPodTemplate(pod *api.PodTemplate, w io.Writer) error { return nil } -func printPodTemplateList(podList *api.PodTemplateList, w io.Writer) error { +func printPodTemplateList(podList *api.PodTemplateList, w io.Writer, withNamespace bool) error { for _, pod := range podList.Items { - if err := printPodTemplate(&pod, w); err != nil { + if err := printPodTemplate(&pod, w, withNamespace); err != nil { return err } } return nil } -func printReplicationController(controller *api.ReplicationController, w io.Writer) error { +func printReplicationController(controller *api.ReplicationController, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{controller.Namespace, controller.Name}.String() + } else { + name = controller.Name + } + containers := controller.Spec.Template.Spec.Containers var firstContainer api.Container if len(containers) > 0 { firstContainer, containers = containers[0], containers[1:] } _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d\n", - controller.Name, + name, firstContainer.Name, firstContainer.Image, formatLabels(controller.Spec.Selector), @@ -510,21 +534,29 @@ func printReplicationController(controller *api.ReplicationController, w io.Writ return nil } -func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer) error { +func printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer, withNamespace bool) error { for _, controller := range list.Items { - if err := printReplicationController(&controller, w); err != nil { + if err := printReplicationController(&controller, w, withNamespace); err != nil { return err } } return nil } -func printService(svc *api.Service, w io.Writer) error { +func printService(svc *api.Service, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{svc.Namespace, svc.Name}.String() + } else { + name = svc.Name + } + ips := []string{svc.Spec.PortalIP} for _, publicIP := range svc.Spec.PublicIPs { ips = append(ips, publicIP) } - if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d/%s\n", svc.Name, formatLabels(svc.Labels), + + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%d/%s\n", name, formatLabels(svc.Labels), formatLabels(svc.Spec.Selector), ips[0], svc.Spec.Ports[0].Port, svc.Spec.Ports[0].Protocol); err != nil { return err } @@ -551,51 +583,64 @@ func printService(svc *api.Service, w io.Writer) error { return nil } -func printServiceList(list *api.ServiceList, w io.Writer) error { +func printServiceList(list *api.ServiceList, w io.Writer, withNamespace bool) error { for _, svc := range list.Items { - if err := printService(&svc, w); err != nil { + if err := printService(&svc, w, withNamespace); err != nil { return err } } return nil } -func printEndpoints(endpoints *api.Endpoints, w io.Writer) error { - _, err := fmt.Fprintf(w, "%s\t%s\n", endpoints.Name, formatEndpoints(endpoints, nil)) +func printEndpoints(endpoints *api.Endpoints, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{endpoints.Namespace, endpoints.Name}.String() + } else { + name = endpoints.Name + } + _, err := fmt.Fprintf(w, "%s\t%s\n", name, formatEndpoints(endpoints, nil)) return err } -func printEndpointsList(list *api.EndpointsList, w io.Writer) error { +func printEndpointsList(list *api.EndpointsList, w io.Writer, withNamespace bool) error { for _, item := range list.Items { - if err := printEndpoints(&item, w); err != nil { + if err := printEndpoints(&item, w, withNamespace); err != nil { return err } } return nil } -func printNamespace(item *api.Namespace, w io.Writer) error { +func printNamespace(item *api.Namespace, w io.Writer, withNamespace bool) error { _, err := fmt.Fprintf(w, "%s\t%s\t%s\n", item.Name, formatLabels(item.Labels), item.Status.Phase) return err } -func printNamespaceList(list *api.NamespaceList, w io.Writer) error { +func printNamespaceList(list *api.NamespaceList, w io.Writer, withNamespace bool) error { for _, item := range list.Items { - if err := printNamespace(&item, w); err != nil { + if err := printNamespace(&item, w, withNamespace); err != nil { return err } } return nil } -func printSecret(item *api.Secret, w io.Writer) error { - _, err := fmt.Fprintf(w, "%s\t%s\t%v\n", item.Name, item.Type, len(item.Data)) +func printSecret(item *api.Secret, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{item.Namespace, item.Name}.String() + } else { + name = item.Name + } + + _, err := fmt.Fprintf(w, "%s\t%s\t%v\n", name, item.Type, len(item.Data)) return err } -func printSecretList(list *api.SecretList, w io.Writer) error { +func printSecretList(list *api.SecretList, w io.Writer, withNamespace bool) error { for _, item := range list.Items { - if err := printSecret(&item, w); err != nil { + if err := printSecret(&item, w, withNamespace); err != nil { return err } } @@ -603,14 +648,21 @@ func printSecretList(list *api.SecretList, w io.Writer) error { return nil } -func printServiceAccount(item *api.ServiceAccount, w io.Writer) error { - _, err := fmt.Fprintf(w, "%s\t%d\n", item.Name, len(item.Secrets)) +func printServiceAccount(item *api.ServiceAccount, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{item.Namespace, item.Name}.String() + } else { + name = item.Name + } + + _, err := fmt.Fprintf(w, "%s\t%d\n", name, len(item.Secrets)) return err } -func printServiceAccountList(list *api.ServiceAccountList, w io.Writer) error { +func printServiceAccountList(list *api.ServiceAccountList, w io.Writer, withNamespace bool) error { for _, item := range list.Items { - if err := printServiceAccount(&item, w); err != nil { + if err := printServiceAccount(&item, w, withNamespace); err != nil { return err } } @@ -618,7 +670,7 @@ func printServiceAccountList(list *api.ServiceAccountList, w io.Writer) error { return nil } -func printNode(node *api.Node, w io.Writer) error { +func printNode(node *api.Node, w io.Writer, withNamespace bool) error { conditionMap := make(map[api.NodeConditionType]*api.NodeCondition) NodeAllConditions := []api.NodeConditionType{api.NodeReady} for i := range node.Status.Conditions { @@ -645,16 +697,23 @@ func printNode(node *api.Node, w io.Writer) error { return err } -func printNodeList(list *api.NodeList, w io.Writer) error { +func printNodeList(list *api.NodeList, w io.Writer, withNamespace bool) error { for _, node := range list.Items { - if err := printNode(&node, w); err != nil { + if err := printNode(&node, w, withNamespace); err != nil { return err } } return nil } -func printPersistentVolume(pv *api.PersistentVolume, w io.Writer) error { +func printPersistentVolume(pv *api.PersistentVolume, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{pv.Namespace, pv.Name}.String() + } else { + name = pv.Name + } + claimRefUID := "" if pv.Spec.ClaimRef != nil { claimRefUID += pv.Spec.ClaimRef.Namespace @@ -667,34 +726,41 @@ func printPersistentVolume(pv *api.PersistentVolume, w io.Writer) error { aQty := pv.Spec.Capacity[api.ResourceStorage] aSize := aQty.Value() - _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\n", pv.Name, pv.Labels, aSize, modesStr, pv.Status.Phase, claimRefUID) + _, err := fmt.Fprintf(w, "%s\t%s\t%d\t%s\t%s\t%s\n", name, formatLabels(pv.Labels), aSize, modesStr, pv.Status.Phase, claimRefUID) return err } -func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer) error { +func printPersistentVolumeList(list *api.PersistentVolumeList, w io.Writer, withNamespace bool) error { for _, pv := range list.Items { - if err := printPersistentVolume(&pv, w); err != nil { + if err := printPersistentVolume(&pv, w, withNamespace); err != nil { return err } } return nil } -func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer) error { - _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", pvc.Name, pvc.Labels, pvc.Status.Phase, pvc.Spec.VolumeName) +func printPersistentVolumeClaim(pvc *api.PersistentVolumeClaim, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{pvc.Namespace, pvc.Name}.String() + } else { + name = pvc.Name + } + + _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, pvc.Labels, pvc.Status.Phase, pvc.Spec.VolumeName) return err } -func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer) error { +func printPersistentVolumeClaimList(list *api.PersistentVolumeClaimList, w io.Writer, withNamespace bool) error { for _, psd := range list.Items { - if err := printPersistentVolumeClaim(&psd, w); err != nil { + if err := printPersistentVolumeClaim(&psd, w, withNamespace); err != nil { return err } } return nil } -func printEvent(event *api.Event, w io.Writer) error { +func printEvent(event *api.Event, w io.Writer, withNamespace bool) error { _, err := fmt.Fprintf( w, "%s\t%s\t%d\t%s\t%s\t%s\t%s\t%s\t%s\n", event.FirstTimestamp.Time.Format(time.RFC1123Z), @@ -711,53 +777,67 @@ func printEvent(event *api.Event, w io.Writer) error { } // Sorts and prints the EventList in a human-friendly format. -func printEventList(list *api.EventList, w io.Writer) error { +func printEventList(list *api.EventList, w io.Writer, withNamespace bool) error { sort.Sort(SortableEvents(list.Items)) for i := range list.Items { - if err := printEvent(&list.Items[i], w); err != nil { + if err := printEvent(&list.Items[i], w, withNamespace); err != nil { return err } } return nil } -func printLimitRange(limitRange *api.LimitRange, w io.Writer) error { +func printLimitRange(limitRange *api.LimitRange, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{limitRange.Namespace, limitRange.Name}.String() + } else { + name = limitRange.Name + } + _, err := fmt.Fprintf( w, "%s\n", - limitRange.Name, + name, ) return err } // Prints the LimitRangeList in a human-friendly format. -func printLimitRangeList(list *api.LimitRangeList, w io.Writer) error { +func printLimitRangeList(list *api.LimitRangeList, w io.Writer, withNamespace bool) error { for i := range list.Items { - if err := printLimitRange(&list.Items[i], w); err != nil { + if err := printLimitRange(&list.Items[i], w, withNamespace); err != nil { return err } } return nil } -func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer) error { +func printResourceQuota(resourceQuota *api.ResourceQuota, w io.Writer, withNamespace bool) error { + var name string + if withNamespace { + name = types.NamespacedName{resourceQuota.Namespace, resourceQuota.Name}.String() + } else { + name = resourceQuota.Name + } + _, err := fmt.Fprintf( w, "%s\n", - resourceQuota.Name, + name, ) return err } // Prints the ResourceQuotaList in a human-friendly format. -func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer) error { +func printResourceQuotaList(list *api.ResourceQuotaList, w io.Writer, withNamespace bool) error { for i := range list.Items { - if err := printResourceQuota(&list.Items[i], w); err != nil { + if err := printResourceQuota(&list.Items[i], w, withNamespace); err != nil { return err } } return nil } -func printComponentStatus(item *api.ComponentStatus, w io.Writer) error { +func printComponentStatus(item *api.ComponentStatus, w io.Writer, withNamespace bool) error { status := "Unknown" message := "" error := "" @@ -777,9 +857,9 @@ func printComponentStatus(item *api.ComponentStatus, w io.Writer) error { return err } -func printComponentStatusList(list *api.ComponentStatusList, w io.Writer) error { +func printComponentStatusList(list *api.ComponentStatusList, w io.Writer, withNamespace bool) error { for _, item := range list.Items { - if err := printComponentStatus(&item, w); err != nil { + if err := printComponentStatus(&item, w, withNamespace); err != nil { return err } } @@ -797,7 +877,7 @@ func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) er h.printHeader(handler.columns, w) h.lastType = t } - args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w)} + args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.withNamespace)} resultValue := handler.printFunc.Call(args)[0] if resultValue.IsNil() { return nil diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index dd7d56a2740..7f520576cc8 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -237,18 +237,18 @@ type TestUnknownType struct{} func (*TestUnknownType) IsAnAPIObject() {} -func PrintCustomType(obj *TestPrintType, w io.Writer) error { +func PrintCustomType(obj *TestPrintType, w io.Writer, withNamespace bool) error { _, err := fmt.Fprintf(w, "%s", obj.Data) return err } -func ErrorPrintHandler(obj *TestPrintType, w io.Writer) error { +func ErrorPrintHandler(obj *TestPrintType, w io.Writer, withNamespace bool) error { return fmt.Errorf("ErrorPrintHandler error") } func TestCustomTypePrinting(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false) + printer := NewHumanReadablePrinter(false, false) printer.Handler(columns, PrintCustomType) obj := TestPrintType{"test object"} @@ -265,7 +265,7 @@ func TestCustomTypePrinting(t *testing.T) { func TestPrintHandlerError(t *testing.T) { columns := []string{"Data"} - printer := NewHumanReadablePrinter(false) + printer := NewHumanReadablePrinter(false, false) printer.Handler(columns, ErrorPrintHandler) obj := TestPrintType{"test object"} buffer := &bytes.Buffer{} @@ -276,7 +276,7 @@ func TestPrintHandlerError(t *testing.T) { } func TestUnknownTypePrinting(t *testing.T) { - printer := NewHumanReadablePrinter(false) + printer := NewHumanReadablePrinter(false, false) buffer := &bytes.Buffer{} err := printer.PrintObj(&TestUnknownType{}, buffer) if err == nil { @@ -465,8 +465,8 @@ func TestPrinters(t *testing.T) { t.Fatal(err) } printers := map[string]ResourcePrinter{ - "humanReadable": NewHumanReadablePrinter(true), - "humanReadableHeaders": NewHumanReadablePrinter(false), + "humanReadable": NewHumanReadablePrinter(true, false), + "humanReadableHeaders": NewHumanReadablePrinter(false, false), "json": &JSONPrinter{}, "yaml": &YAMLPrinter{}, "template": templatePrinter, @@ -503,7 +503,7 @@ func TestPrinters(t *testing.T) { func TestPrintEventsResultSorted(t *testing.T) { // Arrange - printer := NewHumanReadablePrinter(false /* noHeaders */) + printer := NewHumanReadablePrinter(false /* noHeaders */, false) obj := api.EventList{ Items: []api.Event{ @@ -544,7 +544,7 @@ func TestPrintEventsResultSorted(t *testing.T) { } func TestPrintMinionStatus(t *testing.T) { - printer := NewHumanReadablePrinter(false) + printer := NewHumanReadablePrinter(false, false) table := []struct { minion api.Node status string @@ -727,7 +727,7 @@ func TestPrintHumanReadableService(t *testing.T) { for _, svc := range tests { buff := bytes.Buffer{} - printService(&svc, &buff) + printService(&svc, &buff, false) output := string(buff.Bytes()) ip := svc.Spec.PortalIP if !strings.Contains(output, ip) { @@ -855,6 +855,179 @@ func TestInterpretContainerStatus(t *testing.T) { if msg != test.expectedMessage { t.Errorf("expected: %s, got: %s", test.expectedMessage, msg) } - + } +} + +func TestPrintHumanReadableWithNamespace(t *testing.T) { + namespaceName := "testnamespace" + name := "test" + table := []struct { + obj runtime.Object + printNamespace bool + }{ + { + obj: &api.Pod{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + }, + printNamespace: true, + }, + { + obj: &api.ReplicationController{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Spec: api.ReplicationControllerSpec{ + Replicas: 2, + Template: &api.PodTemplateSpec{ + ObjectMeta: api.ObjectMeta{ + Labels: map[string]string{ + "name": "foo", + "type": "production", + }, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Image: "foo/bar", + TerminationMessagePath: api.TerminationMessagePathDefault, + ImagePullPolicy: api.PullIfNotPresent, + }, + }, + RestartPolicy: api.RestartPolicyAlways, + DNSPolicy: api.DNSDefault, + NodeSelector: map[string]string{ + "baz": "blah", + }, + }, + }, + }, + }, + printNamespace: true, + }, + { + obj: &api.Service{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Spec: api.ServiceSpec{ + PortalIP: "1.2.3.4", + PublicIPs: []string{ + "2.3.4.5", + }, + Ports: []api.ServicePort{ + { + Port: 80, + Protocol: "TCP", + }, + }, + }, + }, + printNamespace: true, + }, + { + obj: &api.Endpoints{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Subsets: []api.EndpointSubset{{ + Addresses: []api.EndpointAddress{{IP: "127.0.0.1"}, {IP: "localhost"}}, + Ports: []api.EndpointPort{{Port: 8080}}, + }, + }}, + printNamespace: true, + }, + { + obj: &api.Namespace{ + ObjectMeta: api.ObjectMeta{Name: name}, + }, + printNamespace: false, + }, + { + obj: &api.Secret{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + }, + printNamespace: true, + }, + { + obj: &api.ServiceAccount{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Secrets: []api.ObjectReference{}, + }, + printNamespace: true, + }, + { + obj: &api.Node{ + ObjectMeta: api.ObjectMeta{Name: name}, + Status: api.NodeStatus{}, + }, + printNamespace: false, + }, + { + obj: &api.PersistentVolume{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Spec: api.PersistentVolumeSpec{}, + }, + printNamespace: true, + }, + { + obj: &api.PersistentVolumeClaim{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Spec: api.PersistentVolumeClaimSpec{}, + }, + printNamespace: true, + }, + { + obj: &api.Event{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + Source: api.EventSource{Component: "kubelet"}, + Message: "Item 1", + FirstTimestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), + LastTimestamp: util.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), + Count: 1, + }, + printNamespace: false, + }, + { + obj: &api.LimitRange{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + }, + printNamespace: true, + }, + { + obj: &api.ResourceQuota{ + ObjectMeta: api.ObjectMeta{Name: name, Namespace: namespaceName}, + }, + printNamespace: true, + }, + { + obj: &api.ComponentStatus{ + Conditions: []api.ComponentCondition{ + {Type: api.ComponentHealthy, Status: api.ConditionTrue, Message: "ok", Error: ""}, + }, + }, + printNamespace: false, + }, + } + + printer := NewHumanReadablePrinter(false, false) + for _, test := range table { + buffer := &bytes.Buffer{} + err := printer.PrintObj(test.obj, buffer) + if err != nil { + t.Fatalf("An error occurred printing object: %#v", err) + } + matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s/%s", namespaceName, name)) + if matched { + t.Errorf("Expect printing object not to contain namespace: %v", test.obj) + } + } + + printer = NewHumanReadablePrinter(false, true) + for _, test := range table { + buffer := &bytes.Buffer{} + err := printer.PrintObj(test.obj, buffer) + if err != nil { + t.Fatalf("An error occurred printing object: %#v", err) + } + matched := contains(strings.Fields(buffer.String()), fmt.Sprintf("%s/%s", namespaceName, name)) + if test.printNamespace && !matched { + t.Errorf("Expect printing object to contain namespace: %v", test.obj) + } else if !test.printNamespace && matched { + t.Errorf("Expect printing object not to contain namespace: %v", test.obj) + } } }