Merge pull request #62144 from juanvallejo/jvallejo/wire-print-flags-set-cmds

Automatic merge from submit-queue (batch tested with PRs 62208, 62114, 62144, 60460, 62214). 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 printflags through set commands

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

~~Depends on https://github.com/kubernetes/kubernetes/pull/62139
Opening now to test with CI~~

Wires PrintFlags through all `kubectl set` commands.

cc @soltysh @deads2k
pull/8/head
Kubernetes Submit Queue 2018-04-06 17:06:12 -07:00 committed by GitHub
commit 115f80a9b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 274 additions and 103 deletions

View File

@ -215,13 +215,17 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
o.Resources = resources o.Resources = resources
o.Cmd = cmd o.Cmd = cmd
o.PrintFlags.Complete(o.DryRun) if o.DryRun {
// TODO(juanvallejo): This can be cleaned up even further by creating
// a PrintFlags struct that binds the --dry-run flag, and whose
// ToPrinter method returns a printer that understands how to print
// this success message.
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
return err return err
} }
o.PrintObj = func(obj runtime.Object) error { o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out) return printer.PrintObj(obj, o.Out)
} }

View File

@ -74,7 +74,7 @@ func TestSetEnvLocal(t *testing.T) {
opts := EnvOptions{ opts := EnvOptions{
PrintFlags: &printers.PrintFlags{ PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(), JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags("", false), NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat, OutputFormat: &outputFormat,
}, },
@ -123,7 +123,7 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
opts := EnvOptions{ opts := EnvOptions{
PrintFlags: &printers.PrintFlags{ PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(), JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags("", false), NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat, OutputFormat: &outputFormat,
}, },
@ -514,7 +514,7 @@ func TestSetEnvRemote(t *testing.T) {
opts := EnvOptions{ opts := EnvOptions{
PrintFlags: &printers.PrintFlags{ PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(), JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags("", false), NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat, OutputFormat: &outputFormat,
}, },

View File

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"io" "io"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -36,12 +38,13 @@ import (
type ImageOptions struct { type ImageOptions struct {
resource.FilenameOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
Infos []*resource.Info Infos []*resource.Info
Selector string Selector string
Out io.Writer Out io.Writer
Err io.Writer Err io.Writer
DryRun bool DryRun bool
ShortOutput bool
All bool All bool
Record bool Record bool
Output string Output string
@ -50,6 +53,8 @@ type ImageOptions struct {
Cmd *cobra.Command Cmd *cobra.Command
ResolveImage func(in string) (string, error) ResolveImage func(in string) (string, error)
PrintObj func(runtime.Object) error
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
Resources []string Resources []string
ContainerImages map[string]string ContainerImages map[string]string
@ -81,6 +86,8 @@ var (
func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command { func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &ImageOptions{ options := &ImageOptions{
PrintFlags: printers.NewPrintFlags("image updated"),
Out: out, Out: out,
Err: err, Err: err,
} }
@ -98,7 +105,8 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
}, },
} }
cmdutil.AddPrinterFlags(cmd) options.PrintFlags.AddFlags(cmd)
usage := "identifying the resource to get from a server." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
@ -112,7 +120,6 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.ShortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.Record = cmdutil.GetRecordFlag(cmd) o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command(cmd, false) o.ChangeCause = f.Command(cmd, false)
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd)
@ -120,6 +127,19 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
o.ResolveImage = f.ResolveImage o.ResolveImage = f.ResolveImage
o.Cmd = cmd o.Cmd = cmd
if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
@ -238,7 +258,7 @@ func (o *ImageOptions) Run() error {
} }
if o.Local || o.DryRun { if o.Local || o.DryRun {
if err := cmdutil.PrintObject(o.Cmd, patch.Info.AsVersioned(), o.Out); err != nil { if err := o.PrintObj(patch.Info.AsVersioned()); err != nil {
return err return err
} }
continue continue
@ -263,13 +283,9 @@ func (o *ImageOptions) Run() error {
info.Refresh(obj, true) info.Refresh(obj, true)
if len(o.Output) > 0 { if err := o.PrintObj(info.AsVersioned()); err != nil {
if err := cmdutil.PrintObject(o.Cmd, info.AsVersioned(), o.Out); err != nil { return err
return err
}
continue
} }
cmdutil.PrintSuccess(o.ShortOutput, o.Out, info.Object, o.DryRun, "image updated")
} }
return utilerrors.NewAggregate(allErrs) return utilerrors.NewAggregate(allErrs)
} }

View File

@ -25,6 +25,8 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/kubernetes/pkg/printers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
@ -61,14 +63,23 @@ func TestImageLocal(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdImage(tf, buf, buf) cmd := NewCmdImage(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{ opts := ImageOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}},
Out: buf, Out: buf,
Local: true} Local: true}
err := opts.Complete(tf, cmd, []string{"cassandra=thingy"}) err := opts.Complete(tf, cmd, []string{"cassandra=thingy"})
@ -87,6 +98,11 @@ func TestImageLocal(t *testing.T) {
} }
func TestSetImageValidation(t *testing.T) { func TestSetImageValidation(t *testing.T) {
printFlags := &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
}
testCases := []struct { testCases := []struct {
name string name string
imageOptions *ImageOptions imageOptions *ImageOptions
@ -94,13 +110,14 @@ func TestSetImageValidation(t *testing.T) {
}{ }{
{ {
name: "test resource < 1 and filenames empty", name: "test resource < 1 and filenames empty",
imageOptions: &ImageOptions{}, imageOptions: &ImageOptions{PrintFlags: printFlags},
expectErr: "[one or more resources must be specified as <resource> <name> or <resource>/<name>, at least one image update is required]", expectErr: "[one or more resources must be specified as <resource> <name> or <resource>/<name>, at least one image update is required]",
}, },
{ {
name: "test containerImages < 1", name: "test containerImages < 1",
imageOptions: &ImageOptions{ imageOptions: &ImageOptions{
Resources: []string{"a", "b", "c"}, PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
FilenameOptions: resource.FilenameOptions{ FilenameOptions: resource.FilenameOptions{
Filenames: []string{"testFile"}, Filenames: []string{"testFile"},
@ -111,7 +128,8 @@ func TestSetImageValidation(t *testing.T) {
{ {
name: "test containerImages > 1 and all containers are already specified by *", name: "test containerImages > 1 and all containers are already specified by *",
imageOptions: &ImageOptions{ imageOptions: &ImageOptions{
Resources: []string{"a", "b", "c"}, PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
FilenameOptions: resource.FilenameOptions{ FilenameOptions: resource.FilenameOptions{
Filenames: []string{"testFile"}, Filenames: []string{"testFile"},
}, },
@ -125,7 +143,8 @@ func TestSetImageValidation(t *testing.T) {
{ {
name: "success case", name: "success case",
imageOptions: &ImageOptions{ imageOptions: &ImageOptions{
Resources: []string{"a", "b", "c"}, PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
FilenameOptions: resource.FilenameOptions{ FilenameOptions: resource.FilenameOptions{
Filenames: []string{"testFile"}, Filenames: []string{"testFile"},
}, },
@ -166,14 +185,23 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdImage(tf, buf, buf) cmd := NewCmdImage(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{ opts := ImageOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
Out: buf, Out: buf,
Local: true} Local: true}
err := opts.Complete(tf, cmd, []string{"*=thingy"}) err := opts.Complete(tf, cmd, []string{"*=thingy"})
@ -552,11 +580,21 @@ func TestSetImageRemote(t *testing.T) {
}), }),
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()), VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
} }
outputFormat := "yaml"
out := new(bytes.Buffer) out := new(bytes.Buffer)
cmd := NewCmdImage(tf, out, out) cmd := NewCmdImage(tf, out, out)
cmd.SetOutput(out) cmd.SetOutput(out)
cmd.Flags().Set("output", "yaml") cmd.Flags().Set("output", outputFormat)
opts := ImageOptions{ opts := ImageOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
Out: out, Out: out,
Local: false} Local: false}
err := opts.Complete(tf, cmd, input.args) err := opts.Complete(tf, cmd, input.args)

View File

@ -21,6 +21,8 @@ import (
"io" "io"
"strings" "strings"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
@ -61,6 +63,8 @@ var (
type ResourcesOptions struct { type ResourcesOptions struct {
resource.FilenameOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
Infos []*resource.Info Infos []*resource.Info
Out io.Writer Out io.Writer
Err io.Writer Err io.Writer
@ -73,6 +77,10 @@ type ResourcesOptions struct {
Local bool Local bool
Cmd *cobra.Command Cmd *cobra.Command
DryRun bool
PrintObj func(runtime.Object) error
Limits string Limits string
Requests string Requests string
ResourceRequirements v1.ResourceRequirements ResourceRequirements v1.ResourceRequirements
@ -85,6 +93,8 @@ type ResourcesOptions struct {
// pod templates are selected by default. // pod templates are selected by default.
func NewResourcesOptions(out io.Writer, errOut io.Writer) *ResourcesOptions { func NewResourcesOptions(out io.Writer, errOut io.Writer) *ResourcesOptions {
return &ResourcesOptions{ return &ResourcesOptions{
PrintFlags: printers.NewPrintFlags("resource requirements updated"),
Out: out, Out: out,
Err: errOut, Err: errOut,
ContainerSelector: "*", ContainerSelector: "*",
@ -112,7 +122,8 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.
}, },
} }
cmdutil.AddPrinterFlags(cmd) options.PrintFlags.AddFlags(cmd)
//usage := "Filename, directory, or URL to a file identifying the resource to get from the server" //usage := "Filename, directory, or URL to a file identifying the resource to get from the server"
//kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) //kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
usage := "identifying the resource to get from a server." usage := "identifying the resource to get from a server."
@ -135,6 +146,18 @@ func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
o.Record = cmdutil.GetRecordFlag(cmd) o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command(cmd, false) o.ChangeCause = f.Command(cmd, false)
o.Cmd = cmd o.Cmd = cmd
o.DryRun = cmdutil.GetDryRunFlag(o.Cmd)
if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
@ -238,8 +261,8 @@ func (o *ResourcesOptions) Run() error {
continue continue
} }
if o.Local || cmdutil.GetDryRunFlag(o.Cmd) { if o.Local || o.DryRun {
if err := cmdutil.PrintObject(o.Cmd, patch.Info.AsVersioned(), o.Out); err != nil { if err := o.PrintObj(patch.Info.AsVersioned()); err != nil {
return err return err
} }
continue continue
@ -262,14 +285,9 @@ func (o *ResourcesOptions) Run() error {
} }
info.Refresh(obj, true) info.Refresh(obj, true)
shortOutput := o.Output == "name" if err := o.PrintObj(info.AsVersioned()); err != nil {
if len(o.Output) > 0 && !shortOutput { return err
if err := cmdutil.PrintObject(o.Cmd, info.AsVersioned(), o.Out); err != nil {
return err
}
continue
} }
cmdutil.PrintSuccess(shortOutput, o.Out, info.Object, false, "resource requirements updated")
} }
return utilerrors.NewAggregate(allErrs) return utilerrors.NewAggregate(allErrs)
} }

View File

@ -25,6 +25,8 @@ import (
"strings" "strings"
"testing" "testing"
"k8s.io/kubernetes/pkg/printers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
@ -60,14 +62,23 @@ func TestResourcesLocal(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdResources(tf, buf, buf) cmd := NewCmdResources(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{ opts := ResourcesOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}},
Out: buf, Out: buf,
Local: true, Local: true,
Limits: "cpu=200m,memory=512Mi", Limits: "cpu=200m,memory=512Mi",
@ -105,14 +116,23 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdResources(tf, buf, buf) cmd := NewCmdResources(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
opts := ResourcesOptions{FilenameOptions: resource.FilenameOptions{ opts := ResourcesOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
Out: buf, Out: buf,
Local: true, Local: true,
Limits: "cpu=200m,memory=512Mi", Limits: "cpu=200m,memory=512Mi",
@ -478,11 +498,21 @@ func TestSetResourcesRemote(t *testing.T) {
}), }),
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()), VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
} }
outputFormat := "yaml"
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
cmd := NewCmdResources(tf, buf, buf) cmd := NewCmdResources(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "yaml") cmd.Flags().Set("output", outputFormat)
opts := ResourcesOptions{ opts := ResourcesOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
Out: buf, Out: buf,
Limits: "cpu=200m,memory=512Mi", Limits: "cpu=200m,memory=512Mi",
ContainerSelector: "*"} ContainerSelector: "*"}

View File

@ -20,6 +20,8 @@ import (
"fmt" "fmt"
"io" "io"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
@ -38,6 +40,8 @@ import (
type SelectorOptions struct { type SelectorOptions struct {
fileOptions resource.FilenameOptions fileOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
local bool local bool
dryrun bool dryrun bool
all bool all bool
@ -49,9 +53,10 @@ type SelectorOptions struct {
selector *metav1.LabelSelector selector *metav1.LabelSelector
out io.Writer out io.Writer
PrintObject func(obj runtime.Object) error
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
PrintObj func(runtime.Object) error
builder *resource.Builder builder *resource.Builder
mapper meta.RESTMapper mapper meta.RESTMapper
} }
@ -73,6 +78,8 @@ var (
// NewCmdSelector is the "set selector" command. // NewCmdSelector is the "set selector" command.
func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &SelectorOptions{ options := &SelectorOptions{
PrintFlags: printers.NewPrintFlags("selector updated"),
out: out, out: out,
} }
@ -88,7 +95,9 @@ func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmdutil.CheckErr(options.RunSelector()) cmdutil.CheckErr(options.RunSelector())
}, },
} }
cmdutil.AddPrinterFlags(cmd)
options.PrintFlags.AddFlags(cmd)
cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().Bool("local", false, "If true, set selector will NOT contact api-server but run locally.") cmd.Flags().Bool("local", false, "If true, set selector will NOT contact api-server but run locally.")
cmd.Flags().String("resource-version", "", "If non-empty, the selectors update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.") cmd.Flags().String("resource-version", "", "If non-empty, the selectors update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.")
@ -146,9 +155,17 @@ func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
} }
} }
o.PrintObject = func(obj runtime.Object) error { if o.dryrun {
return cmdutil.PrintObject(cmd, obj, o.out) o.PrintFlags.Complete("%s (dry run)")
} }
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.out)
}
o.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) { o.ClientForMapping = func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.ClientForMapping(mapping) return f.ClientForMapping(mapping)
} }
@ -191,7 +208,7 @@ func (o *SelectorOptions) RunSelector() error {
return patch.Err return patch.Err
} }
if o.local || o.dryrun { if o.local || o.dryrun {
return o.PrintObject(info.Object) return o.PrintObj(info.Object)
} }
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch)
@ -208,13 +225,7 @@ func (o *SelectorOptions) RunSelector() error {
} }
info.Refresh(patched, true) info.Refresh(patched, true)
return o.PrintObj(patch.Info.AsVersioned())
shortOutput := o.output == "name"
if len(o.output) > 0 && !shortOutput {
return o.PrintObject(patched)
}
cmdutil.PrintSuccess(shortOutput, o.out, info.Object, o.dryrun, "selector updated")
return nil
}) })
} }

View File

@ -21,6 +21,8 @@ import (
"fmt" "fmt"
"io" "io"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
@ -54,6 +56,8 @@ var (
// serviceAccountConfig encapsulates the data required to perform the operation. // serviceAccountConfig encapsulates the data required to perform the operation.
type serviceAccountConfig struct { type serviceAccountConfig struct {
PrintFlags *printers.PrintFlags
fileNameOptions resource.FilenameOptions fileNameOptions resource.FilenameOptions
out io.Writer out io.Writer
err io.Writer err io.Writer
@ -68,11 +72,15 @@ type serviceAccountConfig struct {
updatePodSpecForObject func(runtime.Object, func(*v1.PodSpec) error) (bool, error) updatePodSpecForObject func(runtime.Object, func(*v1.PodSpec) error) (bool, error)
infos []*resource.Info infos []*resource.Info
serviceAccountName string serviceAccountName string
PrintObj func(runtime.Object) error
} }
// NewCmdServiceAccount returns the "set serviceaccount" command. // NewCmdServiceAccount returns the "set serviceaccount" command.
func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command { func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
saConfig := &serviceAccountConfig{ saConfig := &serviceAccountConfig{
PrintFlags: printers.NewPrintFlags("serviceaccount updated"),
out: out, out: out,
err: err, err: err,
} }
@ -89,7 +97,8 @@ func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command
cmdutil.CheckErr(saConfig.Run()) cmdutil.CheckErr(saConfig.Run())
}, },
} }
cmdutil.AddPrinterFlags(cmd)
saConfig.PrintFlags.AddFlags(cmd)
usage := "identifying the resource to get from a server." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &saConfig.fileNameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &saConfig.fileNameOptions, usage)
@ -111,6 +120,17 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
saConfig.updatePodSpecForObject = f.UpdatePodSpecForObject saConfig.updatePodSpecForObject = f.UpdatePodSpecForObject
saConfig.cmd = cmd saConfig.cmd = cmd
if saConfig.dryRun {
saConfig.PrintFlags.Complete("%s (dry run)")
}
printer, err := saConfig.PrintFlags.ToPrinter()
if err != nil {
return err
}
saConfig.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, saConfig.out)
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
@ -151,6 +171,7 @@ func (saConfig *serviceAccountConfig) Run() error {
}) })
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object) return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
} }
patches := CalculatePatches(saConfig.infos, cmdutil.InternalVersionJSONEncoder(), patchFn) patches := CalculatePatches(saConfig.infos, cmdutil.InternalVersionJSONEncoder(), patchFn)
for _, patch := range patches { for _, patch := range patches {
info := patch.Info info := patch.Info
@ -159,7 +180,7 @@ func (saConfig *serviceAccountConfig) Run() error {
continue continue
} }
if saConfig.local || saConfig.dryRun { if saConfig.local || saConfig.dryRun {
if err := cmdutil.PrintObject(saConfig.cmd, patch.Info.AsVersioned(), saConfig.out); err != nil { if err := saConfig.PrintObj(patch.Info.AsVersioned()); err != nil {
return err return err
} }
continue continue
@ -177,13 +198,10 @@ func (saConfig *serviceAccountConfig) Run() error {
} }
} }
} }
if len(saConfig.output) > 0 {
if err := cmdutil.PrintObject(saConfig.cmd, info.AsVersioned(), saConfig.out); err != nil { if err := saConfig.PrintObj(info.AsVersioned()); err != nil {
return err return err
}
continue
} }
cmdutil.PrintSuccess(saConfig.shortOutput, saConfig.out, info.Object, saConfig.dryRun, "serviceaccount updated")
} }
return utilerrors.NewAggregate(patchErrs) return utilerrors.NewAggregate(patchErrs)
} }

View File

@ -25,6 +25,8 @@ import (
"path" "path"
"testing" "testing"
"k8s.io/kubernetes/pkg/printers"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
appsv1 "k8s.io/api/apps/v1" appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1" appsv1beta1 "k8s.io/api/apps/v1beta1"
@ -77,15 +79,25 @@ func TestSetServiceAccountLocal(t *testing.T) {
return nil, nil return nil, nil
}), }),
} }
outputFormat := "yaml"
tf.Namespace = "test" tf.Namespace = "test"
out := new(bytes.Buffer) out := new(bytes.Buffer)
cmd := NewCmdServiceAccount(tf, out, out) cmd := NewCmdServiceAccount(tf, out, out)
cmd.SetOutput(out) cmd.SetOutput(out)
cmd.Flags().Set("output", "yaml") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
testapi.Default = testapi.Groups[input.apiGroup] testapi.Default = testapi.Groups[input.apiGroup]
saConfig := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{ saConfig := serviceAccountConfig{
Filenames: []string{input.yaml}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
fileNameOptions: resource.FilenameOptions{
Filenames: []string{input.yaml}},
out: out, out: out,
local: true} local: true}
err := saConfig.Complete(tf, cmd, []string{serviceAccount}) err := saConfig.Complete(tf, cmd, []string{serviceAccount})
@ -114,13 +126,22 @@ func TestSetServiceAccountMultiLocal(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := NewCmdServiceAccount(tf, buf, buf) cmd := NewCmdServiceAccount(tf, buf, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "name") cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true") cmd.Flags().Set("local", "true")
opts := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{ opts := serviceAccountConfig{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}}, PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
fileNameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
out: buf, out: buf,
local: true} local: true}
@ -349,11 +370,21 @@ func TestSetServiceAccountRemote(t *testing.T) {
}), }),
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()), VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
} }
outputFormat := "yaml"
out := new(bytes.Buffer) out := new(bytes.Buffer)
cmd := NewCmdServiceAccount(tf, out, out) cmd := NewCmdServiceAccount(tf, out, out)
cmd.SetOutput(out) cmd.SetOutput(out)
cmd.Flags().Set("output", "yaml") cmd.Flags().Set("output", outputFormat)
saConfig := serviceAccountConfig{ saConfig := serviceAccountConfig{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
out: out, out: out,
local: false} local: false}
err := saConfig.Complete(tf, cmd, input.args) err := saConfig.Complete(tf, cmd, input.args)
@ -385,12 +416,22 @@ func TestServiceAccountValidation(t *testing.T) {
return nil, nil return nil, nil
}), }),
} }
outputFormat := ""
tf.Namespace = "test" tf.Namespace = "test"
out := bytes.NewBuffer([]byte{}) out := bytes.NewBuffer([]byte{})
cmd := NewCmdServiceAccount(tf, out, out) cmd := NewCmdServiceAccount(tf, out, out)
cmd.SetOutput(out) cmd.SetOutput(out)
saConfig := &serviceAccountConfig{} saConfig := &serviceAccountConfig{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
}
err := saConfig.Complete(tf, cmd, input.args) err := saConfig.Complete(tf, cmd, input.args)
assert.EqualError(t, err, input.errorString) assert.EqualError(t, err, input.errorString)
}) })

View File

@ -21,6 +21,8 @@ import (
"io" "io"
"strings" "strings"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -54,6 +56,8 @@ type updateSubjects func(existings []rbac.Subject, targets []rbac.Subject) (bool
// SubjectOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of // SubjectOptions 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 // referencing the cmd.Flags
type SubjectOptions struct { type SubjectOptions struct {
PrintFlags *printers.PrintFlags
resource.FilenameOptions resource.FilenameOptions
Infos []*resource.Info Infos []*resource.Info
@ -70,11 +74,13 @@ type SubjectOptions struct {
Groups []string Groups []string
ServiceAccounts []string ServiceAccounts []string
PrintObject func(obj runtime.Object, out io.Writer) error PrintObj func(obj runtime.Object) error
} }
func NewCmdSubject(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command { func NewCmdSubject(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
options := &SubjectOptions{ options := &SubjectOptions{
PrintFlags: printers.NewPrintFlags("subjects updated"),
Out: out, Out: out,
Err: errOut, Err: errOut,
} }
@ -92,7 +98,8 @@ func NewCmdSubject(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Co
}, },
} }
cmdutil.AddPrinterFlags(cmd) options.PrintFlags.AddFlags(cmd)
usage := "the resource to update the subjects" usage := "the resource to update the subjects"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
@ -109,8 +116,16 @@ func NewCmdSubject(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Co
func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.Output = cmdutil.GetFlagString(cmd, "output") o.Output = cmdutil.GetFlagString(cmd, "output")
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.PrintObject = func(obj runtime.Object, out io.Writer) error {
return cmdutil.PrintObject(cmd, obj, out) if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
} }
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
@ -236,7 +251,7 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
} }
if o.Local || o.DryRun { if o.Local || o.DryRun {
if err := o.PrintObject(info.Object, o.Out); err != nil { if err := o.PrintObj(info.Object); err != nil {
return err return err
} }
continue continue
@ -249,11 +264,7 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
} }
info.Refresh(obj, true) info.Refresh(obj, true)
shortOutput := o.Output == "name" return o.PrintObj(info.AsVersioned())
if len(o.Output) > 0 && !shortOutput {
return o.PrintObject(info.AsVersioned(), o.Out)
}
cmdutil.PrintSuccess(shortOutput, o.Out, info.Object, false, "subjects updated")
} }
return utilerrors.NewAggregate(allErrs) return utilerrors.NewAggregate(allErrs)
} }

View File

@ -45,8 +45,8 @@ type PrintFlags struct {
OutputFormat *string OutputFormat *string
} }
func (f *PrintFlags) Complete(dryRun bool) error { func (f *PrintFlags) Complete(messageTemplate string) error {
f.NamePrintFlags.DryRun = dryRun f.NamePrintFlags.Operation = fmt.Sprintf(messageTemplate, f.NamePrintFlags.Operation)
return nil return nil
} }
@ -80,6 +80,6 @@ func NewPrintFlags(operation string) *PrintFlags {
OutputFormat: &outputFormat, OutputFormat: &outputFormat,
JSONYamlPrintFlags: NewJSONYamlPrintFlags(), JSONYamlPrintFlags: NewJSONYamlPrintFlags(),
NamePrintFlags: NewNamePrintFlags(operation, false), NamePrintFlags: NewNamePrintFlags(operation),
} }
} }

View File

@ -31,10 +31,6 @@ import (
// a resource's fully-qualified Kind.group/name, or a successful // a resource's fully-qualified Kind.group/name, or a successful
// message about that resource if an Operation is provided. // message about that resource if an Operation is provided.
type NamePrintFlags struct { type NamePrintFlags struct {
// DryRun indicates whether the "(dry run)" message
// should be appended to the finalized "successful"
// message printed about an action on an object.
DryRun bool
// Operation describes the name of the action that // Operation describes the name of the action that
// took place on an object, to be included in the // took place on an object, to be included in the
// finalized "successful" message. // finalized "successful" message.
@ -53,10 +49,6 @@ func (f *NamePrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, error)
Decoders: decoders, Decoders: decoders,
} }
if f.DryRun {
namePrinter.Operation = namePrinter.Operation + " (dry run)"
}
outputFormat = strings.ToLower(outputFormat) outputFormat = strings.ToLower(outputFormat)
switch outputFormat { switch outputFormat {
case "name": case "name":
@ -75,9 +67,8 @@ func (f *NamePrintFlags) AddFlags(c *cobra.Command) {}
// NewNamePrintFlags returns flags associated with // NewNamePrintFlags returns flags associated with
// --name printing, with default values set. // --name printing, with default values set.
func NewNamePrintFlags(operation string, dryRun bool) *NamePrintFlags { func NewNamePrintFlags(operation string) *NamePrintFlags {
return &NamePrintFlags{ return &NamePrintFlags{
Operation: operation, Operation: operation,
DryRun: dryRun,
} }
} }

View File

@ -49,12 +49,6 @@ func TestNamePrinterSupportsExpectedFormats(t *testing.T) {
operation: "patched", operation: "patched",
expectedOutput: "pod/foo", expectedOutput: "pod/foo",
}, },
{
name: "empty output format and an operation prints success message with dry run",
operation: "patched",
dryRun: true,
expectedOutput: "pod/foo patched (dry run)",
},
{ {
name: "operation and no valid \"name\" output does not match a printer", name: "operation and no valid \"name\" output does not match a printer",
operation: "patched", operation: "patched",
@ -83,7 +77,6 @@ func TestNamePrinterSupportsExpectedFormats(t *testing.T) {
t.Run(tc.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
printFlags := printers.NamePrintFlags{ printFlags := printers.NamePrintFlags{
Operation: tc.operation, Operation: tc.operation,
DryRun: tc.dryRun,
} }
p, err := printFlags.ToPrinter(tc.outputFormat) p, err := printFlags.ToPrinter(tc.outputFormat)

View File

@ -42,7 +42,7 @@ func GetStandardPrinter(typer runtime.ObjectTyper, encoder runtime.Encoder, deco
printer = p printer = p
case "name": case "name":
nameFlags := NewNamePrintFlags("", false) nameFlags := NewNamePrintFlags("")
namePrinter, err := nameFlags.ToPrinter(format) namePrinter, err := nameFlags.ToPrinter(format)
if err != nil { if err != nil {
return nil, err return nil, err