Merge pull request #63340 from juanvallejo/jvallejo/remove-printer-helpers

Automatic merge from submit-queue. 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>.

Remove cmdutil printer helpers

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

Removes printer helpers from the cmd/util package.
Followup to PrintFlags updates.

cc @deads2k @soltysh
This commit is contained in:
Kubernetes Submit Queue 2018-05-07 15:08:57 -07:00 committed by GitHub
commit 861d20bf48
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 285 additions and 595 deletions

View File

@ -90,8 +90,10 @@ func NewCmdApiResources(f cmdutil.Factory, ioStreams genericclioptions.IOStreams
cmdutil.CheckErr(o.RunApiResources(cmd, f))
},
}
cmdutil.AddOutputFlags(cmd)
cmdutil.AddNoHeadersFlags(cmd)
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
cmd.Flags().StringP("output", "o", "", "Output format. One of: wide|name.")
cmd.Flags().StringVar(&o.APIGroup, "api-group", "", "Limit to resources in the specified API group.")
cmd.Flags().BoolVar(&o.Namespaced, "namespaced", true, "Namespaced indicates if a resource is namespaced or not.")
cmd.Flags().StringSliceVar(&o.Verbs, "verbs", o.Verbs, "Limit to resources that support the specified verbs.")

View File

@ -102,8 +102,9 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IO
},
}
o.PrintFlags.AddFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().BoolVar(&o.CreateAnnotation, "create-annotation", o.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations")

View File

@ -1121,7 +1121,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
name: "set with exist object",
filePath: filenameRC,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc configured\n",
expectedOut: "replicationcontroller/test-rc\n",
output: "name",
},
{
@ -1142,14 +1142,14 @@ func TestRunApplySetLastApplied(t *testing.T) {
name: "set with exist object output json",
filePath: filenameRCJSON,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc configured\n",
expectedOut: "replicationcontroller/test-rc\n",
output: "name",
},
{
name: "set test for a directory of files",
filePath: dirName,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc configured\nreplicationcontroller/test-rc configured\n",
expectedOut: "replicationcontroller/test-rc\nreplicationcontroller/test-rc\n",
output: "name",
},
}

View File

@ -24,7 +24,9 @@ go_library(
"//pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/registry/rbac/reconciliation:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",

View File

@ -17,24 +17,23 @@ limitations under the License.
package auth
import (
"io"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)
func NewCmdAuth(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
func NewCmdAuth(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
// Parent command to which all subcommands are added.
cmds := &cobra.Command{
Use: "auth",
Short: "Inspect authorization",
Long: `Inspect authorization`,
Run: cmdutil.DefaultSubCommandRun(errOut),
Run: cmdutil.DefaultSubCommandRun(streams.ErrOut),
}
cmds.AddCommand(NewCmdCanI(f, out, errOut))
cmds.AddCommand(NewCmdReconcile(f, out, errOut))
cmds.AddCommand(NewCmdCanI(f, streams))
cmds.AddCommand(NewCmdReconcile(f, streams))
return cmds
}

View File

@ -19,12 +19,12 @@ package auth
import (
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -48,8 +48,7 @@ type CanIOptions struct {
Subresource string
ResourceName string
Out io.Writer
Err io.Writer
genericclioptions.IOStreams
}
var (
@ -81,10 +80,9 @@ var (
kubectl auth can-i get /logs/`)
)
func NewCmdCanI(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
func NewCmdCanI(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := &CanIOptions{
Out: out,
Err: err,
IOStreams: streams,
}
cmd := &cobra.Command{
@ -232,9 +230,9 @@ func (o *CanIOptions) resourceFor(mapper meta.RESTMapper, resourceArg string) sc
gvr, err = mapper.ResourceFor(groupResource.WithVersion(""))
if err != nil {
if len(groupResource.Group) == 0 {
fmt.Fprintf(o.Err, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource)
fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s'\n", groupResource.Resource)
} else {
fmt.Fprintf(o.Err, "Warning: the server doesn't have a resource type '%s' in group '%s'\n", groupResource.Resource, groupResource.Group)
fmt.Fprintf(o.ErrOut, "Warning: the server doesn't have a resource type '%s' in group '%s'\n", groupResource.Resource, groupResource.Group)
}
return schema.GroupVersionResource{Resource: resourceArg}
}

View File

@ -119,7 +119,7 @@ func TestRunAccessCheck(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
test.o.Out = ioutil.Discard
test.o.Err = ioutil.Discard
test.o.ErrOut = ioutil.Discard
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()

View File

@ -18,7 +18,6 @@ package auth
import (
"errors"
"io"
"github.com/golang/glog"
"github.com/spf13/cobra"
@ -29,21 +28,25 @@ import (
internalrbacclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/registry/rbac/reconciliation"
)
// ReconcileOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type ReconcileOptions struct {
PrintFlags *printers.PrintFlags
FilenameOptions *resource.FilenameOptions
Visitor resource.Visitor
RBACClient internalrbacclient.RbacInterface
NamespaceClient internalcoreclient.NamespaceInterface
Print func(*resource.Info) error
PrintObject printers.ResourcePrinterFunc
Out io.Writer
Err io.Writer
genericclioptions.IOStreams
}
var (
@ -57,12 +60,16 @@ var (
kubectl auth reconcile -f my-rbac-rules.yaml`)
)
func NewCmdReconcile(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
fileOptions := &resource.FilenameOptions{}
o := &ReconcileOptions{
Out: out,
Err: err,
func NewReconcileOptions(ioStreams genericclioptions.IOStreams) *ReconcileOptions {
return &ReconcileOptions{
FilenameOptions: &resource.FilenameOptions{},
PrintFlags: printers.NewPrintFlags("reconciled"),
IOStreams: ioStreams,
}
}
func NewCmdReconcile(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewReconcileOptions(streams)
cmd := &cobra.Command{
Use: "reconcile -f FILENAME",
@ -71,21 +78,21 @@ func NewCmdReconcile(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
Long: reconcileLong,
Example: reconcileExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(cmd, f, args, fileOptions))
cmdutil.CheckErr(o.Complete(cmd, f, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunReconcile())
},
}
cmdutil.AddPrinterFlags(cmd)
usage := "identifying the resource to reconcile."
cmdutil.AddFilenameOptionFlags(cmd, fileOptions, usage)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to reconcile.")
cmd.MarkFlagRequired("filename")
return cmd
}
func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string, options *resource.FilenameOptions) error {
func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args []string) error {
if len(args) > 0 {
return errors.New("no arguments are allowed")
}
@ -99,7 +106,7 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args
WithScheme(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
FilenameParam(enforceNamespace, o.FilenameOptions).
Flatten().
Do()
@ -115,17 +122,12 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args
o.RBACClient = client.Rbac()
o.NamespaceClient = client.Core().Namespaces()
dryRun := false
output := cmdutil.GetFlagString(cmd, "output")
shortOutput := output == "name"
o.Print = func(info *resource.Info) error {
if len(output) > 0 && !shortOutput {
return cmdutil.PrintObject(cmd, info.Object, o.Out)
}
cmdutil.PrintSuccess(shortOutput, o.Out, info.Object, dryRun, "reconciled")
return nil
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObject = printer.PrintObj
return nil
}
@ -139,13 +141,13 @@ func (o *ReconcileOptions) Validate() error {
if o.NamespaceClient == nil {
return errors.New("ReconcileOptions.NamespaceClient must be set")
}
if o.Print == nil {
if o.PrintObject == nil {
return errors.New("ReconcileOptions.Print must be set")
}
if o.Out == nil {
return errors.New("ReconcileOptions.Out must be set")
}
if o.Err == nil {
if o.ErrOut == nil {
return errors.New("ReconcileOptions.Err must be set")
}
return nil
@ -157,10 +159,6 @@ func (o *ReconcileOptions) RunReconcile() error {
return err
}
// shallowInfoCopy this is used to later twiddle the Object for printing
// we really need more straightforward printing options
shallowInfoCopy := *info
switch t := info.Object.(type) {
case *rbac.Role:
reconcileOptions := reconciliation.ReconcileRoleOptions{
@ -176,8 +174,7 @@ func (o *ReconcileOptions) RunReconcile() error {
if err != nil {
return err
}
shallowInfoCopy.Object = result.Role.GetObject()
o.Print(&shallowInfoCopy)
o.PrintObject(result.Role.GetObject(), o.Out)
case *rbac.ClusterRole:
reconcileOptions := reconciliation.ReconcileRoleOptions{
@ -192,8 +189,7 @@ func (o *ReconcileOptions) RunReconcile() error {
if err != nil {
return err
}
shallowInfoCopy.Object = result.Role.GetObject()
o.Print(&shallowInfoCopy)
o.PrintObject(result.Role.GetObject(), o.Out)
case *rbac.RoleBinding:
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
@ -209,8 +205,7 @@ func (o *ReconcileOptions) RunReconcile() error {
if err != nil {
return err
}
shallowInfoCopy.Object = result.RoleBinding.GetObject()
o.Print(&shallowInfoCopy)
o.PrintObject(result.RoleBinding.GetObject(), o.Out)
case *rbac.ClusterRoleBinding:
reconcileOptions := reconciliation.ReconcileRoleBindingOptions{
@ -225,8 +220,7 @@ func (o *ReconcileOptions) RunReconcile() error {
if err != nil {
return err
}
shallowInfoCopy.Object = result.RoleBinding.GetObject()
o.Print(&shallowInfoCopy)
o.PrintObject(result.RoleBinding.GetObject(), o.Out)
default:
glog.V(1).Infof("skipping %#v", info.Object.GetObjectKind())

View File

@ -123,8 +123,10 @@ func NewCmdCertificateApprove(f cmdutil.Factory, ioStreams genericclioptions.IOS
cmdutil.CheckErr(options.RunCertificateApprove(cmdutil.GetFlagBool(cmd, "force")))
},
}
options.PrintFlags.AddFlags(cmd)
cmd.Flags().Bool("force", false, "Update the CSR even if it is already approved.")
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
return cmd
@ -173,8 +175,10 @@ func NewCmdCertificateDeny(f cmdutil.Factory, ioStreams genericclioptions.IOStre
cmdutil.CheckErr(options.RunCertificateDeny(cmdutil.GetFlagBool(cmd, "force")))
},
}
options.PrintFlags.AddFlags(cmd)
cmd.Flags().Bool("force", false, "Update the CSR even if it is already denied.")
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, "identifying the resource to update")
return cmd

View File

@ -309,7 +309,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
NewCmdPortForward(f, out, err),
NewCmdProxy(f, out),
NewCmdCp(f, ioStreams),
auth.NewCmdAuth(f, out, err),
auth.NewCmdAuth(f, ioStreams),
},
},
{
@ -355,7 +355,7 @@ func NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command {
}
cmds.AddCommand(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), out, err))
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), ioStreams))
cmds.AddCommand(NewCmdPlugin(f, in, out, err))
cmds.AddCommand(NewCmdVersion(f, ioStreams))
cmds.AddCommand(NewCmdApiVersions(f, ioStreams))

View File

@ -14,6 +14,7 @@ go_library(
"current_context.go",
"delete_cluster.go",
"delete_context.go",
"flags.go",
"get_clusters.go",
"get_contexts.go",
"navigation_step_parser.go",
@ -30,6 +31,7 @@ go_library(
deps = [
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
@ -64,6 +66,7 @@ go_test(
embed = [":go_default_library"],
deps = [
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",

View File

@ -18,7 +18,6 @@ package config
import (
"fmt"
"io"
"path"
"strconv"
@ -27,11 +26,12 @@ import (
"k8s.io/client-go/tools/clientcmd"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
// NewCmdConfig creates a command object for the "config" action, and adds all child commands to it.
func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *cobra.Command {
func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, streams genericclioptions.IOStreams) *cobra.Command {
if len(pathOptions.ExplicitFileFlag) == 0 {
pathOptions.ExplicitFileFlag = clientcmd.RecommendedConfigPathFlag
}
@ -48,25 +48,26 @@ func NewCmdConfig(f cmdutil.Factory, pathOptions *clientcmd.PathOptions, out, er
1. If the --` + pathOptions.ExplicitFileFlag + ` flag is set, then only that file is loaded. The flag may only be set once and no merging takes place.
2. If $` + pathOptions.EnvVar + ` environment variable is set, then it is used a list of paths (normal path delimitting rules for your system). These paths are merged. When a value is modified, it is modified in the file that defines the stanza. When a value is created, it is created in the first file that exists. If no files in the chain exist, then it creates the last file in the list.
3. Otherwise, ` + path.Join("${HOME}", pathOptions.GlobalFileSubpath) + ` is used and no merging takes place.`),
Run: cmdutil.DefaultSubCommandRun(errOut),
Run: cmdutil.DefaultSubCommandRun(streams.ErrOut),
}
// file paths are common to all sub commands
cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
cmd.AddCommand(NewCmdConfigView(f, out, errOut, pathOptions))
cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions))
cmd.AddCommand(NewCmdConfigSet(out, pathOptions))
cmd.AddCommand(NewCmdConfigUnset(out, pathOptions))
cmd.AddCommand(NewCmdConfigCurrentContext(out, pathOptions))
cmd.AddCommand(NewCmdConfigUseContext(out, pathOptions))
cmd.AddCommand(NewCmdConfigGetContexts(out, pathOptions))
cmd.AddCommand(NewCmdConfigGetClusters(out, pathOptions))
cmd.AddCommand(NewCmdConfigDeleteCluster(out, pathOptions))
cmd.AddCommand(NewCmdConfigDeleteContext(out, errOut, pathOptions))
cmd.AddCommand(NewCmdConfigRenameContext(out, pathOptions))
// TODO(juanvallejo): update all subcommands to work with genericclioptions.IOStreams
cmd.AddCommand(NewCmdConfigView(f, streams, pathOptions))
cmd.AddCommand(NewCmdConfigSetCluster(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigSetAuthInfo(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigSetContext(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigSet(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigUnset(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigCurrentContext(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigUseContext(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigGetContexts(streams, pathOptions))
cmd.AddCommand(NewCmdConfigGetClusters(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigDeleteCluster(streams.Out, pathOptions))
cmd.AddCommand(NewCmdConfigDeleteContext(streams.Out, streams.ErrOut, pathOptions))
cmd.AddCommand(NewCmdConfigRenameContext(streams.Out, pathOptions))
return cmd
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package config
import (
"bytes"
"fmt"
"io/ioutil"
"os"
@ -31,6 +30,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func newRedFederalCowHammerConfig() clientcmdapi.Config {
@ -863,9 +863,8 @@ func testConfigCommand(args []string, startingConfig clientcmdapi.Config, t *tes
argsToUse = append(argsToUse, "--kubeconfig="+fakeKubeFile.Name())
argsToUse = append(argsToUse, args...)
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdConfig(cmdutil.NewFactory(cmdutil.NewTestConfigFlags()), clientcmd.NewDefaultPathOptions(), buf, buf)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdConfig(cmdutil.NewFactory(cmdutil.NewTestConfigFlags()), clientcmd.NewDefaultPathOptions(), streams)
cmd.SetArgs(argsToUse)
cmd.Execute()

View File

@ -0,0 +1,87 @@
/*
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 config
import (
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/printers"
)
// kubectlConfigPrintFlags composes common printer flag structs
// used across all config commands, and provides a method
// of retrieving a known printer based on flag values provided.
type kubectlConfigPrintFlags struct {
JSONYamlPrintFlags *printers.JSONYamlPrintFlags
NamePrintFlags *printers.NamePrintFlags
TemplateFlags *printers.KubeTemplatePrintFlags
OutputFormat *string
}
func (f *kubectlConfigPrintFlags) Complete(successTemplate string) error {
return f.NamePrintFlags.Complete(successTemplate)
}
func (f *kubectlConfigPrintFlags) ToPrinter() (printers.ResourcePrinter, error) {
outputFormat := ""
if f.OutputFormat != nil {
outputFormat = *f.OutputFormat
}
if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
if p, err := f.TemplateFlags.ToPrinter(outputFormat); !printers.IsNoCompatiblePrinterError(err) {
return p, err
}
return nil, printers.NoCompatiblePrinterError{Options: f}
}
func (f *kubectlConfigPrintFlags) AddFlags(cmd *cobra.Command) {
f.JSONYamlPrintFlags.AddFlags(cmd)
f.NamePrintFlags.AddFlags(cmd)
f.TemplateFlags.AddFlags(cmd)
if f.OutputFormat != nil {
cmd.Flags().StringVarP(f.OutputFormat, "output", "o", *f.OutputFormat, "Output format. One of: json|yaml|name|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].")
}
}
// WithDefaultOutput sets a default output format if one is not provided through a flag value
func (f *kubectlConfigPrintFlags) WithDefaultOutput(output string) *kubectlConfigPrintFlags {
f.OutputFormat = &output
return f
}
func newKubeConfigPrintFlags(operation string) *kubectlConfigPrintFlags {
outputFormat := ""
return &kubectlConfigPrintFlags{
OutputFormat: &outputFormat,
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(operation),
TemplateFlags: printers.NewKubeTemplatePrintFlags(),
}
}

View File

@ -31,6 +31,7 @@ import (
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
@ -41,7 +42,8 @@ type GetContextsOptions struct {
nameOnly bool
showHeaders bool
contextNames []string
out io.Writer
genericclioptions.IOStreams
}
var (
@ -57,8 +59,12 @@ var (
// NewCmdConfigGetContexts creates a command object for the "get-contexts" action, which
// retrieves one or more contexts from a kubeconfig.
func NewCmdConfigGetContexts(out io.Writer, configAccess clientcmd.ConfigAccess) *cobra.Command {
options := &GetContextsOptions{configAccess: configAccess}
func NewCmdConfigGetContexts(streams genericclioptions.IOStreams, configAccess clientcmd.ConfigAccess) *cobra.Command {
options := &GetContextsOptions{
configAccess: configAccess,
IOStreams: streams,
}
cmd := &cobra.Command{
Use: "get-contexts [(-o|--output=)name)]",
@ -74,22 +80,22 @@ func NewCmdConfigGetContexts(out io.Writer, configAccess clientcmd.ConfigAccess)
cmdutil.CheckErr(fmt.Errorf("output must be one of '' or 'name': %v", outputFormat))
}
if !supportedOutputTypes.Has(outputFormat) {
fmt.Fprintf(out, "--output %v is not available in kubectl config get-contexts; resetting to default output format\n", outputFormat)
fmt.Fprintf(options.Out, "--output %v is not available in kubectl config get-contexts; resetting to default output format\n", outputFormat)
cmd.Flags().Set("output", "")
}
cmdutil.CheckErr(options.Complete(cmd, args, out))
cmdutil.CheckErr(options.Complete(cmd, args))
cmdutil.CheckErr(options.RunGetContexts())
},
}
cmdutil.AddOutputFlags(cmd)
cmdutil.AddNoHeadersFlags(cmd)
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
cmd.Flags().StringP("output", "o", "", "Output format. One of: name")
return cmd
}
// Complete assigns GetContextsOptions from the args.
func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string, out io.Writer) error {
func (o *GetContextsOptions) Complete(cmd *cobra.Command, args []string) error {
o.contextNames = args
o.out = out
o.nameOnly = false
if cmdutil.GetFlagString(cmd, "output") == "name" {
o.nameOnly = true
@ -109,9 +115,9 @@ func (o GetContextsOptions) RunGetContexts() error {
return err
}
out, found := o.out.(*tabwriter.Writer)
out, found := o.Out.(*tabwriter.Writer)
if !found {
out = printers.GetNewTabWriter(o.out)
out = printers.GetNewTabWriter(o.Out)
defer out.Flush()
}

View File

@ -17,13 +17,13 @@ limitations under the License.
package config
import (
"bytes"
"io/ioutil"
"os"
"testing"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
type getContextsTest struct {
@ -157,11 +157,11 @@ func (test getContextsTest) run(t *testing.T) {
pathOptions := clientcmd.NewDefaultPathOptions()
pathOptions.GlobalFile = fakeKubeFile.Name()
pathOptions.EnvVar = ""
buf := bytes.NewBuffer([]byte{})
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
options := GetContextsOptions{
configAccess: pathOptions,
}
cmd := NewCmdConfigGetContexts(buf, options.configAccess)
cmd := NewCmdConfigGetContexts(streams, options.configAccess)
if test.nameOnly {
cmd.Flags().Set("output", "name")
}

View File

@ -18,8 +18,6 @@ package config
import (
"errors"
"fmt"
"io"
"github.com/spf13/cobra"
@ -29,16 +27,24 @@ import (
"k8s.io/client-go/tools/clientcmd/api/latest"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
type ViewOptions struct {
PrintFlags *kubectlConfigPrintFlags
PrintObject printers.ResourcePrinterFunc
ConfigAccess clientcmd.ConfigAccess
Merge flag.Tristate
Flatten bool
Minify bool
RawByteData bool
OutputFormat string
genericclioptions.IOStreams
}
var (
@ -56,12 +62,17 @@ var (
# Get the password for the e2e user
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'`)
defaultOutputFormat = "yaml"
)
func NewCmdConfigView(f cmdutil.Factory, out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
options := &ViewOptions{ConfigAccess: ConfigAccess}
// Default to yaml
defaultOutputFormat := "yaml"
func NewCmdConfigView(f cmdutil.Factory, streams genericclioptions.IOStreams, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
o := &ViewOptions{
PrintFlags: newKubeConfigPrintFlags("").WithDefaultOutput("yaml"),
ConfigAccess: ConfigAccess,
IOStreams: streams,
}
cmd := &cobra.Command{
Use: "view",
@ -69,40 +80,48 @@ func NewCmdConfigView(f cmdutil.Factory, out, errOut io.Writer, ConfigAccess cli
Long: view_long,
Example: view_example,
Run: func(cmd *cobra.Command, args []string) {
options.Complete()
outputFormat := cmdutil.GetFlagString(cmd, "output")
if outputFormat == "wide" {
fmt.Fprintf(errOut, "--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat)
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", defaultOutputFormat)
}
if outputFormat == "" {
fmt.Fprintf(errOut, "Reset to default output format (%s) as --output is empty\n", defaultOutputFormat)
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", defaultOutputFormat)
}
printOpts := cmdutil.ExtractCmdPrintOptions(cmd, false)
printer, err := cmdutil.PrinterForOptions(printOpts)
cmdutil.CheckErr(err)
cmdutil.CheckErr(options.Run(out, printer))
cmdutil.CheckErr(o.Complete(cmd))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().Set("output", defaultOutputFormat)
o.PrintFlags.AddFlags(cmd)
options.Merge.Default(true)
mergeFlag := cmd.Flags().VarPF(&options.Merge, "merge", "", "Merge the full hierarchy of kubeconfig files")
o.Merge.Default(true)
mergeFlag := cmd.Flags().VarPF(&o.Merge, "merge", "", "Merge the full hierarchy of kubeconfig files")
mergeFlag.NoOptDefVal = "true"
cmd.Flags().BoolVar(&options.RawByteData, "raw", options.RawByteData, "Display raw byte data")
cmd.Flags().BoolVar(&options.Flatten, "flatten", options.Flatten, "Flatten the resulting kubeconfig file into self-contained output (useful for creating portable kubeconfig files)")
cmd.Flags().BoolVar(&options.Minify, "minify", options.Minify, "Remove all information not used by current-context from the output")
cmd.Flags().BoolVar(&o.RawByteData, "raw", o.RawByteData, "Display raw byte data")
cmd.Flags().BoolVar(&o.Flatten, "flatten", o.Flatten, "Flatten the resulting kubeconfig file into self-contained output (useful for creating portable kubeconfig files)")
cmd.Flags().BoolVar(&o.Minify, "minify", o.Minify, "Remove all information not used by current-context from the output")
return cmd
}
func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error {
func (o *ViewOptions) Complete(cmd *cobra.Command) error {
if o.ConfigAccess.IsExplicitFile() {
if !o.Merge.Provided() {
o.Merge.Set("false")
}
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObject = printer.PrintObj
return nil
}
func (o ViewOptions) Validate() error {
if !o.Merge.Value() && !o.ConfigAccess.IsExplicitFile() {
return errors.New("if merge==false a precise file must to specified")
}
return nil
}
func (o ViewOptions) Run() error {
config, err := o.loadConfig()
if err != nil {
return err
@ -127,22 +146,7 @@ func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error
return err
}
err = printer.PrintObj(convertedObj, out)
if err != nil {
return err
}
return nil
}
func (o *ViewOptions) Complete() bool {
if o.ConfigAccess.IsExplicitFile() {
if !o.Merge.Provided() {
o.Merge.Set("false")
}
}
return true
return o.PrintObject(convertedObj, o.Out)
}
func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) {
@ -155,14 +159,6 @@ func (o ViewOptions) loadConfig() (*clientcmdapi.Config, error) {
return config, err
}
func (o ViewOptions) Validate() error {
if !o.Merge.Value() && !o.ConfigAccess.IsExplicitFile() {
return errors.New("if merge==false a precise file must to specified")
}
return nil
}
// getStartingConfig returns the Config object built from the sources specified by the options, the filename read (only if it was a single file), and an error if something goes wrong
func (o *ViewOptions) getStartingConfig() (*clientcmdapi.Config, error) {
switch {

View File

@ -17,7 +17,6 @@ limitations under the License.
package config
import (
"bytes"
"io/ioutil"
"os"
"testing"
@ -25,6 +24,7 @@ import (
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
type viewClusterTest struct {
@ -141,9 +141,8 @@ func (test viewClusterTest) run(t *testing.T) {
pathOptions := clientcmd.NewDefaultPathOptions()
pathOptions.GlobalFile = fakeKubeFile.Name()
pathOptions.EnvVar = ""
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdConfigView(cmdutil.NewFactory(cmdutil.NewTestConfigFlags()), buf, errBuf, pathOptions)
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdConfigView(cmdutil.NewFactory(cmdutil.NewTestConfigFlags()), streams, pathOptions)
cmd.Flags().Parse(test.flags)
if err := cmd.Execute(); err != nil {
t.Fatalf("unexpected error executing command: %v,kubectl config view flags: %v", err, test.flags)

View File

@ -124,8 +124,6 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Example: delete_example,
Run: func(cmd *cobra.Command, args []string) {
options := deleteFlags.ToOptions(out, errOut)
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
cmdutil.CheckErr(err)
}
@ -185,6 +183,10 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
}
func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
if o.Output != "" && o.Output != "name" {
return cmdutil.UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", o.Output)
}
if o.DeleteAll && len(o.LabelSelector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}

View File

@ -48,9 +48,7 @@ func fakecmd() *cobra.Command {
cmd := &cobra.Command{
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
DisableFlagsInUseLine: true,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
},
Run: func(cmd *cobra.Command, args []string) {},
}
return cmd
}

View File

@ -91,8 +91,15 @@ type ReplaceOptions struct {
}
func NewReplaceOptions(out, errOut io.Writer) *ReplaceOptions {
outputFormat := ""
return &ReplaceOptions{
PrintFlags: printers.NewPrintFlags("replaced"),
// TODO(juanvallejo): figure out why we only support the "name" outputFormat in this command
// we only support "-o name" for this command, so only register the name printer
PrintFlags: &printers.PrintFlags{
OutputFormat: &outputFormat,
NamePrintFlags: printers.NewNamePrintFlags("replaced"),
},
DeleteFlags: NewDeleteFlags("to use to replace the resource."),
Out: out,
@ -110,7 +117,6 @@ func NewCmdReplace(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: replaceLong,
Example: replaceExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run())

View File

@ -93,9 +93,16 @@ type ScaleOptions struct {
}
func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions {
outputFormat := ""
return &ScaleOptions{
// TODO(juanvallejo): figure out why we only support the "name" outputFormat in this command
// we only support "-o name" for this command, so only register the name printer
PrintFlags: &printers.PrintFlags{
OutputFormat: &outputFormat,
NamePrintFlags: printers.NewNamePrintFlags("scaled"),
},
RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("scaled"),
CurrentReplicas: -1,
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
@ -173,9 +180,6 @@ func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
}
func (o *ScaleOptions) Validate(cmd *cobra.Command) error {
if err := cmdutil.ValidateOutputArgs(cmd); err != nil {
return err
}
if o.Replicas < 0 {
return fmt.Errorf("The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0")
}

View File

@ -18,222 +18,10 @@ package util
import (
"fmt"
"io"
"strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
kubectlscheme "k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/kubernetes/pkg/api/legacyscheme"
)
// AddPrinterFlags adds printing related flags to a command (e.g. output format, no headers, template path)
func AddPrinterFlags(cmd *cobra.Command) {
AddNonDeprecatedPrinterFlags(cmd)
cmd.Flags().String("output-version", "", "DEPRECATED: To use a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
cmd.Flags().MarkDeprecated("output-version", "The resource is used exactly as fetched from the API. To get a specific API version, fully-qualify the resource, version, and group (for example: 'jobs.v1.batch/myjob').")
cmd.Flags().MarkHidden("output-version")
}
// AddNonDeprecatedPrinterFlags supports the conversion case which must logically have output-version. Once output-version
// is completely removed, this function can go away.
func AddNonDeprecatedPrinterFlags(cmd *cobra.Command) {
AddOutputFlags(cmd)
AddNoHeadersFlags(cmd)
cmd.Flags().Bool("show-labels", false, "When printing, show all labels as the last column (default hide labels column)")
cmd.Flags().String("template", "", "Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].")
cmd.MarkFlagFilename("template")
cmd.Flags().String("sort-by", "", "If non-empty, sort list types using this field specification. The field specification is expressed as a JSONPath expression (e.g. '{.metadata.name}'). The field in the API resource specified by this JSONPath expression must be an integer or a string.")
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")
}
// AddOutputFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputFlagsForMutation(cmd *cobra.Command) {
cmd.Flags().StringP("output", "o", "", "Output mode. Use \"-o name\" for shorter output (resource/name).")
}
// AddOutputVarFlagsForMutation adds output related flags to a command. Used by mutations only.
func AddOutputVarFlagsForMutation(cmd *cobra.Command, output *string) {
cmd.Flags().StringVarP(output, "output", "o", *output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
}
// AddOutputFlags adds output related flags to a command.
func AddOutputFlags(cmd *cobra.Command) {
cmd.Flags().StringP("output", "o", "", "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].")
cmd.Flags().Bool("allow-missing-template-keys", true, "If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats.")
}
// AddNoHeadersFlags adds no-headers flags to a command.
func AddNoHeadersFlags(cmd *cobra.Command) {
cmd.Flags().Bool("no-headers", false, "When using the default or custom-column output format, don't print headers (default print headers).")
}
// ValidateOutputArgs validates -o flag args for mutations
func ValidateOutputArgs(cmd *cobra.Command) error {
outputMode := GetFlagString(cmd, "output")
if outputMode != "" && outputMode != "name" {
return UsageErrorf(cmd, "Unexpected -o output mode: %v. We only support '-o name'.", outputMode)
}
return nil
}
// PrintSuccess prints a success message and can do a "-o name" as "shortOutput"
// TODO this should really just be a printer. It's got just about the exact same signature.
func PrintSuccess(shortOutput bool, out io.Writer, obj runtime.Object, dryRun bool, operation string) {
dryRunMsg := ""
if dryRun {
dryRunMsg = " (dry run)"
}
// match name printer format
name := "<unknown>"
if acc, err := meta.Accessor(obj); err == nil {
if n := acc.GetName(); len(n) > 0 {
name = n
}
}
// legacy scheme to be sure we work ok with internal types.
// TODO internal types aren't supposed to exist here
groupKind := printers.GetObjectGroupKind(obj, legacyscheme.Scheme)
kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
if len(groupKind.Group) == 0 {
kindString = strings.ToLower(groupKind.Kind)
}
if shortOutput {
// -o name: prints resource/name
fmt.Fprintf(out, "%s/%s\n", kindString, name)
return
}
// understandable output by default
fmt.Fprintf(out, "%s \"%s\" %s%s\n", kindString, name, operation, dryRunMsg)
}
// PrintObject prints a single object based on the default command options
// TODO this should go away once commands can embed the PrintOptions instead
func PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
printer, err := PrinterForOptions(ExtractCmdPrintOptions(cmd, false))
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// PrinterForOptions returns the printer for the outputOptions (if given) or
// returns the default printer for the command.
// TODO this should become a function on the PrintOptions struct
func PrinterForOptions(options *printers.PrintOptions) (printers.ResourcePrinter, error) {
// TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{kubectlscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}
encoder := kubectlscheme.Codecs.LegacyCodec(kubectlscheme.Registry.RegisteredGroupVersions()...)
printer, err := printers.GetStandardPrinter(kubectlscheme.Scheme, encoder, decoders, *options)
if err != nil {
return nil, err
}
// we try to convert to HumanReadablePrinter, if return ok, it must be no generic
// we execute AddHandlers() here before maybeWrapSortingPrinter so that we don't
// need to convert to delegatePrinter again then invoke AddHandlers()
// TODO this looks highly questionable. human readable printers are baked into code. This can just live in the definition of the handler itself
// TODO or be registered there
if humanReadablePrinter, ok := printer.(printers.PrintHandler); ok {
printersinternal.AddHandlers(humanReadablePrinter)
}
printer = maybeWrapSortingPrinter(printer, *options)
// wrap the printer in a versioning printer that understands when to convert and when not to convert
printer = printers.NewVersionedPrinter(printer, legacyscheme.Scheme, legacyscheme.Scheme, kubectlscheme.Versions...)
return printer, nil
}
// ExtractCmdPrintOptions parses printer specific commandline args and
// returns a PrintOptions object.
// Requires that printer flags have been added to cmd (see AddPrinterFlags)
func ExtractCmdPrintOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions {
flags := cmd.Flags()
columnLabel, err := flags.GetStringSlice("label-columns")
if err != nil {
columnLabel = []string{}
}
options := &printers.PrintOptions{
NoHeaders: GetFlagBool(cmd, "no-headers"),
Wide: GetWideFlag(cmd),
ShowAll: GetFlagBool(cmd, "show-all"),
ShowLabels: GetFlagBool(cmd, "show-labels"),
AbsoluteTimestamps: isWatch(cmd),
ColumnLabels: columnLabel,
WithNamespace: withNamespace,
}
var outputFormat string
if flags.Lookup("output") != nil {
outputFormat = GetFlagString(cmd, "output")
}
if flags.Lookup("sort-by") != nil {
options.SortBy = GetFlagString(cmd, "sort-by")
}
// templates are logically optional for specifying a format.
// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
var templateFile string
if flag := flags.Lookup("template"); flag != nil {
if flag.Value.Type() == "string" {
templateFile = GetFlagString(cmd, "template")
}
}
if len(outputFormat) == 0 && len(templateFile) != 0 {
outputFormat = "template"
}
templateFormats := []string{
"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
}
for _, format := range templateFormats {
if strings.HasPrefix(outputFormat, format) {
templateFile = outputFormat[len(format):]
outputFormat = format[:len(format)-1]
}
}
// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
// to be safe about how we access the allow-missing-template-keys flag
if flags.Lookup("allow-missing-template-keys") != nil {
options.AllowMissingKeys = GetFlagBool(cmd, "allow-missing-template-keys")
}
options.OutputFormatType = outputFormat
options.OutputFormatArgument = templateFile
return options
}
func maybeWrapSortingPrinter(printer printers.ResourcePrinter, printOpts printers.PrintOptions) printers.ResourcePrinter {
if len(printOpts.SortBy) != 0 {
return &kubectl.SortingPrinter{
Delegate: printer,
SortField: fmt.Sprintf("{%s}", printOpts.SortBy),
}
}
return printer
}
// SuggestApiResources returns a suggestion to use the "api-resources" command
// to retrieve a supported list of resources
func SuggestApiResources(parent string) string {

View File

@ -21,7 +21,6 @@ go_library(
"kube_template_flags.go",
"name.go",
"name_flags.go",
"printers.go",
"tabwriter.go",
"template.go",
"template_flags.go",

View File

@ -65,12 +65,16 @@ func (f *PrintFlags) ToPrinter() (ResourcePrinter, error) {
outputFormat = *f.OutputFormat
}
if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
return p, err
if f.JSONYamlPrintFlags != nil {
if p, err := f.JSONYamlPrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
return p, err
}
}
if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
return p, err
if f.NamePrintFlags != nil {
if p, err := f.NamePrintFlags.ToPrinter(outputFormat); !IsNoCompatiblePrinterError(err) {
return p, err
}
}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: f.OutputFormat}
@ -87,15 +91,7 @@ func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
// WithDefaultOutput sets a default output format if one is not provided through a flag value
func (f *PrintFlags) WithDefaultOutput(output string) *PrintFlags {
existingFormat := ""
if f.OutputFormat != nil {
existingFormat = *f.OutputFormat
}
if len(existingFormat) == 0 {
existingFormat = output
}
f.OutputFormat = &existingFormat
f.OutputFormat = &output
return f
}

View File

@ -229,78 +229,6 @@ func (obj *TestUnknownType) DeepCopyObject() runtime.Object {
return &clone
}
func TestPrinter(t *testing.T) {
//test inputs
simpleTest := &TestPrintType{"foo"}
podTest := &api.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}
podListTest := &api.PodList{
Items: []api.Pod{
{ObjectMeta: metav1.ObjectMeta{Name: "foo"}},
{ObjectMeta: metav1.ObjectMeta{Name: "bar"}},
},
}
emptyListTest := &api.PodList{}
testapi, err := legacyscheme.Scheme.ConvertToVersion(podTest, schema.GroupVersion{Group: "", Version: "v1"})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
printerTests := []struct {
Name string
PrintOpts *printers.PrintOptions
Input runtime.Object
OutputVersions []schema.GroupVersion
Expect string
}{
{"test json", &printers.PrintOptions{OutputFormatType: "json", AllowMissingKeys: true}, simpleTest, nil, "{\n \"Data\": \"foo\"\n}\n"},
{"test yaml", &printers.PrintOptions{OutputFormatType: "yaml", AllowMissingKeys: true}, simpleTest, nil, "Data: foo\n"},
{"test template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}", AllowMissingKeys: true},
podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
{"test jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
{"test jsonpath list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"},
{"test jsonpath empty list", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""},
{"test name", &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pod/foo\n"},
{"emits versioned objects", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"},
}
for _, test := range printerTests {
buf := bytes.NewBuffer([]byte{})
printer, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.RegisteredGroupVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}
if len(test.OutputVersions) > 0 {
printer = printers.NewVersionedPrinter(printer, legacyscheme.Scheme, legacyscheme.Scheme, test.OutputVersions...)
}
if err := printer.PrintObj(test.Input, buf); err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}
if buf.String() != test.Expect {
t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String())
}
}
}
func TestBadPrinter(t *testing.T) {
badPrinterTests := []struct {
Name string
PrintOpts *printers.PrintOptions
Error error
}{
{"empty template", &printers.PrintOptions{OutputFormatType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
{"bad template", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
{"bad templatefile", &printers.PrintOptions{OutputFormatType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
{"bad jsonpath", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
{"unknown format", &printers.PrintOptions{OutputFormatType: "anUnknownFormat", OutputFormatArgument: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
}
for _, test := range badPrinterTests {
_, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.RegisteredGroupVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err == nil || err.Error() != test.Error.Error() {
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
}
}
}
func testPrinter(t *testing.T, printer printers.ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) {
buf := bytes.NewBuffer([]byte{})
@ -471,8 +399,13 @@ func TestNamePrinter(t *testing.T) {
},
"pod/foo\npod/bar\n"},
}
printOpts := &printers.PrintOptions{OutputFormatType: "name", AllowMissingKeys: false}
printer, _ := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.RegisteredGroupVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *printOpts)
printFlags := printers.NewPrintFlags("").WithDefaultOutput("name")
printer, err := printFlags.ToPrinter()
if err != nil {
t.Fatalf("unexpected err: %v", err)
}
for name, item := range tests {
buff := &bytes.Buffer{}
err := printer.PrintObj(item.obj, buff)
@ -2987,44 +2920,6 @@ func TestPrintPodDisruptionBudget(t *testing.T) {
}
}
func TestAllowMissingKeys(t *testing.T) {
tests := []struct {
Name string
PrintOpts *printers.PrintOptions
Input runtime.Object
Expect string
Error string
}{
{"test template, allow missing keys", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.blarg}}", AllowMissingKeys: true}, &api.Pod{}, "<no value>", ""},
{"test template, strict", &printers.PrintOptions{OutputFormatType: "template", OutputFormatArgument: "{{.blarg}}", AllowMissingKeys: false}, &api.Pod{}, "", `error executing template "{{.blarg}}": template: output:1:2: executing "output" at <.blarg>: map has no entry for key "blarg"`},
{"test jsonpath, allow missing keys", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.blarg}", AllowMissingKeys: true}, &api.Pod{}, "", ""},
{"test jsonpath, strict", &printers.PrintOptions{OutputFormatType: "jsonpath", OutputFormatArgument: "{.blarg}", AllowMissingKeys: false}, &api.Pod{}, "", "error executing jsonpath \"{.blarg}\": blarg is not found\n"},
}
for _, test := range tests {
buf := bytes.NewBuffer([]byte{})
printer, err := printers.GetStandardPrinter(legacyscheme.Scheme, legacyscheme.Codecs.LegacyCodec(legacyscheme.Registry.RegisteredGroupVersions()...), []runtime.Decoder{legacyscheme.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, *test.PrintOpts)
if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}
err = printer.PrintObj(test.Input, buf)
if len(test.Error) == 0 && err != nil {
t.Errorf("in %s, unexpected error: %v", test.Name, err)
continue
}
if len(test.Error) > 0 {
if err == nil {
t.Errorf("in %s, expected to get error: %v", test.Name, test.Error)
} else if e, a := test.Error, err.Error(); e != a {
t.Errorf("in %s, expected error %q, got %q", test.Name, e, a)
}
continue
}
if buf.String() != test.Expect {
t.Errorf("in %s, expect %q, got %q", test.Name, test.Expect, buf.String())
}
}
}
func TestPrintControllerRevision(t *testing.T) {
tests := []struct {
history apps.ControllerRevision

View File

@ -1,89 +0,0 @@
/*
Copyright 2017 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 printers
import (
"fmt"
"k8s.io/apimachinery/pkg/runtime"
)
// GetStandardPrinter takes a format type, an optional format argument. It will return
// a printer or an error. The printer is agnostic to schema versions, so you must
// send arguments to PrintObj in the version you wish them to be shown using a
// VersionedPrinter (typically when generic is true).
func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) {
format, formatArgument, allowMissingTemplateKeys := options.OutputFormatType, options.OutputFormatArgument, options.AllowMissingKeys
var printer ResourcePrinter
switch format {
case "json", "yaml":
jsonYamlFlags := NewJSONYamlPrintFlags()
p, err := jsonYamlFlags.ToPrinter(format)
if err != nil {
return nil, err
}
printer = p
case "name":
nameFlags := NewNamePrintFlags("")
namePrinter, err := nameFlags.ToPrinter(format)
if err != nil {
return nil, err
}
printer = namePrinter
case "template", "go-template", "jsonpath", "templatefile", "go-template-file", "jsonpath-file":
// TODO: construct and bind this separately (at the command level)
kubeTemplateFlags := KubeTemplatePrintFlags{
GoTemplatePrintFlags: &GoTemplatePrintFlags{
AllowMissingKeys: &allowMissingTemplateKeys,
TemplateArgument: &formatArgument,
},
JSONPathPrintFlags: &JSONPathPrintFlags{
AllowMissingKeys: &allowMissingTemplateKeys,
TemplateArgument: &formatArgument,
},
}
kubeTemplatePrinter, err := kubeTemplateFlags.ToPrinter(format)
if err != nil {
return nil, err
}
printer = kubeTemplatePrinter
case "custom-columns", "custom-columns-file":
customColumnsFlags := &CustomColumnsPrintFlags{
NoHeaders: options.NoHeaders,
TemplateArgument: formatArgument,
}
customColumnsPrinter, err := customColumnsFlags.ToPrinter(format)
if err != nil {
return nil, err
}
printer = customColumnsPrinter
default:
return nil, fmt.Errorf("output format %q not recognized", format)
}
return printer, nil
}