From f432ebe262eefa3b2b25c76eda87af51b892fb2a Mon Sep 17 00:00:00 2001 From: juanvallejo Date: Tue, 24 Apr 2018 00:40:35 -0400 Subject: [PATCH] finish wiring PrintFlags --- pkg/kubectl/cmd/cmd.go | 8 +- pkg/kubectl/cmd/rollingupdate.go | 283 +++++++++++------- pkg/kubectl/cmd/rollingupdate_test.go | 5 +- pkg/kubectl/cmd/rollout/BUILD | 3 + pkg/kubectl/cmd/rollout/rollout.go | 19 +- pkg/kubectl/cmd/rollout/rollout_pause.go | 47 ++- pkg/kubectl/cmd/rollout/rollout_pause_test.go | 7 +- pkg/kubectl/cmd/rollout/rollout_resume.go | 48 ++- pkg/kubectl/cmd/rollout/rollout_status.go | 82 +++-- pkg/kubectl/cmd/rollout/rollout_undo.go | 34 ++- pkg/kubectl/cmd/scale.go | 153 ++++++---- pkg/kubectl/cmd/taint.go | 57 ++-- pkg/kubectl/cmd/taint_test.go | 5 +- 13 files changed, 496 insertions(+), 255 deletions(-) diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 3f23fbe54d..c8f54336bc 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -273,9 +273,9 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob { Message: "Deploy Commands:", Commands: []*cobra.Command{ - rollout.NewCmdRollout(f, out, err), - NewCmdRollingUpdate(f, out), - NewCmdScale(f, out, err), + rollout.NewCmdRollout(f, ioStreams), + NewCmdRollingUpdate(f, ioStreams), + NewCmdScale(f, ioStreams), NewCmdAutoscale(f, ioStreams), }, }, @@ -288,7 +288,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob NewCmdCordon(f, ioStreams), NewCmdUncordon(f, ioStreams), NewCmdDrain(f, ioStreams), - NewCmdTaint(f, out), + NewCmdTaint(f, ioStreams), }, }, { diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index 748a4ecb89..922948a6a0 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -19,11 +19,9 @@ package cmd import ( "bytes" "fmt" - "io" "time" "github.com/golang/glog" - "github.com/spf13/cobra" "k8s.io/api/core/v1" @@ -31,14 +29,19 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/apimachinery/pkg/util/intstr" + scaleclient "k8s.io/client-go/scale" "k8s.io/kubernetes/pkg/api/legacyscheme" api "k8s.io/kubernetes/pkg/apis/core" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" "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/kubectl/util" "k8s.io/kubernetes/pkg/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/kubectl/validation" + "k8s.io/kubernetes/pkg/printers" ) var ( @@ -75,8 +78,55 @@ var ( pollInterval, _ = time.ParseDuration("3s") ) -func NewCmdRollingUpdate(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &resource.FilenameOptions{} +type RollingUpdateOptions struct { + FilenameOptions *resource.FilenameOptions + + OldName string + KeepOldName bool + + DeploymentKey string + Image string + Container string + PullPolicy string + Rollback bool + Period time.Duration + Timeout time.Duration + Interval time.Duration + DryRun bool + OutputFormat string + Namespace string + EnforceNamespace bool + + ScaleClient scaleclient.ScalesGetter + ClientSet internalclientset.Interface + Builder *resource.Builder + + ShouldValidate bool + Validator func(bool) (validation.Schema, error) + + FindNewName func(*api.ReplicationController) string + + PrintFlags *printers.PrintFlags + ToPrinter func(string) (printers.ResourcePrinterFunc, error) + + genericclioptions.IOStreams +} + +func NewRollingUpdateOptions(streams genericclioptions.IOStreams) *RollingUpdateOptions { + return &RollingUpdateOptions{ + PrintFlags: printers.NewPrintFlags("rolling updated"), + FilenameOptions: &resource.FilenameOptions{}, + DeploymentKey: "deployment", + Timeout: timeout, + Interval: pollInterval, + Period: updatePeriod, + + IOStreams: streams, + } +} + +func NewCmdRollingUpdate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { + o := NewRollingUpdateOptions(ioStreams) cmd := &cobra.Command{ Use: "rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f NEW_CONTROLLER_SPEC)", @@ -87,23 +137,26 @@ func NewCmdRollingUpdate(f cmdutil.Factory, out io.Writer) *cobra.Command { Deprecated: `use "rollout" instead`, Hidden: true, Run: func(cmd *cobra.Command, args []string) { - err := RunRollingUpdate(f, out, cmd, args, options) - cmdutil.CheckErr(err) + cmdutil.CheckErr(o.Complete(f, cmd, args)) + cmdutil.CheckErr(o.Validate(cmd, args)) + cmdutil.CheckErr(o.Run()) }, } - cmd.Flags().Duration("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) - cmd.Flags().Duration("poll-interval", pollInterval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) - cmd.Flags().Duration("timeout", timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) + + o.PrintFlags.AddFlags(cmd) + + cmd.Flags().DurationVar(&o.Period, "update-period", o.Period, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) + cmd.Flags().DurationVar(&o.Interval, "poll-interval", o.Interval, `Time delay between polling for replication controller status after the update. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) + cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, `Max time to wait for a replication controller to update before giving up. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) usage := "Filename or URL to file to use to create the new replication controller." - kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) - cmd.Flags().String("image", "", i18n.T("Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f")) - cmd.Flags().String("deployment-label-key", "deployment", i18n.T("The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise")) - cmd.Flags().String("container", "", i18n.T("Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod")) - cmd.Flags().String("image-pull-policy", "", i18n.T("Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.")) - cmd.Flags().Bool("rollback", false, "If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout") + kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.Filenames, usage) + cmd.Flags().StringVar(&o.Image, "image", o.Image, i18n.T("Image to use for upgrading the replication controller. Must be distinct from the existing image (either new image or new image tag). Can not be used with --filename/-f")) + cmd.Flags().StringVar(&o.DeploymentKey, "deployment-label-key", o.DeploymentKey, i18n.T("The key to use to differentiate between two different controllers, default 'deployment'. Only relevant when --image is specified, ignored otherwise")) + cmd.Flags().StringVar(&o.Container, "container", o.Container, i18n.T("Container name which will have its image upgraded. Only relevant when --image is specified, ignored otherwise. Required when using --image on a multi-container pod")) + cmd.Flags().StringVar(&o.PullPolicy, "image-pull-policy", o.PullPolicy, i18n.T("Explicit policy for when to pull container images. Required when --image is same as existing image, ignored otherwise.")) + cmd.Flags().BoolVar(&o.Rollback, "rollback", o.Rollback, "If true, this is a request to abort an existing rollout that is partially rolled out. It effectively reverses current and next and runs a rollout") cmdutil.AddDryRunFlag(cmd) cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) return cmd } @@ -140,69 +193,93 @@ func validateArguments(cmd *cobra.Command, filenames, args []string) error { return utilerrors.NewAggregate(errors) } -func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error { - err := validateArguments(cmd, options.Filenames, args) +func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { + o.OldName = args[0] + o.DryRun = cmdutil.GetDryRunFlag(cmd) + o.OutputFormat = cmdutil.GetFlagString(cmd, "output") + o.KeepOldName = len(args) == 1 + o.ShouldValidate = cmdutil.GetFlagBool(cmd, "validate") + + o.Validator = f.Validator + o.FindNewName = func(obj *api.ReplicationController) string { + return findNewName(args, obj) + } + + var err error + o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() if err != nil { return err } - deploymentKey := cmdutil.GetFlagString(cmd, "deployment-label-key") + o.ScaleClient, err = f.ScaleClient() + if err != nil { + return err + } + + o.ClientSet, err = f.ClientSet() + if err != nil { + return err + } + + o.Builder = f.NewBuilder() + + o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { + o.PrintFlags.NamePrintFlags.Operation = operation + if o.DryRun { + o.PrintFlags.Complete("%s (dry run)") + } + + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + + return printer.PrintObj, nil + } + return nil +} + +func (o *RollingUpdateOptions) Validate(cmd *cobra.Command, args []string) error { + return validateArguments(cmd, o.FilenameOptions.Filenames, args) +} + +func (o *RollingUpdateOptions) Run() error { + filename := "" - image := cmdutil.GetFlagString(cmd, "image") - pullPolicy := cmdutil.GetFlagString(cmd, "image-pull-policy") - oldName := args[0] - rollback := cmdutil.GetFlagBool(cmd, "rollback") - period := cmdutil.GetFlagDuration(cmd, "update-period") - interval := cmdutil.GetFlagDuration(cmd, "poll-interval") - timeout := cmdutil.GetFlagDuration(cmd, "timeout") - dryrun := cmdutil.GetDryRunFlag(cmd) - outputFormat := cmdutil.GetFlagString(cmd, "output") - container := cmdutil.GetFlagString(cmd, "container") - - if len(options.Filenames) > 0 { - filename = options.Filenames[0] + if len(o.FilenameOptions.Filenames) > 0 { + filename = o.FilenameOptions.Filenames[0] } - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err - } - - clientset, err := f.ClientSet() - if err != nil { - return err - } - coreClient := clientset.Core() + coreClient := o.ClientSet.Core() var newRc *api.ReplicationController // fetch rc - oldRc, err := coreClient.ReplicationControllers(cmdNamespace).Get(oldName, metav1.GetOptions{}) + oldRc, err := coreClient.ReplicationControllers(o.Namespace).Get(o.OldName, metav1.GetOptions{}) if err != nil { - if !errors.IsNotFound(err) || len(image) == 0 || len(args) > 1 { + if !errors.IsNotFound(err) || len(o.Image) == 0 || !o.KeepOldName { return err } // We're in the middle of a rename, look for an RC with a source annotation of oldName - newRc, err := kubectl.FindSourceController(coreClient, cmdNamespace, oldName) + newRc, err := kubectl.FindSourceController(coreClient, o.Namespace, o.OldName) if err != nil { return err } - return kubectl.Rename(coreClient, newRc, oldName) + return kubectl.Rename(coreClient, newRc, o.OldName) } - var keepOldName bool var replicasDefaulted bool if len(filename) != 0 { - schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) + schema, err := o.Validator(o.ShouldValidate) if err != nil { return err } - request := f.NewBuilder(). + request := o.Builder. Unstructured(). Schema(schema). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}). + NamespaceParam(o.Namespace).DefaultNamespace(). + FilenameParam(o.EnforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}). Flatten(). Do() infos, err := request.Infos() @@ -211,10 +288,10 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args } // Handle filename input from stdin. if len(infos) > 1 { - return cmdutil.UsageErrorf(cmd, "%s specifies multiple items", filename) + return fmt.Errorf("%s specifies multiple items", filename) } if len(infos) == 0 { - return cmdutil.UsageErrorf(cmd, "please make sure %s exists and is not empty", filename) + return fmt.Errorf("please make sure %s exists and is not empty", filename) } uncastVersionedObj, err := legacyscheme.Scheme.ConvertToVersion(infos[0].Object, v1.SchemeGroupVersion) @@ -233,39 +310,38 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args } if newRc == nil { glog.V(4).Infof("Object %T is not a ReplicationController", infos[0].Object) - return cmdutil.UsageErrorf(cmd, "%s contains a %v not a ReplicationController", filename, infos[0].Object.GetObjectKind().GroupVersionKind()) + return fmt.Errorf("%s contains a %v not a ReplicationController", filename, infos[0].Object.GetObjectKind().GroupVersionKind()) } } // If the --image option is specified, we need to create a new rc with at least one different selector // than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for // same-image updates. - if len(image) != 0 { + if len(o.Image) != 0 { codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion) - keepOldName = len(args) == 1 - newName := findNewName(args, oldRc) - if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, cmdNamespace, newName); err != nil { + newName := o.FindNewName(oldRc) + if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, o.Namespace, newName); err != nil { return err } if newRc != nil { - if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != image { - return cmdutil.UsageErrorf(cmd, "Found existing in-progress update to image (%s).\nEither continue in-progress update with --image=%s or rollback with --rollback", inProgressImage, inProgressImage) + if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != o.Image { + return fmt.Errorf("Found existing in-progress update to image (%s).\nEither continue in-progress update with --image=%s or rollback with --rollback", inProgressImage, inProgressImage) } - fmt.Fprintf(out, "Found existing update in progress (%s), resuming.\n", newRc.Name) + fmt.Fprintf(o.Out, "Found existing update in progress (%s), resuming.\n", newRc.Name) } else { config := &kubectl.NewControllerConfig{ - Namespace: cmdNamespace, - OldName: oldName, + Namespace: o.Namespace, + OldName: o.OldName, NewName: newName, - Image: image, - Container: container, - DeploymentKey: deploymentKey, + Image: o.Image, + Container: o.Container, + DeploymentKey: o.DeploymentKey, } - if oldRc.Spec.Template.Spec.Containers[0].Image == image { - if len(pullPolicy) == 0 { - return cmdutil.UsageErrorf(cmd, "--image-pull-policy (Always|Never|IfNotPresent) must be provided when --image is the same as existing container image") + if oldRc.Spec.Template.Spec.Containers[0].Image == o.Image { + if len(o.PullPolicy) == 0 { + return fmt.Errorf("--image-pull-policy (Always|Never|IfNotPresent) must be provided when --image is the same as existing container image") } - config.PullPolicy = api.PullPolicy(pullPolicy) + config.PullPolicy = api.PullPolicy(o.PullPolicy) } newRc, err = kubectl.CreateNewControllerFromCurrentController(coreClient, codec, config) if err != nil { @@ -280,35 +356,29 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args } // If new image is same as old, the hash may not be distinct, so add a suffix. oldHash += "-orig" - oldRc, err = kubectl.UpdateExistingReplicationController(coreClient, coreClient, oldRc, cmdNamespace, newRc.Name, deploymentKey, oldHash, out) + oldRc, err = kubectl.UpdateExistingReplicationController(coreClient, coreClient, oldRc, o.Namespace, newRc.Name, o.DeploymentKey, oldHash, o.Out) if err != nil { return err } } - if rollback { - keepOldName = len(args) == 1 - newName := findNewName(args, oldRc) - if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, cmdNamespace, newName); err != nil { + if o.Rollback { + newName := o.FindNewName(oldRc) + if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, o.Namespace, newName); err != nil { return err } if newRc == nil { - return cmdutil.UsageErrorf(cmd, "Could not find %s to rollback.\n", newName) + return fmt.Errorf("Could not find %s to rollback.\n", newName) } } - if oldName == newRc.Name { - return cmdutil.UsageErrorf(cmd, "%s cannot have the same name as the existing ReplicationController %s", - filename, oldName) + if o.OldName == newRc.Name { + return fmt.Errorf("%s cannot have the same name as the existing ReplicationController %s", + filename, o.OldName) } - scalesGetter, err := f.ScaleClient() - if err != nil { - return err - } - - updater := kubectl.NewRollingUpdater(newRc.Namespace, coreClient, coreClient, scalesGetter) + updater := kubectl.NewRollingUpdater(newRc.Namespace, coreClient, coreClient, o.ScaleClient) // To successfully pull off a rolling update the new and old rc have to differ // by at least one selector. Every new pod should have the selector and every @@ -321,46 +391,50 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args } } if !hasLabel { - return cmdutil.UsageErrorf(cmd, "%s must specify a matching key with non-equal value in Selector for %s", - filename, oldName) + return fmt.Errorf("%s must specify a matching key with non-equal value in Selector for %s", + filename, o.OldName) } // TODO: handle scales during rolling update if replicasDefaulted { newRc.Spec.Replicas = oldRc.Spec.Replicas } - if dryrun { + if o.DryRun { oldRcData := &bytes.Buffer{} newRcData := &bytes.Buffer{} - if outputFormat == "" { + if o.OutputFormat == "" { oldRcData.WriteString(oldRc.Name) newRcData.WriteString(newRc.Name) } else { - if err := cmdutil.PrintObject(cmd, oldRc, oldRcData); err != nil { + printer, err := o.ToPrinter("rolling updated") + if err != nil { return err } - if err := cmdutil.PrintObject(cmd, newRc, newRcData); err != nil { + if err := printer.PrintObj(oldRc, oldRcData); err != nil { + return err + } + if err := printer.PrintObj(newRc, newRcData); err != nil { return err } } - fmt.Fprintf(out, "Rolling from:\n%s\nTo:\n%s\n", string(oldRcData.Bytes()), string(newRcData.Bytes())) + fmt.Fprintf(o.Out, "Rolling from:\n%s\nTo:\n%s\n", string(oldRcData.Bytes()), string(newRcData.Bytes())) return nil } updateCleanupPolicy := kubectl.DeleteRollingUpdateCleanupPolicy - if keepOldName { + if o.KeepOldName { updateCleanupPolicy = kubectl.RenameRollingUpdateCleanupPolicy } config := &kubectl.RollingUpdaterConfig{ - Out: out, + Out: o.Out, OldRc: oldRc, NewRc: newRc, - UpdatePeriod: period, - Interval: interval, + UpdatePeriod: o.Period, + Interval: o.Interval, Timeout: timeout, CleanupPolicy: updateCleanupPolicy, MaxUnavailable: intstr.FromInt(0), MaxSurge: intstr.FromInt(1), } - if rollback { + if o.Rollback { err = kubectl.AbortRollingUpdate(config) if err != nil { return err @@ -373,20 +447,21 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args } message := "rolling updated" - if keepOldName { - newRc.Name = oldName + if o.KeepOldName { + newRc.Name = o.OldName } else { message = fmt.Sprintf("rolling updated to %q", newRc.Name) } - newRc, err = coreClient.ReplicationControllers(cmdNamespace).Get(newRc.Name, metav1.GetOptions{}) + newRc, err = coreClient.ReplicationControllers(o.Namespace).Get(newRc.Name, metav1.GetOptions{}) if err != nil { return err } - if outputFormat != "" { - return cmdutil.PrintObject(cmd, newRc, out) + + printer, err := o.ToPrinter(message) + if err != nil { + return err } - cmdutil.PrintSuccess(false, out, newRc, dryrun, message) - return nil + return printer.PrintObj(newRc, o.Out) } func findNewName(args []string, oldRc *api.ReplicationController) string { diff --git a/pkg/kubectl/cmd/rollingupdate_test.go b/pkg/kubectl/cmd/rollingupdate_test.go index 4e452dbb62..3d15074635 100644 --- a/pkg/kubectl/cmd/rollingupdate_test.go +++ b/pkg/kubectl/cmd/rollingupdate_test.go @@ -17,10 +17,10 @@ limitations under the License. package cmd import ( - "bytes" "testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) func TestValidateArgs(t *testing.T) { @@ -74,8 +74,7 @@ func TestValidateArgs(t *testing.T) { }, } for _, test := range tests { - out := &bytes.Buffer{} - cmd := NewCmdRollingUpdate(f, out) + cmd := NewCmdRollingUpdate(f, genericclioptions.NewTestIOStreamsDiscard()) if test.flags != nil { for key, val := range test.flags { diff --git a/pkg/kubectl/cmd/rollout/BUILD b/pkg/kubectl/cmd/rollout/BUILD index df24db8989..6a2e546f64 100644 --- a/pkg/kubectl/cmd/rollout/BUILD +++ b/pkg/kubectl/cmd/rollout/BUILD @@ -24,8 +24,10 @@ go_library( "//pkg/kubectl/cmd/set: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/kubectl/util/i18n:go_default_library", + "//pkg/printers:go_default_library", "//pkg/util/interrupt:go_default_library", "//vendor/github.com/renstrom/dedent:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library", @@ -59,6 +61,7 @@ go_test( "//pkg/api/legacyscheme:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library", + "//pkg/kubectl/genericclioptions:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", diff --git a/pkg/kubectl/cmd/rollout/rollout.go b/pkg/kubectl/cmd/rollout/rollout.go index 941b585c11..d7d03e266b 100644 --- a/pkg/kubectl/cmd/rollout/rollout.go +++ b/pkg/kubectl/cmd/rollout/rollout.go @@ -17,12 +17,12 @@ limitations under the License. package rollout import ( - "io" - "github.com/renstrom/dedent" "github.com/spf13/cobra" + "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" ) @@ -46,22 +46,21 @@ var ( `) ) -func NewCmdRollout(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { - +func NewCmdRollout(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { cmd := &cobra.Command{ Use: "rollout SUBCOMMAND", DisableFlagsInUseLine: true, Short: i18n.T("Manage the rollout of a resource"), Long: rollout_long, Example: rollout_example, - Run: cmdutil.DefaultSubCommandRun(errOut), + Run: cmdutil.DefaultSubCommandRun(streams.Out), } // subcommands - cmd.AddCommand(NewCmdRolloutHistory(f, out)) - cmd.AddCommand(NewCmdRolloutPause(f, out)) - cmd.AddCommand(NewCmdRolloutResume(f, out)) - cmd.AddCommand(NewCmdRolloutUndo(f, out)) - cmd.AddCommand(NewCmdRolloutStatus(f, out)) + cmd.AddCommand(NewCmdRolloutHistory(f, streams.Out)) + cmd.AddCommand(NewCmdRolloutPause(f, streams)) + cmd.AddCommand(NewCmdRolloutResume(f, streams)) + cmd.AddCommand(NewCmdRolloutUndo(f, streams.Out)) + cmd.AddCommand(NewCmdRolloutStatus(f, streams)) return cmd } diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 8071623622..10962de572 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "io" "github.com/spf13/cobra" @@ -30,20 +29,24 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/set" "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/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/printers" ) // PauseConfig 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 PauseConfig struct { resource.FilenameOptions + PrintFlags *printers.PrintFlags + ToPrinter func(string) (printers.ResourcePrinterFunc, error) Pauser func(info *resource.Info) ([]byte, error) Mapper meta.RESTMapper Infos []*resource.Info - Out io.Writer + genericclioptions.IOStreams } var ( @@ -61,8 +64,11 @@ var ( kubectl rollout pause deployment/nginx`) ) -func NewCmdRolloutPause(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &PauseConfig{} +func NewCmdRolloutPause(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := &PauseConfig{ + PrintFlags: printers.NewPrintFlags("paused"), + IOStreams: streams, + } validArgs := []string{"deployment"} argAliases := kubectl.ResourceAliases(validArgs) @@ -75,11 +81,11 @@ func NewCmdRolloutPause(f cmdutil.Factory, out io.Writer) *cobra.Command { Example: pause_example, Run: func(cmd *cobra.Command, args []string) { allErrs := []error{} - err := options.CompletePause(f, cmd, out, args) + err := o.CompletePause(f, cmd, args) if err != nil { allErrs = append(allErrs, err) } - err = options.RunPause() + err = o.RunPause() if err != nil { allErrs = append(allErrs, err) } @@ -90,11 +96,11 @@ func NewCmdRolloutPause(f cmdutil.Factory, out io.Writer) *cobra.Command { } usage := "identifying the resource to get from a server." - cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) + cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) return cmd } -func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error { +func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args []string) error { if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) { return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } @@ -102,7 +108,6 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i o.Mapper = f.RESTMapper() o.Pauser = f.Pauser - o.Out = out cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { @@ -123,6 +128,16 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i return err } + o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { + o.PrintFlags.NamePrintFlags.Operation = operation + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + + return printer.PrintObj, nil + } + o.Infos, err = r.Infos() if err != nil { return err @@ -140,7 +155,12 @@ func (o PauseConfig) RunPause() error { } if string(patch.Patch) == "{}" || len(patch.Patch) == 0 { - cmdutil.PrintSuccess(false, o.Out, info.Object, false, "already paused") + printer, err := o.ToPrinter("already paused") + if err != nil { + allErrs = append(allErrs, err) + continue + } + printer.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out) continue } @@ -151,7 +171,12 @@ func (o PauseConfig) RunPause() error { } info.Refresh(obj, true) - cmdutil.PrintSuccess(false, o.Out, info.Object, false, "paused") + printer, err := o.ToPrinter("paused") + if err != nil { + allErrs = append(allErrs, err) + continue + } + printer.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out) } return utilerrors.NewAggregate(allErrs) diff --git a/pkg/kubectl/cmd/rollout/rollout_pause_test.go b/pkg/kubectl/cmd/rollout/rollout_pause_test.go index 23bcff14ee..a40e032730 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause_test.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause_test.go @@ -31,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api/legacyscheme" extensions "k8s.io/kubernetes/pkg/apis/extensions" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" ) var rolloutPauseGroupVersionEncoder = schema.GroupVersion{Group: "extensions", Version: "v1beta1"} @@ -62,11 +63,11 @@ func TestRolloutPause(t *testing.T) { } tf.Namespace = "test" - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdRolloutPause(tf, buf) + streams, _, buf, _ := genericclioptions.NewTestIOStreams() + cmd := NewCmdRolloutPause(tf, streams) cmd.Run(cmd, []string{deploymentName}) - expectedOutput := "deployment.apps \"" + deploymentName + "\" paused\n" + expectedOutput := "deployment.extensions/" + deploymentName + " paused\n" if buf.String() != expectedOutput { t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String()) } diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index 65ee72765b..a25ed405ba 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "io" "github.com/spf13/cobra" @@ -30,20 +29,24 @@ import ( "k8s.io/kubernetes/pkg/kubectl/cmd/set" "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/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/printers" ) // ResumeConfig 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 ResumeConfig struct { resource.FilenameOptions + PrintFlags *printers.PrintFlags + ToPrinter func(string) (printers.ResourcePrinterFunc, error) Resumer func(object *resource.Info) ([]byte, error) Mapper meta.RESTMapper Infos []*resource.Info - Out io.Writer + genericclioptions.IOStreams } var ( @@ -59,8 +62,11 @@ var ( kubectl rollout resume deployment/nginx`) ) -func NewCmdRolloutResume(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &ResumeConfig{} +func NewCmdRolloutResume(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := &ResumeConfig{ + PrintFlags: printers.NewPrintFlags("resumed"), + IOStreams: streams, + } validArgs := []string{"deployment"} argAliases := kubectl.ResourceAliases(validArgs) @@ -73,11 +79,11 @@ func NewCmdRolloutResume(f cmdutil.Factory, out io.Writer) *cobra.Command { Example: resume_example, Run: func(cmd *cobra.Command, args []string) { allErrs := []error{} - err := options.CompleteResume(f, cmd, out, args) + err := o.CompleteResume(f, cmd, args) if err != nil { allErrs = append(allErrs, err) } - err = options.RunResume() + err = o.RunResume() if err != nil { allErrs = append(allErrs, err) } @@ -88,11 +94,11 @@ func NewCmdRolloutResume(f cmdutil.Factory, out io.Writer) *cobra.Command { } usage := "identifying the resource to get from a server." - cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) + cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) return cmd } -func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string) error { +func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, args []string) error { if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) { return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } @@ -100,13 +106,22 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out o.Mapper = f.RESTMapper() o.Resumer = f.Resumer - o.Out = out cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err } + o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { + o.PrintFlags.NamePrintFlags.Operation = operation + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + + return printer.PrintObj, nil + } + r := f.NewBuilder(). Internal(legacyscheme.Scheme). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -145,8 +160,12 @@ func (o ResumeConfig) RunResume() error { } if string(patch.Patch) == "{}" || len(patch.Patch) == 0 { - cmdutil.PrintSuccess(false, o.Out, info.Object, false, "already resumed") - continue + printer, err := o.ToPrinter("already resumed") + if err != nil { + allErrs = append(allErrs, err) + continue + } + printer.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out) } obj, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) @@ -156,7 +175,12 @@ func (o ResumeConfig) RunResume() error { } info.Refresh(obj, true) - cmdutil.PrintSuccess(false, o.Out, info.Object, false, "resumed") + printer, err := o.ToPrinter("resumed") + if err != nil { + allErrs = append(allErrs, err) + continue + } + printer.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out) } return utilerrors.NewAggregate(allErrs) diff --git a/pkg/kubectl/cmd/rollout/rollout_status.go b/pkg/kubectl/cmd/rollout/rollout_status.go index b42c1c9f5a..5a0b27b0b2 100644 --- a/pkg/kubectl/cmd/rollout/rollout_status.go +++ b/pkg/kubectl/cmd/rollout/rollout_status.go @@ -18,7 +18,6 @@ package rollout import ( "fmt" - "io" "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/watch" @@ -26,6 +25,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl" "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/kubectl/util/i18n" "k8s.io/kubernetes/pkg/util/interrupt" @@ -49,8 +49,32 @@ var ( kubectl rollout status deployment/nginx`) ) -func NewCmdRolloutStatus(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &resource.FilenameOptions{} +type RolloutStatusOptions struct { + FilenameOptions *resource.FilenameOptions + genericclioptions.IOStreams + + Namespace string + EnforceNamespace bool + BuilderArgs []string + + Watch bool + Revision int64 + + StatusViewer func(*meta.RESTMapping) (kubectl.StatusViewer, error) + + Builder *resource.Builder +} + +func NewRolloutStatusOptions(streams genericclioptions.IOStreams) *RolloutStatusOptions { + return &RolloutStatusOptions{ + FilenameOptions: &resource.FilenameOptions{}, + IOStreams: streams, + Watch: true, + } +} + +func NewCmdRolloutStatus(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := NewRolloutStatusOptions(streams) validArgs := []string{"deployment", "daemonset", "statefulset"} argAliases := kubectl.ResourceAliases(validArgs) @@ -62,38 +86,52 @@ func NewCmdRolloutStatus(f cmdutil.Factory, out io.Writer) *cobra.Command { Long: status_long, Example: status_example, Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(RunStatus(f, cmd, out, args, options)) + cmdutil.CheckErr(o.Complete(f, args)) + cmdutil.CheckErr(o.Validate(cmd, args)) + cmdutil.CheckErr(o.Run()) }, ValidArgs: validArgs, ArgAliases: argAliases, } usage := "identifying the resource to get from a server." - cmdutil.AddFilenameOptionFlags(cmd, options, usage) - cmd.Flags().BoolP("watch", "w", true, "Watch the status of the rollout until it's done.") - cmd.Flags().Int64("revision", 0, "Pin to a specific revision for showing its status. Defaults to 0 (last revision).") + cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage) + cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "Watch the status of the rollout until it's done.") + cmd.Flags().Int64Var(&o.Revision, "revision", o.Revision, "Pin to a specific revision for showing its status. Defaults to 0 (last revision).") return cmd } -func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string, options *resource.FilenameOptions) error { - if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) { - return cmdutil.UsageErrorf(cmd, "Required resource not specified.") - } +func (o *RolloutStatusOptions) Complete(f cmdutil.Factory, args []string) error { + o.Builder = f.NewBuilder() - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() + var err error + o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() if err != nil { return err } - r := f.NewBuilder(). + o.BuilderArgs = args + o.StatusViewer = f.StatusViewer + return nil +} + +func (o *RolloutStatusOptions) Validate(cmd *cobra.Command, args []string) error { + if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) { + return cmdutil.UsageErrorf(cmd, "Required resource not specified.") + } + return nil +} + +func (o *RolloutStatusOptions) Run() error { + r := o.Builder. Internal(legacyscheme.Scheme). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, options). - ResourceTypeOrNameArgs(true, args...). + NamespaceParam(o.Namespace).DefaultNamespace(). + FilenameParam(o.EnforceNamespace, o.FilenameOptions). + ResourceTypeOrNameArgs(true, o.BuilderArgs...). SingleResourceType(). Latest(). Do() - err = r.Err() + err := r.Err() if err != nil { return err } @@ -117,12 +155,12 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri return err } - statusViewer, err := f.StatusViewer(mapping) + statusViewer, err := o.StatusViewer(mapping) if err != nil { return err } - revision := cmdutil.GetFlagInt64(cmd, "revision") + revision := o.Revision if revision < 0 { return fmt.Errorf("revision must be a positive integer: %v", revision) } @@ -132,12 +170,12 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri if err != nil { return err } - fmt.Fprintf(out, "%s", status) + fmt.Fprintf(o.Out, "%s", status) if done { return nil } - shouldWatch := cmdutil.GetFlagBool(cmd, "watch") + shouldWatch := o.Watch if !shouldWatch { return nil } @@ -157,7 +195,7 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri if err != nil { return false, err } - fmt.Fprintf(out, "%s", status) + fmt.Fprintf(o.Out, "%s", status) // Quit waiting if the rollout is done if done { return true, nil diff --git a/pkg/kubectl/cmd/rollout/rollout_undo.go b/pkg/kubectl/cmd/rollout/rollout_undo.go index 903ad1e328..aef52b8054 100644 --- a/pkg/kubectl/cmd/rollout/rollout_undo.go +++ b/pkg/kubectl/cmd/rollout/rollout_undo.go @@ -27,6 +27,7 @@ import ( cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/printers" "github.com/spf13/cobra" ) @@ -36,6 +37,9 @@ import ( type UndoOptions struct { resource.FilenameOptions + PrintFlags *printers.PrintFlags + ToPrinter func(string) (printers.ResourcePrinterFunc, error) + Rollbackers []kubectl.Rollbacker Mapper meta.RESTMapper Infos []*resource.Info @@ -61,7 +65,9 @@ var ( ) func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &UndoOptions{} + o := &UndoOptions{ + PrintFlags: printers.NewPrintFlags(""), + } validArgs := []string{"deployment", "daemonset", "statefulset"} argAliases := kubectl.ResourceAliases(validArgs) @@ -74,11 +80,11 @@ func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command { Example: undo_example, Run: func(cmd *cobra.Command, args []string) { allErrs := []error{} - err := options.CompleteUndo(f, cmd, out, args) + err := o.CompleteUndo(f, cmd, out, args) if err != nil { allErrs = append(allErrs, err) } - err = options.RunUndo() + err = o.RunUndo() if err != nil { allErrs = append(allErrs, err) } @@ -90,7 +96,7 @@ func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command { cmd.Flags().Int64("to-revision", 0, "The revision to rollback to. Default to 0 (last revision).") usage := "identifying the resource to get from a server." - cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) + cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) cmdutil.AddDryRunFlag(cmd) return cmd } @@ -110,6 +116,19 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io return err } + o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { + o.PrintFlags.NamePrintFlags.Operation = operation + if o.DryRun { + o.PrintFlags.Complete("%s (dry run)") + } + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + + return printer.PrintObj, nil + } + r := f.NewBuilder(). Internal(legacyscheme.Scheme). NamespaceParam(cmdNamespace).DefaultNamespace(). @@ -147,7 +166,12 @@ func (o *UndoOptions) RunUndo() error { allErrs = append(allErrs, cmdutil.AddSourceToErr("undoing", info.Source, err)) continue } - cmdutil.PrintSuccess(false, o.Out, info.Object, false, result) + printer, err := o.ToPrinter(result) + if err != nil { + allErrs = append(allErrs, err) + continue + } + printer.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out) } return utilerrors.NewAggregate(allErrs) } diff --git a/pkg/kubectl/cmd/scale.go b/pkg/kubectl/cmd/scale.go index 428bbafc17..bd2743a954 100644 --- a/pkg/kubectl/cmd/scale.go +++ b/pkg/kubectl/cmd/scale.go @@ -18,12 +18,14 @@ package cmd import ( "fmt" - "io" - - "github.com/spf13/cobra" + "time" "github.com/golang/glog" + "github.com/spf13/cobra" + + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion" "k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl/cmd/scalejob" @@ -32,6 +34,7 @@ import ( "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/printers" ) var ( @@ -64,21 +67,49 @@ var ( type ScaleOptions struct { FilenameOptions resource.FilenameOptions RecordFlags *genericclioptions.RecordFlags + PrintFlags *printers.PrintFlags + PrintObj printers.ResourcePrinterFunc + + BuilderArgs []string + Namespace string + EnforceNamespace bool + + Builder *resource.Builder + ClientSet internalclientset.Interface + Scaler kubectl.Scaler + + All bool + Selector string + + CmdParent string + + ResourceVersion string + CurrentReplicas int + Replicas int + Duration time.Duration + + ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error) Recorder genericclioptions.Recorder + + genericclioptions.IOStreams } -func NewScaleOptions() *ScaleOptions { +func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions { return &ScaleOptions{ RecordFlags: genericclioptions.NewRecordFlags(), + PrintFlags: printers.NewPrintFlags("scaled"), - Recorder: genericclioptions.NoopRecorder{}, + CurrentReplicas: -1, + + Recorder: genericclioptions.NoopRecorder{}, + IOStreams: ioStreams, } } // NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale -func NewCmdScale(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { - o := NewScaleOptions() +func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + o := NewScaleOptions(streams) validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"} argAliases := kubectl.ResourceAliases(validArgs) @@ -90,33 +121,58 @@ func NewCmdScale(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { Long: scaleLong, Example: scaleExample, Run: func(cmd *cobra.Command, args []string) { - cmdutil.CheckErr(o.Complete(f, cmd)) + cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" - cmdutil.CheckErr(o.RunScale(f, out, errOut, cmd, args, shortOutput)) + cmdutil.CheckErr(o.RunScale()) }, ValidArgs: validArgs, ArgAliases: argAliases, } o.RecordFlags.AddFlags(cmd) + o.PrintFlags.AddFlags(cmd) - cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") - cmd.Flags().Bool("all", false, "Select all resources in the namespace of the specified resource types") - cmd.Flags().String("resource-version", "", i18n.T("Precondition for resource version. Requires that the current resource version match this value in order to scale.")) - cmd.Flags().Int("current-replicas", -1, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.") - cmd.Flags().Int("replicas", -1, "The new desired number of replicas. Required.") + cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources in the namespace of the specified resource types") + cmd.Flags().StringVar(&o.ResourceVersion, "resource-version", o.ResourceVersion, i18n.T("Precondition for resource version. Requires that the current resource version match this value in order to scale.")) + cmd.Flags().IntVar(&o.CurrentReplicas, "current-replicas", o.CurrentReplicas, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.") + cmd.Flags().IntVar(&o.Replicas, "replicas", o.Replicas, "The new desired number of replicas. Required.") cmd.MarkFlagRequired("replicas") - cmd.Flags().Duration("timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).") - cmdutil.AddOutputFlagsForMutation(cmd) + cmd.Flags().DurationVar(&o.Duration, "timeout", o.Duration, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).") usage := "identifying the resource to set a new size" cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) return cmd } -func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { +func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { var err error + o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() + if err != nil { + return err + } + + o.CmdParent = cmd.Parent().Name() + o.Builder = f.NewBuilder() + + o.ClientSet, err = f.ClientSet() + if err != nil { + return err + } + + o.Scaler, err = f.Scaler() + if err != nil { + return err + } + + o.BuilderArgs = args + o.ClientForMapping = f.UnstructuredClientForMapping + + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return err + } + o.PrintObj = printer.PrintObj o.RecordFlags.Complete(f.Command(cmd, false)) o.Recorder, err = o.RecordFlags.ToRecorder() @@ -124,36 +180,28 @@ func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { return err } - return err + return nil } // RunScale executes the scaling -func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, shortOutput bool) error { - cmdNamespace, enforceNamespace, err := f.DefaultNamespace() - if err != nil { - return err +func (o *ScaleOptions) RunScale() error { + + if o.Replicas < 0 { + return fmt.Errorf("The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0") } - count := cmdutil.GetFlagInt(cmd, "replicas") - if count < 0 { - return cmdutil.UsageErrorf(cmd, "The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0") - } - - selector := cmdutil.GetFlagString(cmd, "selector") - all := cmdutil.GetFlagBool(cmd, "all") - - r := f.NewBuilder(). + r := o.Builder. Unstructured(). ContinueOnError(). - NamespaceParam(cmdNamespace).DefaultNamespace(). - FilenameParam(enforceNamespace, &o.FilenameOptions). - ResourceTypeOrNameArgs(all, args...). + NamespaceParam(o.Namespace).DefaultNamespace(). + FilenameParam(o.EnforceNamespace, &o.FilenameOptions). + ResourceTypeOrNameArgs(o.All, o.BuilderArgs...). Flatten(). - LabelSelectorParam(selector). + LabelSelectorParam(o.Selector). Do() - err = r.Err() + err := r.Err() if resource.IsUsageError(err) { - return cmdutil.UsageErrorf(cmd, "%v", err) + return fmt.Errorf("%v", err) } if err != nil { return err @@ -167,17 +215,16 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c return nil }) - resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") - if len(resourceVersion) != 0 && len(infos) > 1 { + if len(o.ResourceVersion) != 0 && len(infos) > 1 { return fmt.Errorf("cannot use --resource-version with multiple resources") } - currentSize := cmdutil.GetFlagInt(cmd, "current-replicas") - precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion} + currentSize := o.CurrentReplicas + precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: o.ResourceVersion} retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) var waitForReplicas *kubectl.RetryParams - if timeout := cmdutil.GetFlagDuration(cmd, "timeout"); timeout != 0 { + if timeout := o.Duration; timeout != 0 { waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) } @@ -190,24 +237,15 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c mapping := info.ResourceMapping() if mapping.Resource == "jobs" { // go down the legacy jobs path. This can be removed in 3.14 For now, contain it. - fmt.Fprintf(errOut, "%s scale job is DEPRECATED and will be removed in a future version.\n", cmd.Parent().Name()) + fmt.Fprintf(o.ErrOut, "%s scale job is DEPRECATED and will be removed in a future version.\n", o.CmdParent) - clientset, err := f.ClientSet() - if err != nil { - return err - } - if err := ScaleJob(info, clientset.Batch(), uint(count), precondition, retry, waitForReplicas); err != nil { + if err := ScaleJob(info, o.ClientSet.Batch(), uint(o.Replicas), precondition, retry, waitForReplicas); err != nil { return err } } else { - scaler, err := f.Scaler() - if err != nil { - return err - } - gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource) - if err := scaler.Scale(info.Namespace, info.Name, uint(count), precondition, retry, waitForReplicas, gvk.GroupResource()); err != nil { + if err := o.Scaler.Scale(info.Namespace, info.Name, uint(o.Replicas), precondition, retry, waitForReplicas, gvk.GroupResource()); err != nil { return err } } @@ -216,7 +254,7 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c if mergePatch, err := o.Recorder.MakeRecordMergePatch(info.Object); err != nil { glog.V(4).Infof("error recording current command: %v", err) } else if len(mergePatch) > 0 { - client, err := f.UnstructuredClientForMapping(mapping) + client, err := o.ClientForMapping(mapping) if err != nil { return err } @@ -227,8 +265,7 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c } counter++ - cmdutil.PrintSuccess(shortOutput, out, info.Object, false, "scaled") - return nil + return o.PrintObj(info.Object, o.Out) }) if err != nil { return err diff --git a/pkg/kubectl/cmd/taint.go b/pkg/kubectl/cmd/taint.go index 53aa62f82b..cfba42b1eb 100644 --- a/pkg/kubectl/cmd/taint.go +++ b/pkg/kubectl/cmd/taint.go @@ -18,7 +18,6 @@ package cmd import ( "fmt" - "io" "strings" "encoding/json" @@ -26,6 +25,7 @@ import ( "github.com/golang/glog" "github.com/spf13/cobra" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/strategicpatch" @@ -34,13 +34,18 @@ import ( "k8s.io/kubernetes/pkg/kubectl" "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/kubectl/util/i18n" + "k8s.io/kubernetes/pkg/printers" taintutils "k8s.io/kubernetes/pkg/util/taints" ) // TaintOptions have the data required to perform the taint operation type TaintOptions struct { + PrintFlags *printers.PrintFlags + ToPrinter func(string) (printers.ResourcePrinterFunc, error) + resources []string taintsToAdd []v1.Taint taintsToRemove []v1.Taint @@ -48,9 +53,10 @@ type TaintOptions struct { selector string overwrite bool all bool - f cmdutil.Factory - out io.Writer - cmd *cobra.Command + + ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error) + + genericclioptions.IOStreams } var ( @@ -79,8 +85,11 @@ var ( kubectl taint node -l myLabel=X dedicated=foo:PreferNoSchedule`)) ) -func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { - options := &TaintOptions{} +func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { + options := &TaintOptions{ + PrintFlags: printers.NewPrintFlags("tainted"), + IOStreams: streams, + } validArgs := []string{"node"} argAliases := kubectl.ResourceAliases(validArgs) @@ -92,7 +101,7 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { Long: fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength), Example: taintExample, Run: func(cmd *cobra.Command, args []string) { - if err := options.Complete(f, out, cmd, args); err != nil { + if err := options.Complete(f, cmd, args); err != nil { cmdutil.CheckErr(err) } if err := options.Validate(); err != nil { @@ -105,9 +114,10 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { ValidArgs: validArgs, ArgAliases: argAliases, } - cmdutil.AddValidateFlags(cmd) - cmdutil.AddPrinterFlags(cmd) + options.PrintFlags.AddFlags(cmd) + + cmdutil.AddValidateFlags(cmd) cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow taints to be overwritten, otherwise reject taint updates that overwrite existing taints.") cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all nodes in the cluster") @@ -115,7 +125,7 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { } // Complete adapts from the command line args and factory to the data required. -func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) (err error) { +func (o *TaintOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) (err error) { namespace, _, err := f.DefaultNamespace() if err != nil { return err @@ -140,6 +150,16 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com } } + o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { + o.PrintFlags.NamePrintFlags.Operation = operation + printer, err := o.PrintFlags.ToPrinter() + if err != nil { + return nil, err + } + + return printer.PrintObj, nil + } + if len(o.resources) < 1 { return fmt.Errorf("one or more resources must be specified as ") } @@ -166,9 +186,8 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com o.builder = o.builder.LabelSelectorParam(o.selector). Flatten(). Latest() - o.f = f - o.out = out - o.cmd = cmd + + o.ClientForMapping = f.ClientForMapping return nil } @@ -258,7 +277,7 @@ func (o TaintOptions) RunTaint() error { } mapping := info.ResourceMapping() - client, err := o.f.ClientForMapping(mapping) + client, err := o.ClientForMapping(mapping) if err != nil { return err } @@ -274,13 +293,11 @@ func (o TaintOptions) RunTaint() error { return err } - outputFormat := cmdutil.GetFlagString(o.cmd, "output") - if outputFormat != "" { - return cmdutil.PrintObject(o.cmd, outputObj, o.out) + printer, err := o.ToPrinter(operation) + if err != nil { + return err } - - cmdutil.PrintSuccess(false, o.out, info.Object, false, operation) - return nil + return printer.PrintObj(outputObj, o.Out) }) } diff --git a/pkg/kubectl/cmd/taint_test.go b/pkg/kubectl/cmd/taint_test.go index 9fdab7db57..22fcee6b91 100644 --- a/pkg/kubectl/cmd/taint_test.go +++ b/pkg/kubectl/cmd/taint_test.go @@ -17,7 +17,6 @@ limitations under the License. package cmd import ( - "bytes" "io/ioutil" "net/http" "reflect" @@ -32,6 +31,7 @@ import ( "k8s.io/kubernetes/pkg/api/legacyscheme" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/scheme" ) @@ -302,8 +302,7 @@ func TestTaint(t *testing.T) { } tf.ClientConfigVal = defaultClientConfig() - buf := bytes.NewBuffer([]byte{}) - cmd := NewCmdTaint(tf, buf) + cmd := NewCmdTaint(tf, genericclioptions.NewTestIOStreamsDiscard()) saw_fatal := false func() {