Merge pull request #62881 from juanvallejo/jvallejo/wire-print-flags-get-cmd

Automatic merge from submit-queue (batch tested with PRs 61601, 62881, 63159). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

wire print flags through get.go

**Release note**:
```release-note
NONE
```

Adds the PrintFlags pattern to `get.go`.
Prerequisite to ongoing server-side printing work.

cc @soltysh @deads2k
pull/8/head
Kubernetes Submit Queue 2018-04-25 14:03:21 -07:00 committed by GitHub
commit 08d358e3d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 425 additions and 379 deletions

View File

@ -149,6 +149,13 @@ package_group(
],
)
package_group(
name = "pkg_kubectl_cmd_get_CONSUMERS",
packages = [
"//pkg/kubectl/cmd",
],
)
package_group(
name = "pkg_kubectl_cmd_rollout_CONSUMERS",
packages = [
@ -177,7 +184,7 @@ package_group(
"//pkg/kubectl/cmd/auth",
"//pkg/kubectl/cmd/config",
"//pkg/kubectl/cmd/create",
"//pkg/kubectl/cmd/resource",
"//pkg/kubectl/cmd/get",
"//pkg/kubectl/cmd/rollout",
"//pkg/kubectl/cmd/set",
"//pkg/kubectl/cmd/templates",
@ -199,7 +206,7 @@ package_group(
"//pkg/kubectl/cmd",
"//pkg/kubectl/cmd/auth",
"//pkg/kubectl/cmd/create",
"//pkg/kubectl/cmd/resource",
"//pkg/kubectl/cmd/get",
"//pkg/kubectl/cmd/rollout",
"//pkg/kubectl/cmd/set",
"//pkg/kubectl/explain",
@ -233,7 +240,7 @@ package_group(
"//pkg/kubectl/cmd/auth",
"//pkg/kubectl/cmd/config",
"//pkg/kubectl/cmd/create",
"//pkg/kubectl/cmd/resource",
"//pkg/kubectl/cmd/get",
"//pkg/kubectl/cmd/rollout",
"//pkg/kubectl/cmd/set",
"//pkg/kubectl/cmd/testing",
@ -303,7 +310,7 @@ package_group(
"//pkg/kubectl/cmd/auth",
"//pkg/kubectl/cmd/config",
"//pkg/kubectl/cmd/create",
"//pkg/kubectl/cmd/resource",
"//pkg/kubectl/cmd/get",
"//pkg/kubectl/cmd/rollout",
"//pkg/kubectl/cmd/set",
"//pkg/kubectl/cmd/testing",

View File

@ -139,6 +139,7 @@ pkg/kubectl/cmd
pkg/kubectl/cmd/auth
pkg/kubectl/cmd/config
pkg/kubectl/cmd/create
pkg/kubectl/cmd/get
pkg/kubectl/cmd/rollout
pkg/kubectl/cmd/set
pkg/kubectl/cmd/templates

View File

@ -227,7 +227,7 @@ function wait-for-pods-with-label()
{
local i
for i in $(seq 1 10); do
kubeout=`kubectl get po -l $1 --template '{{range.items}}{{.metadata.name}}{{end}}' --sort-by metadata.name "${kube_flags[@]}"`
kubeout=`kubectl get po -l $1 --output=go-template --template='{{range.items}}{{.metadata.name}}{{end}}' --sort-by metadata.name "${kube_flags[@]}"`
if [[ $kubeout = $2 ]]; then
return
fi

View File

@ -69,7 +69,7 @@ go_library(
"//pkg/kubectl/cmd/auth:go_default_library",
"//pkg/kubectl/cmd/config:go_default_library",
"//pkg/kubectl/cmd/create:go_default_library",
"//pkg/kubectl/cmd/resource:go_default_library",
"//pkg/kubectl/cmd/get:go_default_library",
"//pkg/kubectl/cmd/rollout:go_default_library",
"//pkg/kubectl/cmd/scalejob:go_default_library",
"//pkg/kubectl/cmd/set:go_default_library",
@ -190,7 +190,6 @@ go_test(
"//pkg/apis/extensions:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/create:go_default_library",
"//pkg/kubectl/cmd/resource:go_default_library",
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
@ -201,7 +200,6 @@ go_test(
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/kubectl/util/term:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/strings:go_default_library",
"//vendor/github.com/googleapis/gnostic/OpenAPIv2:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/gopkg.in/yaml.v2:go_default_library",
@ -249,7 +247,7 @@ filegroup(
"//pkg/kubectl/cmd/auth:all-srcs",
"//pkg/kubectl/cmd/config:all-srcs",
"//pkg/kubectl/cmd/create:all-srcs",
"//pkg/kubectl/cmd/resource:all-srcs",
"//pkg/kubectl/cmd/get:all-srcs",
"//pkg/kubectl/cmd/rollout:all-srcs",
"//pkg/kubectl/cmd/scalejob:all-srcs",
"//pkg/kubectl/cmd/set:all-srcs",

View File

@ -27,7 +27,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/auth"
cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config"
"k8s.io/kubernetes/pkg/kubectl/cmd/create"
"k8s.io/kubernetes/pkg/kubectl/cmd/resource"
"k8s.io/kubernetes/pkg/kubectl/cmd/get"
"k8s.io/kubernetes/pkg/kubectl/cmd/rollout"
"k8s.io/kubernetes/pkg/kubectl/cmd/set"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -264,8 +264,8 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
{
Message: "Basic Commands (Intermediate):",
Commands: []*cobra.Command{
resource.NewCmdGet(f, ioStreams),
NewCmdExplain(f, ioStreams),
get.NewCmdGet(f, ioStreams),
NewCmdEdit(f, ioStreams),
NewCmdDelete(f, out, err),
},

View File

@ -19,7 +19,6 @@ package cmd
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
@ -35,18 +34,12 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/cmd/resource"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/strings"
)
// This init should be removed after switching this command and its tests to user external types.
@ -244,78 +237,6 @@ func newAllPhasePodList() *api.PodList {
}
}
func Example_printServiceWithLabels() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := resource.NewCmdGet(tf, genericclioptions.NewTestIOStreamsDiscard())
svc := &api.ServiceList{
Items: []api.Service{
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "ns1",
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
Labels: map[string]string{
"l1": "value",
},
},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{Protocol: "UDP", Port: 53},
{Protocol: "TCP", Port: 53},
},
Selector: map[string]string{
"s": "magic",
},
ClusterIP: "10.1.1.1",
Type: api.ServiceTypeClusterIP,
},
Status: api.ServiceStatus{},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc2",
Namespace: "ns2",
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
Labels: map[string]string{
"l1": "dolla-bill-yall",
},
},
Spec: api.ServiceSpec{
Ports: []api.ServicePort{
{Protocol: "TCP", Port: 80},
{Protocol: "TCP", Port: 8080},
},
Selector: map[string]string{
"s": "kazam",
},
ClusterIP: "10.1.1.2",
Type: api.ServiceTypeClusterIP,
},
Status: api.ServiceStatus{},
}},
}
ld := strings.NewLineDelimiter(os.Stdout, "|")
defer ld.Flush()
cmd.Flags().Set("label-columns", "l1")
err := cmdutil.PrintObject(cmd, svc, ld)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// |NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE L1|
// |svc1 ClusterIP 10.1.1.1 <none> 53/UDP,53/TCP 10y value|
// |svc2 ClusterIP 10.1.1.2 <none> 80/TCP,8080/TCP 10y dolla-bill-yall|
// ||
}
func TestNormalizationFuncGlobalExistence(t *testing.T) {
// This test can be safely deleted when we will not support multiple flag formats
root := NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)

View File

@ -1,9 +1,27 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
go_library(
name = "go_default_library",
srcs = ["get.go"],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/resource",
srcs = [
"get.go",
"get_flags.go",
"humanreadable_flags.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/get",
visibility = ["//visibility:public"],
deps = [
"//pkg/apis/core:go_default_library",
@ -13,8 +31,10 @@ go_library(
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util/interrupt:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
@ -34,7 +54,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["get_test.go"],
srcs = [
"get_test.go",
"humanreadable_flags_test.go",
],
data = [
"//api/openapi-spec:swagger-spec",
"//test/e2e/testing-manifests:all-srcs",
@ -53,6 +76,7 @@ go_test(
"//pkg/kubectl/cmd/util/openapi/testing:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/printers:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
@ -68,17 +92,3 @@ go_test(
"//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
package get
import (
"encoding/json"
@ -52,6 +52,11 @@ import (
// GetOptions contains the input to the get command.
type GetOptions struct {
PrintFlags *PrintFlags
ToPrinter func(*meta.RESTMapping, bool) (printers.ResourcePrinterFunc, error)
IsGeneric bool
PrintWithOpenAPICols bool
resource.FilenameOptions
Raw string
@ -67,10 +72,9 @@ type GetOptions struct {
ServerPrint bool
NoHeaders bool
Sort bool
IgnoreNotFound bool
ShowKind bool
LabelColumns []string
Export bool
IncludeUninitialized bool
@ -129,7 +133,8 @@ const (
// NewGetOptions returns a GetOptions with default chunk size 500.
func NewGetOptions(streams genericclioptions.IOStreams) *GetOptions {
return &GetOptions{
ChunkSize: 500,
PrintFlags: NewGetPrintFlags(),
ChunkSize: 500,
IOStreams: streams,
}
@ -138,7 +143,7 @@ func NewGetOptions(streams genericclioptions.IOStreams) *GetOptions {
// NewCmdGet creates a command object for the generic "get" action, which
// retrieves one or more resources from a server.
func NewCmdGet(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := NewGetOptions(streams)
o := NewGetOptions(streams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -148,77 +153,112 @@ func NewCmdGet(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Co
Long: getLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: getExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate(cmd))
cmdutil.CheckErr(options.Run(f, cmd, args))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run(f, cmd, args))
},
SuggestFor: []string{"list", "ps"},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
cmd.Flags().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
cmd.Flags().BoolVarP(&options.Watch, "watch", "w", options.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
cmd.Flags().BoolVar(&options.WatchOnly, "watch-only", options.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().Int64Var(&options.ChunkSize, "chunk-size", options.ChunkSize, "Return large lists in chunks rather than all at once. Pass 0 to disable. This flag is beta and may change in the future.")
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", options.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
cmd.Flags().StringVarP(&options.LabelSelector, "selector", "l", options.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().StringVar(&options.FieldSelector, "field-selector", options.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
cmd.Flags().BoolVar(&options.AllNamespaces, "all-namespaces", options.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
o.PrintFlags.AddFlags(cmd)
cmd.Flags().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to request from the server. Uses the transport specified by the kubeconfig file.")
cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.")
cmd.Flags().BoolVar(&o.WatchOnly, "watch-only", o.WatchOnly, "Watch for changes to the requested object(s), without listing/getting first.")
cmd.Flags().Int64Var(&o.ChunkSize, "chunk-size", o.ChunkSize, "Return large lists in chunks rather than all at once. Pass 0 to disable. This flag is beta and may change in the future.")
cmd.Flags().BoolVar(&o.IgnoreNotFound, "ignore-not-found", o.IgnoreNotFound, "If the requested object does not exist the command will return exit code 0.")
cmd.Flags().StringVarP(&o.LabelSelector, "selector", "l", o.LabelSelector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().StringVar(&o.FieldSelector, "field-selector", o.FieldSelector, "Selector (field query) to filter on, supports '=', '==', and '!='.(e.g. --field-selector key1=value1,key2=value2). The server only supports a limited number of field queries per type.")
cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", o.AllNamespaces, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.")
cmdutil.AddIncludeUninitializedFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
addOpenAPIPrintColumnFlags(cmd)
addServerPrintColumnFlags(cmd)
cmd.Flags().BoolVar(&options.ShowKind, "show-kind", options.ShowKind, "If present, list the resource type for the requested object(s).")
cmd.Flags().StringSliceVarP(&options.LabelColumns, "label-columns", "L", options.LabelColumns, "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...")
cmd.Flags().BoolVar(&options.Export, "export", options.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to get from a server.")
cmd.Flags().BoolVar(&o.Export, "export", o.Export, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to get from a server.")
return cmd
}
// Complete takes the command arguments and factory and infers any remaining options.
func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(options.Raw) > 0 {
func (o *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(o.Raw) > 0 {
if len(args) > 0 {
return fmt.Errorf("arguments may not be passed when --raw is specified")
}
return nil
}
options.ServerPrint = cmdutil.GetFlagBool(cmd, useServerPrintColumns)
o.ServerPrint = cmdutil.GetFlagBool(cmd, useServerPrintColumns)
var err error
options.Namespace, options.ExplicitNamespace, err = f.DefaultNamespace()
o.Namespace, o.ExplicitNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
if options.AllNamespaces {
options.ExplicitNamespace = false
if o.AllNamespaces {
o.ExplicitNamespace = false
}
isSorting, err := cmd.Flags().GetString("sort-by")
if err != nil {
return err
}
options.Sort = len(isSorting) > 0
o.Sort = len(isSorting) > 0
o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers")
// TODO (soltysh): currently we don't support sorting and custom columns
// with server side print. So in these cases force the old behavior.
outputOption := cmd.Flags().Lookup("output").Value.String()
if options.Sort && outputOption == "custom-columns" {
options.ServerPrint = false
if o.Sort && outputOption == "custom-columns" {
o.ServerPrint = false
}
options.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
// obtain printer here in order to determine if we are
// printing humanreadable or generic output.
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.IsGeneric = printer.IsGeneric()
o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
o.PrintWithOpenAPICols = cmdutil.GetFlagBool(cmd, useOpenAPIPrintColumnFlagLabel)
o.ToPrinter = func(mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinterFunc, error) {
// make a new copy of current flags / opts before mutating
printFlags := o.PrintFlags.Copy()
if mapping != nil {
if !cmdSpecifiesOutputFmt(cmd) && o.PrintWithOpenAPICols {
if apiSchema, err := f.OpenAPISchema(); err == nil {
printFlags.UseOpenAPIColumns(apiSchema, mapping)
}
}
if resource.MultipleTypesRequested(args) {
printFlags.EnsureWithKind(mapping.GroupVersionKind.GroupKind())
}
}
if withNamespace {
printFlags.EnsureWithNamespace()
}
printer, err := printFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
switch {
case options.Watch || options.WatchOnly:
case o.Watch || o.WatchOnly:
// include uninitialized objects when watching on a single object
// unless explicitly set --include-uninitialized=false
options.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2)
o.IncludeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2)
default:
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
fmt.Fprintf(options.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.ValidResourceTypeList(f))
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
fmt.Fprintf(o.ErrOut, "You must specify the type of resource to get. %s\n\n", cmdutil.ValidResourceTypeList(f))
fullCmdName := cmd.Parent().CommandPath()
usageString := "Required resource not specified."
if len(fullCmdName) > 0 && cmdutil.IsSiblingCommandExists(cmd, "explain") {
@ -232,15 +272,15 @@ func (options *GetOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
}
// Validate checks the set of flags provided by the user.
func (options *GetOptions) Validate(cmd *cobra.Command) error {
if len(options.Raw) > 0 {
if options.Watch || options.WatchOnly || len(options.LabelSelector) > 0 || options.Export {
func (o *GetOptions) Validate(cmd *cobra.Command) error {
if len(o.Raw) > 0 {
if o.Watch || o.WatchOnly || len(o.LabelSelector) > 0 || o.Export {
return fmt.Errorf("--raw may not be specified with other flags that filter the server request or alter the output")
}
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return cmdutil.UsageErrorf(cmd, "--raw and --output are mutually exclusive")
}
if _, err := url.ParseRequestURI(options.Raw); err != nil {
if _, err := url.ParseRequestURI(o.Raw); err != nil {
return cmdutil.UsageErrorf(cmd, "--raw must be a valid URL path: %v", err)
}
}
@ -255,35 +295,29 @@ func (options *GetOptions) Validate(cmd *cobra.Command) error {
// Run performs the get operation.
// TODO: remove the need to pass these arguments, like other commands.
func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(options.Raw) > 0 {
return options.raw(f)
func (o *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
if len(o.Raw) > 0 {
return o.raw(f)
}
if options.Watch || options.WatchOnly {
return options.watch(f, cmd, args)
}
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
printer, err := cmdutil.PrinterForOptions(printOpts)
if err != nil {
return err
if o.Watch || o.WatchOnly {
return o.watch(f, cmd, args)
}
r := f.NewBuilder().
Unstructured().
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector).
FieldSelectorParam(options.FieldSelector).
ExportParam(options.Export).
RequestChunksOf(options.ChunkSize).
IncludeUninitialized(options.IncludeUninitialized).
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
ExportParam(o.Export).
RequestChunksOf(o.ChunkSize).
IncludeUninitialized(o.IncludeUninitialized).
ResourceTypeOrNameArgs(true, args...).
ContinueOnError().
Latest().
Flatten().
TransformRequests(func(req *rest.Request) {
if options.ServerPrint && !printer.IsGeneric() && !options.Sort {
if o.ServerPrint && !o.IsGeneric && !o.Sort {
group := metav1beta1.GroupName
version := metav1beta1.SchemeGroupVersion.Version
@ -293,15 +327,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
}).
Do()
if options.IgnoreNotFound {
if o.IgnoreNotFound {
r.IgnoreErrors(kapierrors.IsNotFound)
}
if err := r.Err(); err != nil {
return err
}
if printer.IsGeneric() {
return options.printGeneric(printer, r)
if o.IsGeneric {
return o.printGeneric(r)
}
allErrs := []error{}
@ -313,8 +347,8 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
objs := make([]runtime.Object, len(infos))
for ix := range infos {
if options.ServerPrint {
table, err := options.decodeIntoTable(cmdutil.InternalVersionJSONEncoder(), infos[ix].Object)
if o.ServerPrint {
table, err := o.decodeIntoTable(cmdutil.InternalVersionJSONEncoder(), infos[ix].Object)
if err == nil {
infos[ix].Object = table
} else {
@ -332,35 +366,26 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
return err
}
var sorter *kubectl.RuntimeSort
if options.Sort && len(objs) > 1 {
if o.Sort && len(objs) > 1 {
// TODO: questionable
if sorter, err = kubectl.SortObjects(cmdutil.InternalVersionDecoder(), objs, sorting); err != nil {
return err
}
}
// use the default printer for each object
printer = nil
var printer printers.ResourcePrinter
var lastMapping *meta.RESTMapping
w := printers.GetNewTabWriter(options.Out)
useOpenAPIPrintColumns := cmdutil.GetFlagBool(cmd, useOpenAPIPrintColumnFlagLabel)
showKind := options.ShowKind || resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter)
noHeaders := cmdutil.GetFlagBool(cmd, "no-headers")
nonEmptyObjCount := 0
w := printers.GetNewTabWriter(o.Out)
for ix := range objs {
var mapping *meta.RESTMapping
var original runtime.Object
var info *resource.Info
if sorter != nil {
info = infos[sorter.OriginalPosition(ix)]
mapping = info.Mapping
original = info.Object
} else {
info = infos[ix]
mapping = info.Mapping
original = info.Object
}
// if dealing with a table that has no rows, skip remaining steps
@ -371,29 +396,24 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
}
}
nonEmptyObjCount++
printWithNamespace := o.AllNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
if printer != nil {
w.Flush()
w.Flush()
// TODO: this doesn't belong here
// add linebreak between resource groups (if there is more than one)
// skip linebreak above first resource group
if lastMapping != nil && !o.NoHeaders {
fmt.Fprintln(o.ErrOut)
}
printWithNamespace := options.AllNamespaces
if mapping != nil && mapping.Scope.Name() == meta.RESTScopeNameRoot {
printWithNamespace = false
}
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, printWithNamespace)
// if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true,
// then get the default output options for this mapping from OpenAPI schema.
if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns {
updatePrintOptionsForOpenAPI(f, mapping, printOpts)
}
if showKind && mapping != nil {
printOpts.WithKind = true
printOpts.Kind = mapping.GroupVersionKind.GroupKind()
}
printer, err = cmdutil.PrinterForOptions(printOpts)
printer, err = o.ToPrinter(mapping, printWithNamespace)
if err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
@ -402,66 +422,33 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
continue
}
// TODO: this doesn't belong here
// add linebreak between resource groups (if there is more than one)
// skip linebreak above first resource group
if lastMapping != nil && !noHeaders {
fmt.Fprintf(options.ErrOut, "%s\n", "")
}
lastMapping = mapping
}
typedObj := info.AsInternal()
objToPrint := typedObj
if printer.IsGeneric() {
// use raw object as received from the builder when using generic
// printer instead of decodedObj
objToPrint = original
}
if err := printer.PrintObj(objToPrint, w); err != nil {
if !errs.Has(err.Error()) {
errs.Insert(err.Error())
allErrs = append(allErrs, err)
}
continue
}
printer.PrintObj(info.AsInternal(), w)
}
w.Flush()
nonEmptyObjCount := 0
for _, obj := range objs {
if table, ok := obj.(*metav1beta1.Table); ok {
// exclude any Table objects with empty rows from our total object count
if len(table.Rows) == 0 {
continue
}
}
nonEmptyObjCount++
}
if nonEmptyObjCount == 0 && !options.IgnoreNotFound {
fmt.Fprintln(options.ErrOut, "No resources found.")
if nonEmptyObjCount == 0 && !o.IgnoreNotFound {
fmt.Fprintln(o.ErrOut, "No resources found.")
}
return utilerrors.NewAggregate(allErrs)
}
// raw makes a simple HTTP request to the provided path on the server using the default
// credentials.
func (options *GetOptions) raw(f cmdutil.Factory) error {
func (o *GetOptions) raw(f cmdutil.Factory) error {
restClient, err := f.RESTClient()
if err != nil {
return err
}
stream, err := restClient.Get().RequestURI(options.Raw).Stream()
stream, err := restClient.Get().RequestURI(o.Raw).Stream()
if err != nil {
return err
}
defer stream.Close()
_, err = io.Copy(options.Out, stream)
_, err = io.Copy(o.Out, stream)
if err != nil && err != io.EOF {
return err
}
@ -470,16 +457,16 @@ func (options *GetOptions) raw(f cmdutil.Factory) error {
// watch starts a client-side watch of one or more resources.
// TODO: remove the need for arguments here.
func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func (o *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
r := f.NewBuilder().
Unstructured().
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector).
FieldSelectorParam(options.FieldSelector).
ExportParam(options.Export).
RequestChunksOf(options.ChunkSize).
IncludeUninitialized(options.IncludeUninitialized).
NamespaceParam(o.Namespace).DefaultNamespace().AllNamespaces(o.AllNamespaces).
FilenameParam(o.ExplicitNamespace, &o.FilenameOptions).
LabelSelectorParam(o.LabelSelector).
FieldSelectorParam(o.FieldSelector).
ExportParam(o.Export).
RequestChunksOf(o.ChunkSize).
IncludeUninitialized(o.IncludeUninitialized).
ResourceTypeOrNameArgs(true, args...).
SingleResourceType().
Latest().
@ -514,8 +501,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
info := infos[0]
mapping := info.ResourceMapping()
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, options.AllNamespaces)
printer, err := cmdutil.PrinterForOptions(printOpts)
printer, err := o.ToPrinter(mapping, o.AllNamespaces)
if err != nil {
return err
}
@ -540,9 +526,9 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
}
// print the current object
if !options.WatchOnly {
if !o.WatchOnly {
var objsToPrint []runtime.Object
writer := printers.GetNewTabWriter(options.Out)
writer := printers.GetNewTabWriter(o.Out)
if isList {
objsToPrint, _ = meta.ExtractList(obj)
@ -550,10 +536,12 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
objsToPrint = append(objsToPrint, obj)
}
for _, objToPrint := range objsToPrint {
// printing always takes the internal version, but the watch event uses externals
// TODO fix printing to use server-side or be version agnostic
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
if err := printer.PrintObj(attemptToConvertToInternal(objToPrint, mapping, internalGV), writer); err != nil {
if !o.IsGeneric {
// printing always takes the internal version, but the watch event uses externals
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
objToPrint = attemptToConvertToInternal(objToPrint, mapping, internalGV)
}
if err := printer.PrintObj(objToPrint, writer); err != nil {
return fmt.Errorf("unable to output the provided object: %v", err)
}
}
@ -579,7 +567,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
// printing always takes the internal version, but the watch event uses externals
// TODO fix printing to use server-side or be version agnostic
internalGV := mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion()
if err := printer.PrintObj(attemptToConvertToInternal(e.Object, mapping, internalGV), options.Out); err != nil {
if err := printer.PrintObj(attemptToConvertToInternal(e.Object, mapping, internalGV), o.Out); err != nil {
return false, err
}
return false, nil
@ -599,7 +587,7 @@ func attemptToConvertToInternal(obj runtime.Object, converter runtime.ObjectConv
return internalObject
}
func (options *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.Object) (runtime.Object, error) {
func (o *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.Object) (runtime.Object, error) {
if obj.GetObjectKind().GroupVersionKind().Kind != "Table" {
return nil, fmt.Errorf("attempt to decode non-Table object into a v1beta1.Table")
}
@ -618,7 +606,7 @@ func (options *GetOptions) decodeIntoTable(encoder runtime.Encoder, obj runtime.
return table, nil
}
func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *resource.Result) error {
func (o *GetOptions) printGeneric(r *resource.Result) error {
// we flattened the data from the builder, so we have individual items, but now we'd like to either:
// 1. if there is more than one item, combine them all into a single list
// 2. if there is a single item and that item is a list, leave it as its specific list
@ -633,10 +621,15 @@ func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *res
errs = append(errs, err)
}
if len(infos) == 0 && options.IgnoreNotFound {
if len(infos) == 0 && o.IgnoreNotFound {
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
}
printer, err := o.ToPrinter(nil, false)
if err != nil {
return err
}
var obj runtime.Object
if !singleItemImplied || len(infos) > 1 {
// we have more than one item, so coerce all items into a list.
@ -694,13 +687,13 @@ func (options *GetOptions) printGeneric(printer printers.ResourcePrinter, r *res
for _, item := range items {
list.Items = append(list.Items, *item.(*unstructured.Unstructured))
}
if err := printer.PrintObj(list, options.Out); err != nil {
if err := printer.PrintObj(list, o.Out); err != nil {
errs = append(errs, err)
}
return utilerrors.Reduce(utilerrors.Flatten(utilerrors.NewAggregate(errs)))
}
if printErr := printer.PrintObj(obj, options.Out); printErr != nil {
if printErr := printer.PrintObj(obj, o.Out); printErr != nil {
errs = append(errs, printErr)
}

View File

@ -0,0 +1,169 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package get
import (
"strings"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/printers"
)
// PrintFlags composes common printer flag structs
// used in the Get command.
type PrintFlags struct {
JSONYamlPrintFlags *printers.JSONYamlPrintFlags
NamePrintFlags *printers.NamePrintFlags
TemplateFlags *printers.KubeTemplatePrintFlags
CustomColumnsFlags *printers.CustomColumnsPrintFlags
HumanReadableFlags *HumanPrintFlags
NoHeaders *bool
OutputFormat *string
}
// EnsureWithNamespace ensures that humanreadable flags return
// a printer capable of printing with a "namespace" column.
func (f *PrintFlags) EnsureWithNamespace() error {
return f.HumanReadableFlags.EnsureWithNamespace()
}
// EnsureWithKind ensures that humanreadable flags return
// a printer capable of including resource kinds.
func (f *PrintFlags) EnsureWithKind(kind schema.GroupKind) error {
return f.HumanReadableFlags.EnsureWithKind(kind)
}
// Copy returns a copy of PrintFlags for mutation
func (f *PrintFlags) Copy() PrintFlags {
printFlags := *f
return printFlags
}
// UseOpenAPIColumns modifies the output format, as well as the
// "allowMissingKeys" option for template printers, to values
// defined in the OpenAPI schema of a resource.
func (f *PrintFlags) UseOpenAPIColumns(api openapi.Resources, mapping *meta.RESTMapping) error {
// Found openapi metadata for this resource
schema := api.LookupResource(mapping.GroupVersionKind)
if schema == nil {
// Schema not found, return empty columns
return nil
}
columns, found := openapi.GetPrintColumns(schema.GetExtensions())
if !found {
// Extension not found, return empty columns
return nil
}
parts := strings.SplitN(columns, "=", 2)
if len(parts) < 2 {
return nil
}
allowMissingKeys := true
f.OutputFormat = &parts[0]
f.TemplateFlags.TemplateArgument = &parts[1]
f.TemplateFlags.AllowMissingKeys = &allowMissingKeys
return nil
}
// ToPrinter attempts to find a composed set of PrintFlags suitable for
// returning a printer based on current flag values.
func (f *PrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
outputFormat := ""
if f.OutputFormat != nil {
outputFormat = *f.OutputFormat
}
noHeaders := false
if f.NoHeaders != nil {
noHeaders = *f.NoHeaders
}
f.HumanReadableFlags.NoHeaders = noHeaders
f.CustomColumnsFlags.NoHeaders = noHeaders
if f.TemplateFlags.TemplateArgument != nil {
f.CustomColumnsFlags.TemplateArgument = *f.TemplateFlags.TemplateArgument
}
if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.HumanReadableFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.TemplateFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.CustomColumnsFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
return nil, printers.NoCompatiblePrinterError{Options: f}
}
// AddFlags receives a *cobra.Command reference and binds
// flags related to humanreadable and template printing.
func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
f.JSONYamlPrintFlags.AddFlags(cmd)
f.NamePrintFlags.AddFlags(cmd)
f.TemplateFlags.AddFlags(cmd)
f.HumanReadableFlags.AddFlags(cmd)
f.CustomColumnsFlags.AddFlags(cmd)
if f.OutputFormat != nil {
cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, "Output format. One of: json|yaml|wide|name|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=... See custom columns [http://kubernetes.io/docs/user-guide/kubectl-overview/#custom-columns], golang template [http://golang.org/pkg/text/template/#pkg-overview] and jsonpath template [http://kubernetes.io/docs/user-guide/jsonpath].")
}
if f.NoHeaders != nil {
cmd.Flags().BoolVar(f.NoHeaders, "no-headers", *f.NoHeaders, "When using the default or custom-column output format, don't print headers (default print headers).")
}
// TODO(juanvallejo): This is deprecated - remove
cmd.Flags().BoolP("show-all", "a", true, "When printing, show all resources (default show all pods including terminated one.)")
cmd.Flags().MarkDeprecated("show-all", "will be removed in an upcoming release")
}
// NewGetPrintFlags returns flags associated with humanreadable,
// template, and "name" printing, with default values set.
func NewGetPrintFlags() *PrintFlags {
outputFormat := ""
noHeaders := false
return &PrintFlags{
OutputFormat: &outputFormat,
NoHeaders: &noHeaders,
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
TemplateFlags: printers.NewKubeTemplatePrintFlags(),
HumanReadableFlags: NewHumanPrintFlags(),
CustomColumnsFlags: printers.NewCustomColumnsPrintFlags(),
}
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package resource
package get
import (
"bytes"
@ -513,7 +513,6 @@ func TestGetAllListObjects(t *testing.T) {
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdGet(tf, streams)
cmd.SetOutput(buf)
cmd.Flags().Set("show-all", "true")
cmd.Run(cmd, []string{"pods"})
expected := `NAME READY STATUS RESTARTS AGE
@ -889,32 +888,6 @@ node/foo Unknown <none> <unknown>
}
}
func TestGetByFormatForcesFlag(t *testing.T) {
pods, _, _ := testData()
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
codec := legacyscheme.Codecs.LegacyCodec(scheme.Versions...)
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
}
tf.Namespace = "test"
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdGet(tf, streams)
cmd.SetOutput(buf)
cmd.Flags().Lookup("output").Value.Set("yaml")
cmd.Flags().Set("show-all", "false")
cmd.Run(cmd, []string{"pods"})
showAllFlag, _ := cmd.Flags().GetBool("show-all")
if showAllFlag {
t.Error("expected showAll to not be true when getting resource")
}
}
func watchTestData() ([]api.Pod, []watch.Event) {
pods := []api.Pod{
{

View File

@ -14,13 +14,15 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
package get
import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)
// HumanPrintFlags provides default flags necessary for printing.
@ -40,11 +42,28 @@ type HumanPrintFlags struct {
WithNamespace bool
}
// EnsureWithKind sets the provided GroupKind humanreadable value.
// If the kind received is non-empty, the "showKind" humanreadable
// printer option is set to true.
func (f *HumanPrintFlags) EnsureWithKind(kind schema.GroupKind) error {
showKind := !kind.Empty()
f.Kind = kind
f.ShowKind = &showKind
return nil
}
// EnsureWithNamespace sets the "WithNamespace" humanreadable option to true.
func (f *HumanPrintFlags) EnsureWithNamespace() error {
f.WithNamespace = true
return nil
}
// ToPrinter receives an outputFormat and returns a printer capable of
// handling human-readable output.
func (f *HumanPrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, bool, error) {
func (f *HumanPrintFlags) ToPrinter(outputFormat string) (printers.ResourcePrinter, error) {
if len(outputFormat) > 0 && outputFormat != "wide" {
return nil, false, nil
return nil, printers.NoCompatiblePrinterError{Options: f}
}
encoder := scheme.Codecs.LegacyCodec(scheme.Registry.RegisteredGroupVersions()...)
@ -65,7 +84,7 @@ func (f *HumanPrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, bool,
columnLabels = *f.ColumnLabels
}
p := NewHumanReadablePrinter(encoder, decoder, PrintOptions{
p := printers.NewHumanReadablePrinter(encoder, decoder, printers.PrintOptions{
Kind: f.Kind,
WithKind: showKind,
NoHeaders: f.NoHeaders,
@ -74,14 +93,11 @@ func (f *HumanPrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, bool,
ColumnLabels: columnLabels,
ShowLabels: showLabels,
})
// TODO(juanvallejo): enable this here once we wire commands to instantiate PrintFlags directly.
// PrintHandlers are currently added through cmd/util/printing.go#PrinterForOptions
//printersinternal.AddHandlers(p)
printersinternal.AddHandlers(p)
// TODO(juanvallejo): handle sorting here
return p, true, nil
return p, nil
}
// AddFlags receives a *cobra.Command reference and binds
@ -103,19 +119,19 @@ func (f *HumanPrintFlags) AddFlags(c *cobra.Command) {
// NewHumanPrintFlags returns flags associated with
// human-readable printing, with default values set.
func NewHumanPrintFlags(kind schema.GroupKind, noHeaders, withNamespace, absoluteTimestamps bool) *HumanPrintFlags {
func NewHumanPrintFlags() *HumanPrintFlags {
showLabels := false
sortBy := ""
showKind := false
columnLabels := []string{}
return &HumanPrintFlags{
NoHeaders: noHeaders,
WithNamespace: withNamespace,
AbsoluteTimestamps: absoluteTimestamps,
NoHeaders: false,
WithNamespace: false,
AbsoluteTimestamps: false,
ColumnLabels: &columnLabels,
Kind: kind,
Kind: schema.GroupKind{},
ShowLabels: &showLabels,
SortBy: &sortBy,
ShowKind: &showKind,

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package printers_test
package get
import (
"bytes"
@ -26,11 +26,15 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)
func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
testObject := &api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
testObject := &api.Pod{ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{
"l1": "value",
},
}}
testCases := []struct {
name string
@ -75,6 +79,11 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
showKind: true,
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\npod/foo\\ +0/0\\ +0\\ +<unknown>\n",
},
{
name: "label-columns prints specified label values in new column",
columnLabels: []string{"l1"},
expectedOutput: "NAME\\ +READY\\ +STATUS\\ +RESTARTS\\ +AGE\\ +L1\nfoo\\ +0/0\\ +0\\ +<unknown>\\ +value\n",
},
{
name: "withNamespace displays an additional NAMESPACE column",
withNamespace: true,
@ -94,7 +103,7 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
printFlags := printers.HumanPrintFlags{
printFlags := HumanPrintFlags{
ShowKind: &tc.showKind,
ShowLabels: &tc.showLabels,
SortBy: &tc.sortBy,
@ -108,14 +117,14 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
printFlags.Kind = schema.GroupKind{Kind: "pod"}
}
p, matched, err := printFlags.ToPrinter(tc.outputFormat)
p, err := printFlags.ToPrinter(tc.outputFormat)
if tc.expectNoMatch {
if matched {
if !printers.IsNoCompatiblePrinterError(err) {
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
}
return
}
if !matched {
if printers.IsNoCompatiblePrinterError(err) {
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
}
@ -129,10 +138,6 @@ func TestHumanReadablePrinterSupportsExpectedOptions(t *testing.T) {
t.Fatalf("unexpected error: %v", err)
}
// TODO(juanvallejo): remove this once we wire PrintFlags at the command level.
// handlers should be attached to the printer inside of the ToPrinter method.
printersinternal.AddHandlers(p.(*printers.HumanReadablePrinter))
out := bytes.NewBuffer([]byte{})
err = p.PrintObj(testObject, out)
if err != nil {

View File

@ -13,7 +13,6 @@ go_library(
"customcolumn_flags.go",
"flags.go",
"humanreadable.go",
"humanreadable_flags.go",
"interface.go",
"json.go",
"json_yaml_flags.go",
@ -53,7 +52,6 @@ go_test(
srcs = [
"customcolumn_flags_test.go",
"customcolumn_test.go",
"humanreadable_flags_test.go",
"json_yaml_flags_test.go",
"jsonpath_flags_test.go",
"name_flags_test.go",
@ -63,11 +61,9 @@ go_test(
":go_default_library",
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/core:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
],
)

View File

@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
@ -37,9 +38,9 @@ type CustomColumnsPrintFlags struct {
// handling custom-column printing.
// Returns false if the specified templateFormat does not match a supported format.
// Supported format types can be found in pkg/printers/printers.go
func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, bool, error) {
func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, error) {
if len(templateFormat) == 0 {
return nil, false, fmt.Errorf("missing output format")
return nil, NoCompatiblePrinterError{}
}
templateValue := ""
@ -63,11 +64,11 @@ func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (ResourcePrin
}
if _, supportedFormat := supportedFormats[templateFormat]; !supportedFormat {
return nil, false, nil
return nil, NoCompatiblePrinterError{}
}
if len(templateValue) == 0 {
return nil, true, fmt.Errorf("custom-columns format specified but no custom columns given")
return nil, fmt.Errorf("custom-columns format specified but no custom columns given")
}
decoder := scheme.Codecs.UniversalDecoder()
@ -75,15 +76,15 @@ func (f *CustomColumnsPrintFlags) ToPrinter(templateFormat string) (ResourcePrin
if templateFormat == "custom-columns-file" {
file, err := os.Open(templateValue)
if err != nil {
return nil, true, fmt.Errorf("error reading template %s, %v\n", templateValue, err)
return nil, fmt.Errorf("error reading template %s, %v\n", templateValue, err)
}
defer file.Close()
p, err := NewCustomColumnsPrinterFromTemplate(file, decoder)
return p, true, err
return p, err
}
p, err := NewCustomColumnsPrinterFromSpec(templateValue, decoder, f.NoHeaders)
return p, true, err
return NewVersionedPrinter(p, legacyscheme.Scheme, legacyscheme.Scheme, scheme.Versions...), err
}
// AddFlags receives a *cobra.Command reference and binds
@ -93,9 +94,9 @@ func (f *CustomColumnsPrintFlags) AddFlags(c *cobra.Command) {}
// NewCustomColumnsPrintFlags returns flags associated with
// custom-column printing, with default values set.
// NoHeaders and TemplateArgument should be set by callers.
func NewCustomColumnsPrintFlags(noHeaders bool, templateValue string) *CustomColumnsPrintFlags {
func NewCustomColumnsPrintFlags() *CustomColumnsPrintFlags {
return &CustomColumnsPrintFlags{
NoHeaders: noHeaders,
TemplateArgument: templateValue,
NoHeaders: false,
TemplateArgument: "",
}
}

View File

@ -98,14 +98,14 @@ func TestPrinterSupportsExpectedCustomColumnFormats(t *testing.T) {
TemplateArgument: tc.templateArg,
}
p, matched, err := printFlags.ToPrinter(tc.outputFormat)
p, err := printFlags.ToPrinter(tc.outputFormat)
if tc.expectNoMatch {
if matched {
if !printers.IsNoCompatiblePrinterError(err) {
t.Fatalf("expected no printer matches for output format %q", tc.outputFormat)
}
return
}
if !matched {
if printers.IsNoCompatiblePrinterError(err) {
t.Fatalf("expected to match template printer for output format %q", tc.outputFormat)
}

View File

@ -99,26 +99,6 @@ func TestVersionedPrinter(t *testing.T) {
}
}
func TestPrintDefault(t *testing.T) {
printerTests := []struct {
Name string
Format string
}{
{"test wide", "wide"},
{"test blank format", ""},
}
for _, test := range printerTests {
printer, err := printers.GetStandardPrinter(nil, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.RegisteredGroupVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{AllowMissingKeys: false})
if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}
if printer.IsGeneric() {
t.Errorf("in %s, printer should not be generic: %#v", test.Name, printer)
}
}
}
func TestPrintUnstructuredObject(t *testing.T) {
obj := &unstructured.Unstructured{
Object: map[string]interface{}{

View File

@ -75,37 +75,13 @@ func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, deco
NoHeaders: options.NoHeaders,
TemplateArgument: formatArgument,
}
customColumnsPrinter, matched, err := customColumnsFlags.ToPrinter(format)
if !matched {
return nil, fmt.Errorf("unable to match a name printer to handle current print options")
}
customColumnsPrinter, err := customColumnsFlags.ToPrinter(format)
if err != nil {
return nil, err
}
printer = customColumnsPrinter
case "wide":
fallthrough
case "":
humanPrintFlags := NewHumanPrintFlags(options.Kind, options.NoHeaders, options.WithNamespace, options.AbsoluteTimestamps)
// TODO: these should be bound through a call to humanPrintFlags#AddFlags(cmd) once we instantiate PrintFlags at the command level
humanPrintFlags.ShowKind = &options.WithKind
humanPrintFlags.ShowLabels = &options.ShowLabels
humanPrintFlags.ColumnLabels = &options.ColumnLabels
humanPrintFlags.SortBy = &options.SortBy
humanPrinter, matches, err := humanPrintFlags.ToPrinter(format)
if !matches {
return nil, fmt.Errorf("unable to match a printer to handle current print options")
}
if err != nil {
return nil, err
}
printer = humanPrinter
default:
return nil, fmt.Errorf("output format %q not recognized", format)
}