finish wiring PrintFlags

pull/8/head
juanvallejo 2018-04-24 00:40:35 -04:00 committed by Maciej Szulik
parent 284e8182a4
commit f432ebe262
No known key found for this signature in database
GPG Key ID: F15E55D276FA84C4
13 changed files with 496 additions and 255 deletions

View File

@ -273,9 +273,9 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
{ {
Message: "Deploy Commands:", Message: "Deploy Commands:",
Commands: []*cobra.Command{ Commands: []*cobra.Command{
rollout.NewCmdRollout(f, out, err), rollout.NewCmdRollout(f, ioStreams),
NewCmdRollingUpdate(f, out), NewCmdRollingUpdate(f, ioStreams),
NewCmdScale(f, out, err), NewCmdScale(f, ioStreams),
NewCmdAutoscale(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), NewCmdCordon(f, ioStreams),
NewCmdUncordon(f, ioStreams), NewCmdUncordon(f, ioStreams),
NewCmdDrain(f, ioStreams), NewCmdDrain(f, ioStreams),
NewCmdTaint(f, out), NewCmdTaint(f, ioStreams),
}, },
}, },
{ {

View File

@ -19,11 +19,9 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
@ -31,14 +29,19 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
scaleclient "k8s.io/client-go/scale"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" 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"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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/resource"
"k8s.io/kubernetes/pkg/kubectl/util" "k8s.io/kubernetes/pkg/kubectl/util"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers"
) )
var ( var (
@ -75,8 +78,55 @@ var (
pollInterval, _ = time.ParseDuration("3s") pollInterval, _ = time.ParseDuration("3s")
) )
func NewCmdRollingUpdate(f cmdutil.Factory, out io.Writer) *cobra.Command { type RollingUpdateOptions struct {
options := &resource.FilenameOptions{} 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{ cmd := &cobra.Command{
Use: "rolling-update OLD_CONTROLLER_NAME ([NEW_CONTROLLER_NAME] --image=NEW_CONTAINER_IMAGE | -f NEW_CONTROLLER_SPEC)", 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`, Deprecated: `use "rollout" instead`,
Hidden: true, Hidden: true,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunRollingUpdate(f, out, cmd, args, options) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(err) 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".`) o.PrintFlags.AddFlags(cmd)
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".`)
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." usage := "Filename or URL to file to use to create the new replication controller."
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.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().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().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().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().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().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().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().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().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") 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.AddDryRunFlag(cmd)
cmdutil.AddValidateFlags(cmd) cmdutil.AddValidateFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
return cmd return cmd
} }
@ -140,69 +193,93 @@ func validateArguments(cmd *cobra.Command, filenames, args []string) error {
return utilerrors.NewAggregate(errors) return utilerrors.NewAggregate(errors)
} }
func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error { func (o *RollingUpdateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
err := validateArguments(cmd, options.Filenames, args) 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 { if err != nil {
return err 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 := "" filename := ""
image := cmdutil.GetFlagString(cmd, "image") if len(o.FilenameOptions.Filenames) > 0 {
pullPolicy := cmdutil.GetFlagString(cmd, "image-pull-policy") filename = o.FilenameOptions.Filenames[0]
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]
} }
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() coreClient := o.ClientSet.Core()
if err != nil {
return err
}
clientset, err := f.ClientSet()
if err != nil {
return err
}
coreClient := clientset.Core()
var newRc *api.ReplicationController var newRc *api.ReplicationController
// fetch rc // 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 err != nil {
if !errors.IsNotFound(err) || len(image) == 0 || len(args) > 1 { if !errors.IsNotFound(err) || len(o.Image) == 0 || !o.KeepOldName {
return err return err
} }
// We're in the middle of a rename, look for an RC with a source annotation of oldName // 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 { if err != nil {
return err return err
} }
return kubectl.Rename(coreClient, newRc, oldName) return kubectl.Rename(coreClient, newRc, o.OldName)
} }
var keepOldName bool
var replicasDefaulted bool var replicasDefaulted bool
if len(filename) != 0 { if len(filename) != 0 {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) schema, err := o.Validator(o.ShouldValidate)
if err != nil { if err != nil {
return err return err
} }
request := f.NewBuilder(). request := o.Builder.
Unstructured(). Unstructured().
Schema(schema). Schema(schema).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}). FilenameParam(o.EnforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}).
Flatten(). Flatten().
Do() Do()
infos, err := request.Infos() 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. // Handle filename input from stdin.
if len(infos) > 1 { 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 { 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) 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 { if newRc == nil {
glog.V(4).Infof("Object %T is not a ReplicationController", infos[0].Object) 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 // 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 // than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for
// same-image updates. // same-image updates.
if len(image) != 0 { if len(o.Image) != 0 {
codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion) codec := legacyscheme.Codecs.LegacyCodec(v1.SchemeGroupVersion)
keepOldName = len(args) == 1 newName := o.FindNewName(oldRc)
newName := findNewName(args, oldRc) if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, o.Namespace, newName); err != nil {
if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, cmdNamespace, newName); err != nil {
return err return err
} }
if newRc != nil { if newRc != nil {
if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != image { if inProgressImage := newRc.Spec.Template.Spec.Containers[0].Image; inProgressImage != o.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) 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 { } else {
config := &kubectl.NewControllerConfig{ config := &kubectl.NewControllerConfig{
Namespace: cmdNamespace, Namespace: o.Namespace,
OldName: oldName, OldName: o.OldName,
NewName: newName, NewName: newName,
Image: image, Image: o.Image,
Container: container, Container: o.Container,
DeploymentKey: deploymentKey, DeploymentKey: o.DeploymentKey,
} }
if oldRc.Spec.Template.Spec.Containers[0].Image == image { if oldRc.Spec.Template.Spec.Containers[0].Image == o.Image {
if len(pullPolicy) == 0 { if len(o.PullPolicy) == 0 {
return cmdutil.UsageErrorf(cmd, "--image-pull-policy (Always|Never|IfNotPresent) must be provided when --image is the same as existing container image") 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) newRc, err = kubectl.CreateNewControllerFromCurrentController(coreClient, codec, config)
if err != nil { 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. // If new image is same as old, the hash may not be distinct, so add a suffix.
oldHash += "-orig" 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 { if err != nil {
return err return err
} }
} }
if rollback { if o.Rollback {
keepOldName = len(args) == 1 newName := o.FindNewName(oldRc)
newName := findNewName(args, oldRc) if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, o.Namespace, newName); err != nil {
if newRc, err = kubectl.LoadExistingNextReplicationController(coreClient, cmdNamespace, newName); err != nil {
return err return err
} }
if newRc == nil { 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 { if o.OldName == newRc.Name {
return cmdutil.UsageErrorf(cmd, "%s cannot have the same name as the existing ReplicationController %s", return fmt.Errorf("%s cannot have the same name as the existing ReplicationController %s",
filename, oldName) filename, o.OldName)
} }
scalesGetter, err := f.ScaleClient() updater := kubectl.NewRollingUpdater(newRc.Namespace, coreClient, coreClient, o.ScaleClient)
if err != nil {
return err
}
updater := kubectl.NewRollingUpdater(newRc.Namespace, coreClient, coreClient, scalesGetter)
// To successfully pull off a rolling update the new and old rc have to differ // 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 // 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 { if !hasLabel {
return cmdutil.UsageErrorf(cmd, "%s must specify a matching key with non-equal value in Selector for %s", return fmt.Errorf("%s must specify a matching key with non-equal value in Selector for %s",
filename, oldName) filename, o.OldName)
} }
// TODO: handle scales during rolling update // TODO: handle scales during rolling update
if replicasDefaulted { if replicasDefaulted {
newRc.Spec.Replicas = oldRc.Spec.Replicas newRc.Spec.Replicas = oldRc.Spec.Replicas
} }
if dryrun { if o.DryRun {
oldRcData := &bytes.Buffer{} oldRcData := &bytes.Buffer{}
newRcData := &bytes.Buffer{} newRcData := &bytes.Buffer{}
if outputFormat == "" { if o.OutputFormat == "" {
oldRcData.WriteString(oldRc.Name) oldRcData.WriteString(oldRc.Name)
newRcData.WriteString(newRc.Name) newRcData.WriteString(newRc.Name)
} else { } else {
if err := cmdutil.PrintObject(cmd, oldRc, oldRcData); err != nil { printer, err := o.ToPrinter("rolling updated")
if err != nil {
return err 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 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 return nil
} }
updateCleanupPolicy := kubectl.DeleteRollingUpdateCleanupPolicy updateCleanupPolicy := kubectl.DeleteRollingUpdateCleanupPolicy
if keepOldName { if o.KeepOldName {
updateCleanupPolicy = kubectl.RenameRollingUpdateCleanupPolicy updateCleanupPolicy = kubectl.RenameRollingUpdateCleanupPolicy
} }
config := &kubectl.RollingUpdaterConfig{ config := &kubectl.RollingUpdaterConfig{
Out: out, Out: o.Out,
OldRc: oldRc, OldRc: oldRc,
NewRc: newRc, NewRc: newRc,
UpdatePeriod: period, UpdatePeriod: o.Period,
Interval: interval, Interval: o.Interval,
Timeout: timeout, Timeout: timeout,
CleanupPolicy: updateCleanupPolicy, CleanupPolicy: updateCleanupPolicy,
MaxUnavailable: intstr.FromInt(0), MaxUnavailable: intstr.FromInt(0),
MaxSurge: intstr.FromInt(1), MaxSurge: intstr.FromInt(1),
} }
if rollback { if o.Rollback {
err = kubectl.AbortRollingUpdate(config) err = kubectl.AbortRollingUpdate(config)
if err != nil { if err != nil {
return err return err
@ -373,20 +447,21 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args
} }
message := "rolling updated" message := "rolling updated"
if keepOldName { if o.KeepOldName {
newRc.Name = oldName newRc.Name = o.OldName
} else { } else {
message = fmt.Sprintf("rolling updated to %q", newRc.Name) 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 { if err != nil {
return err 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 printer.PrintObj(newRc, o.Out)
return nil
} }
func findNewName(args []string, oldRc *api.ReplicationController) string { func findNewName(args []string, oldRc *api.ReplicationController) string {

View File

@ -17,10 +17,10 @@ limitations under the License.
package cmd package cmd
import ( import (
"bytes"
"testing" "testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
) )
func TestValidateArgs(t *testing.T) { func TestValidateArgs(t *testing.T) {
@ -74,8 +74,7 @@ func TestValidateArgs(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
out := &bytes.Buffer{} cmd := NewCmdRollingUpdate(f, genericclioptions.NewTestIOStreamsDiscard())
cmd := NewCmdRollingUpdate(f, out)
if test.flags != nil { if test.flags != nil {
for key, val := range test.flags { for key, val := range test.flags {

View File

@ -24,8 +24,10 @@ go_library(
"//pkg/kubectl/cmd/set:go_default_library", "//pkg/kubectl/cmd/set:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/util/i18n:go_default_library", "//pkg/kubectl/util/i18n:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/interrupt:go_default_library", "//pkg/util/interrupt:go_default_library",
"//vendor/github.com/renstrom/dedent:go_default_library", "//vendor/github.com/renstrom/dedent:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library", "//vendor/github.com/spf13/cobra:go_default_library",
@ -59,6 +61,7 @@ go_test(
"//pkg/api/legacyscheme:go_default_library", "//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions:go_default_library",
"//pkg/kubectl/cmd/testing: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:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",

View File

@ -17,12 +17,12 @@ limitations under the License.
package rollout package rollout
import ( import (
"io"
"github.com/renstrom/dedent" "github.com/renstrom/dedent"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/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{ cmd := &cobra.Command{
Use: "rollout SUBCOMMAND", Use: "rollout SUBCOMMAND",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
Short: i18n.T("Manage the rollout of a resource"), Short: i18n.T("Manage the rollout of a resource"),
Long: rollout_long, Long: rollout_long,
Example: rollout_example, Example: rollout_example,
Run: cmdutil.DefaultSubCommandRun(errOut), Run: cmdutil.DefaultSubCommandRun(streams.Out),
} }
// subcommands // subcommands
cmd.AddCommand(NewCmdRolloutHistory(f, out)) cmd.AddCommand(NewCmdRolloutHistory(f, streams.Out))
cmd.AddCommand(NewCmdRolloutPause(f, out)) cmd.AddCommand(NewCmdRolloutPause(f, streams))
cmd.AddCommand(NewCmdRolloutResume(f, out)) cmd.AddCommand(NewCmdRolloutResume(f, streams))
cmd.AddCommand(NewCmdRolloutUndo(f, out)) cmd.AddCommand(NewCmdRolloutUndo(f, streams.Out))
cmd.AddCommand(NewCmdRolloutStatus(f, out)) cmd.AddCommand(NewCmdRolloutStatus(f, streams))
return cmd return cmd
} }

View File

@ -18,7 +18,6 @@ package rollout
import ( import (
"fmt" "fmt"
"io"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -30,20 +29,24 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/set" "k8s.io/kubernetes/pkg/kubectl/cmd/set"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "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 // 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() // referencing the cmd.Flags()
type PauseConfig struct { type PauseConfig struct {
resource.FilenameOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Pauser func(info *resource.Info) ([]byte, error) Pauser func(info *resource.Info) ([]byte, error)
Mapper meta.RESTMapper Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
Out io.Writer genericclioptions.IOStreams
} }
var ( var (
@ -61,8 +64,11 @@ var (
kubectl rollout pause deployment/nginx`) kubectl rollout pause deployment/nginx`)
) )
func NewCmdRolloutPause(f cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdRolloutPause(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := &PauseConfig{} o := &PauseConfig{
PrintFlags: printers.NewPrintFlags("paused"),
IOStreams: streams,
}
validArgs := []string{"deployment"} validArgs := []string{"deployment"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -75,11 +81,11 @@ func NewCmdRolloutPause(f cmdutil.Factory, out io.Writer) *cobra.Command {
Example: pause_example, Example: pause_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
allErrs := []error{} allErrs := []error{}
err := options.CompletePause(f, cmd, out, args) err := o.CompletePause(f, cmd, args)
if err != nil { if err != nil {
allErrs = append(allErrs, err) allErrs = append(allErrs, err)
} }
err = options.RunPause() err = o.RunPause()
if err != nil { if err != nil {
allErrs = append(allErrs, err) 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." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
return cmd 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) { if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) 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.Mapper = f.RESTMapper()
o.Pauser = f.Pauser o.Pauser = f.Pauser
o.Out = out
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
@ -123,6 +128,16 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i
return err 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() o.Infos, err = r.Infos()
if err != nil { if err != nil {
return err return err
@ -140,7 +155,12 @@ func (o PauseConfig) RunPause() error {
} }
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 { 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 continue
} }
@ -151,7 +171,12 @@ func (o PauseConfig) RunPause() error {
} }
info.Refresh(obj, true) 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) return utilerrors.NewAggregate(allErrs)

View File

@ -31,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
extensions "k8s.io/kubernetes/pkg/apis/extensions" extensions "k8s.io/kubernetes/pkg/apis/extensions"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
) )
var rolloutPauseGroupVersionEncoder = schema.GroupVersion{Group: "extensions", Version: "v1beta1"} var rolloutPauseGroupVersionEncoder = schema.GroupVersion{Group: "extensions", Version: "v1beta1"}
@ -62,11 +63,11 @@ func TestRolloutPause(t *testing.T) {
} }
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdRolloutPause(tf, buf) cmd := NewCmdRolloutPause(tf, streams)
cmd.Run(cmd, []string{deploymentName}) cmd.Run(cmd, []string{deploymentName})
expectedOutput := "deployment.apps \"" + deploymentName + "\" paused\n" expectedOutput := "deployment.extensions/" + deploymentName + " paused\n"
if buf.String() != expectedOutput { if buf.String() != expectedOutput {
t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String()) t.Errorf("expected output: %s, but got: %s", expectedOutput, buf.String())
} }

View File

@ -18,7 +18,6 @@ package rollout
import ( import (
"fmt" "fmt"
"io"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -30,20 +29,24 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/set" "k8s.io/kubernetes/pkg/kubectl/cmd/set"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "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 // 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() // referencing the cmd.Flags()
type ResumeConfig struct { type ResumeConfig struct {
resource.FilenameOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Resumer func(object *resource.Info) ([]byte, error) Resumer func(object *resource.Info) ([]byte, error)
Mapper meta.RESTMapper Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
Out io.Writer genericclioptions.IOStreams
} }
var ( var (
@ -59,8 +62,11 @@ var (
kubectl rollout resume deployment/nginx`) kubectl rollout resume deployment/nginx`)
) )
func NewCmdRolloutResume(f cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdRolloutResume(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := &ResumeConfig{} o := &ResumeConfig{
PrintFlags: printers.NewPrintFlags("resumed"),
IOStreams: streams,
}
validArgs := []string{"deployment"} validArgs := []string{"deployment"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -73,11 +79,11 @@ func NewCmdRolloutResume(f cmdutil.Factory, out io.Writer) *cobra.Command {
Example: resume_example, Example: resume_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
allErrs := []error{} allErrs := []error{}
err := options.CompleteResume(f, cmd, out, args) err := o.CompleteResume(f, cmd, args)
if err != nil { if err != nil {
allErrs = append(allErrs, err) allErrs = append(allErrs, err)
} }
err = options.RunResume() err = o.RunResume()
if err != nil { if err != nil {
allErrs = append(allErrs, err) 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." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
return cmd 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) { if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) 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.Mapper = f.RESTMapper()
o.Resumer = f.Resumer o.Resumer = f.Resumer
o.Out = out
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err 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(). r := f.NewBuilder().
Internal(legacyscheme.Scheme). Internal(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -145,8 +160,12 @@ func (o ResumeConfig) RunResume() error {
} }
if string(patch.Patch) == "{}" || len(patch.Patch) == 0 { if string(patch.Patch) == "{}" || len(patch.Patch) == 0 {
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "already resumed") printer, err := o.ToPrinter("already resumed")
continue 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) 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) 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) return utilerrors.NewAggregate(allErrs)

View File

@ -18,7 +18,6 @@ package rollout
import ( import (
"fmt" "fmt"
"io"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
@ -26,6 +25,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/util/interrupt" "k8s.io/kubernetes/pkg/util/interrupt"
@ -49,8 +49,32 @@ var (
kubectl rollout status deployment/nginx`) kubectl rollout status deployment/nginx`)
) )
func NewCmdRolloutStatus(f cmdutil.Factory, out io.Writer) *cobra.Command { type RolloutStatusOptions struct {
options := &resource.FilenameOptions{} 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"} validArgs := []string{"deployment", "daemonset", "statefulset"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -62,38 +86,52 @@ func NewCmdRolloutStatus(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: status_long, Long: status_long,
Example: status_example, Example: status_example,
Run: func(cmd *cobra.Command, args []string) { 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, ValidArgs: validArgs,
ArgAliases: argAliases, ArgAliases: argAliases,
} }
usage := "identifying the resource to get from a server." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, options, usage) cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage)
cmd.Flags().BoolP("watch", "w", true, "Watch the status of the rollout until it's done.") cmd.Flags().BoolVarP(&o.Watch, "watch", "w", o.Watch, "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).") cmd.Flags().Int64Var(&o.Revision, "revision", o.Revision, "Pin to a specific revision for showing its status. Defaults to 0 (last revision).")
return cmd return cmd
} }
func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []string, options *resource.FilenameOptions) error { func (o *RolloutStatusOptions) Complete(f cmdutil.Factory, args []string) error {
if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) { o.Builder = f.NewBuilder()
return cmdutil.UsageErrorf(cmd, "Required resource not specified.")
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() var err error
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
if err != nil { if err != nil {
return err 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). Internal(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(o.EnforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, o.BuilderArgs...).
SingleResourceType(). SingleResourceType().
Latest(). Latest().
Do() Do()
err = r.Err() err := r.Err()
if err != nil { if err != nil {
return err return err
} }
@ -117,12 +155,12 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri
return err return err
} }
statusViewer, err := f.StatusViewer(mapping) statusViewer, err := o.StatusViewer(mapping)
if err != nil { if err != nil {
return err return err
} }
revision := cmdutil.GetFlagInt64(cmd, "revision") revision := o.Revision
if revision < 0 { if revision < 0 {
return fmt.Errorf("revision must be a positive integer: %v", revision) 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 { if err != nil {
return err return err
} }
fmt.Fprintf(out, "%s", status) fmt.Fprintf(o.Out, "%s", status)
if done { if done {
return nil return nil
} }
shouldWatch := cmdutil.GetFlagBool(cmd, "watch") shouldWatch := o.Watch
if !shouldWatch { if !shouldWatch {
return nil return nil
} }
@ -157,7 +195,7 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri
if err != nil { if err != nil {
return false, err return false, err
} }
fmt.Fprintf(out, "%s", status) fmt.Fprintf(o.Out, "%s", status)
// Quit waiting if the rollout is done // Quit waiting if the rollout is done
if done { if done {
return true, nil return true, nil

View File

@ -27,6 +27,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -36,6 +37,9 @@ import (
type UndoOptions struct { type UndoOptions struct {
resource.FilenameOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Rollbackers []kubectl.Rollbacker Rollbackers []kubectl.Rollbacker
Mapper meta.RESTMapper Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
@ -61,7 +65,9 @@ var (
) )
func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &UndoOptions{} o := &UndoOptions{
PrintFlags: printers.NewPrintFlags(""),
}
validArgs := []string{"deployment", "daemonset", "statefulset"} validArgs := []string{"deployment", "daemonset", "statefulset"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -74,11 +80,11 @@ func NewCmdRolloutUndo(f cmdutil.Factory, out io.Writer) *cobra.Command {
Example: undo_example, Example: undo_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
allErrs := []error{} allErrs := []error{}
err := options.CompleteUndo(f, cmd, out, args) err := o.CompleteUndo(f, cmd, out, args)
if err != nil { if err != nil {
allErrs = append(allErrs, err) allErrs = append(allErrs, err)
} }
err = options.RunUndo() err = o.RunUndo()
if err != nil { if err != nil {
allErrs = append(allErrs, err) 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).") 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." usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddDryRunFlag(cmd) cmdutil.AddDryRunFlag(cmd)
return cmd return cmd
} }
@ -110,6 +116,19 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io
return err 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(). r := f.NewBuilder().
Internal(legacyscheme.Scheme). Internal(legacyscheme.Scheme).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -147,7 +166,12 @@ func (o *UndoOptions) RunUndo() error {
allErrs = append(allErrs, cmdutil.AddSourceToErr("undoing", info.Source, err)) allErrs = append(allErrs, cmdutil.AddSourceToErr("undoing", info.Source, err))
continue 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) return utilerrors.NewAggregate(allErrs)
} }

View File

@ -18,12 +18,14 @@ package cmd
import ( import (
"fmt" "fmt"
"io" "time"
"github.com/spf13/cobra"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types" "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" batchclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/scalejob" "k8s.io/kubernetes/pkg/kubectl/cmd/scalejob"
@ -32,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
) )
var ( var (
@ -64,21 +67,49 @@ var (
type ScaleOptions struct { type ScaleOptions struct {
FilenameOptions resource.FilenameOptions FilenameOptions resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags 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 Recorder genericclioptions.Recorder
genericclioptions.IOStreams
} }
func NewScaleOptions() *ScaleOptions { func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions {
return &ScaleOptions{ return &ScaleOptions{
RecordFlags: genericclioptions.NewRecordFlags(), 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 // 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 { func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewScaleOptions() o := NewScaleOptions(streams)
validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"} validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -90,33 +121,58 @@ func NewCmdScale(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: scaleLong, Long: scaleLong,
Example: scaleExample, Example: scaleExample,
Run: func(cmd *cobra.Command, args []string) { 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)) cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
shortOutput := cmdutil.GetFlagString(cmd, "output") == "name" cmdutil.CheckErr(o.RunScale())
cmdutil.CheckErr(o.RunScale(f, out, errOut, cmd, args, shortOutput))
}, },
ValidArgs: validArgs, ValidArgs: validArgs,
ArgAliases: argAliases, ArgAliases: argAliases,
} }
o.RecordFlags.AddFlags(cmd) 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().StringVarP(&o.Selector, "selector", "l", o.Selector, "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().BoolVar(&o.All, "all", o.All, "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().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().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().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().Int("replicas", -1, "The new desired number of replicas. Required.") cmd.Flags().IntVar(&o.Replicas, "replicas", o.Replicas, "The new desired number of replicas. Required.")
cmd.MarkFlagRequired("replicas") 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).") 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).")
cmdutil.AddOutputFlagsForMutation(cmd)
usage := "identifying the resource to set a new size" usage := "identifying the resource to set a new size"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
return cmd 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 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.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder() 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 err return nil
} }
// RunScale executes the scaling // RunScale executes the scaling
func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args []string, shortOutput bool) error { func (o *ScaleOptions) RunScale() error {
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil { if o.Replicas < 0 {
return err return fmt.Errorf("The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0")
} }
count := cmdutil.GetFlagInt(cmd, "replicas") r := o.Builder.
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().
Unstructured(). Unstructured().
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(all, args...). ResourceTypeOrNameArgs(o.All, o.BuilderArgs...).
Flatten(). Flatten().
LabelSelectorParam(selector). LabelSelectorParam(o.Selector).
Do() Do()
err = r.Err() err := r.Err()
if resource.IsUsageError(err) { if resource.IsUsageError(err) {
return cmdutil.UsageErrorf(cmd, "%v", err) return fmt.Errorf("%v", err)
} }
if err != nil { if err != nil {
return err return err
@ -167,17 +215,16 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c
return nil return nil
}) })
resourceVersion := cmdutil.GetFlagString(cmd, "resource-version") if len(o.ResourceVersion) != 0 && len(infos) > 1 {
if len(resourceVersion) != 0 && len(infos) > 1 {
return fmt.Errorf("cannot use --resource-version with multiple resources") return fmt.Errorf("cannot use --resource-version with multiple resources")
} }
currentSize := cmdutil.GetFlagInt(cmd, "current-replicas") currentSize := o.CurrentReplicas
precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: resourceVersion} precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: o.ResourceVersion}
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)
var waitForReplicas *kubectl.RetryParams var waitForReplicas *kubectl.RetryParams
if timeout := cmdutil.GetFlagDuration(cmd, "timeout"); timeout != 0 { if timeout := o.Duration; timeout != 0 {
waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) 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() mapping := info.ResourceMapping()
if mapping.Resource == "jobs" { if mapping.Resource == "jobs" {
// go down the legacy jobs path. This can be removed in 3.14 For now, contain it. // 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 := ScaleJob(info, o.ClientSet.Batch(), uint(o.Replicas), precondition, retry, waitForReplicas); err != nil {
if err != nil {
return err
}
if err := ScaleJob(info, clientset.Batch(), uint(count), precondition, retry, waitForReplicas); err != nil {
return err return err
} }
} else { } else {
scaler, err := f.Scaler()
if err != nil {
return err
}
gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource) 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 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 { if mergePatch, err := o.Recorder.MakeRecordMergePatch(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err) glog.V(4).Infof("error recording current command: %v", err)
} else if len(mergePatch) > 0 { } else if len(mergePatch) > 0 {
client, err := f.UnstructuredClientForMapping(mapping) client, err := o.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }
@ -227,8 +265,7 @@ func (o *ScaleOptions) RunScale(f cmdutil.Factory, out, errOut io.Writer, cmd *c
} }
counter++ counter++
cmdutil.PrintSuccess(shortOutput, out, info.Object, false, "scaled") return o.PrintObj(info.Object, o.Out)
return nil
}) })
if err != nil { if err != nil {
return err return err

View File

@ -18,7 +18,6 @@ package cmd
import ( import (
"fmt" "fmt"
"io"
"strings" "strings"
"encoding/json" "encoding/json"
@ -26,6 +25,7 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"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/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
@ -34,13 +34,18 @@ import (
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" 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/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
taintutils "k8s.io/kubernetes/pkg/util/taints" taintutils "k8s.io/kubernetes/pkg/util/taints"
) )
// TaintOptions have the data required to perform the taint operation // TaintOptions have the data required to perform the taint operation
type TaintOptions struct { type TaintOptions struct {
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
resources []string resources []string
taintsToAdd []v1.Taint taintsToAdd []v1.Taint
taintsToRemove []v1.Taint taintsToRemove []v1.Taint
@ -48,9 +53,10 @@ type TaintOptions struct {
selector string selector string
overwrite bool overwrite bool
all bool all bool
f cmdutil.Factory
out io.Writer ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error)
cmd *cobra.Command
genericclioptions.IOStreams
} }
var ( var (
@ -79,8 +85,11 @@ var (
kubectl taint node -l myLabel=X dedicated=foo:PreferNoSchedule`)) kubectl taint node -l myLabel=X dedicated=foo:PreferNoSchedule`))
) )
func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command { func NewCmdTaint(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := &TaintOptions{} options := &TaintOptions{
PrintFlags: printers.NewPrintFlags("tainted"),
IOStreams: streams,
}
validArgs := []string{"node"} validArgs := []string{"node"}
argAliases := kubectl.ResourceAliases(validArgs) 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), Long: fmt.Sprintf(taintLong, validation.DNS1123SubdomainMaxLength, validation.LabelValueMaxLength),
Example: taintExample, Example: taintExample,
Run: func(cmd *cobra.Command, args []string) { 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) cmdutil.CheckErr(err)
} }
if err := options.Validate(); err != nil { if err := options.Validate(); err != nil {
@ -105,9 +114,10 @@ func NewCmdTaint(f cmdutil.Factory, out io.Writer) *cobra.Command {
ValidArgs: validArgs, ValidArgs: validArgs,
ArgAliases: argAliases, 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().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.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") 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. // 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() namespace, _, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err 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 { if len(o.resources) < 1 {
return fmt.Errorf("one or more resources must be specified as <resource> <name>") return fmt.Errorf("one or more resources must be specified as <resource> <name>")
} }
@ -166,9 +186,8 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
o.builder = o.builder.LabelSelectorParam(o.selector). o.builder = o.builder.LabelSelectorParam(o.selector).
Flatten(). Flatten().
Latest() Latest()
o.f = f
o.out = out o.ClientForMapping = f.ClientForMapping
o.cmd = cmd
return nil return nil
} }
@ -258,7 +277,7 @@ func (o TaintOptions) RunTaint() error {
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := o.f.ClientForMapping(mapping) client, err := o.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }
@ -274,13 +293,11 @@ func (o TaintOptions) RunTaint() error {
return err return err
} }
outputFormat := cmdutil.GetFlagString(o.cmd, "output") printer, err := o.ToPrinter(operation)
if outputFormat != "" { if err != nil {
return cmdutil.PrintObject(o.cmd, outputObj, o.out) return err
} }
return printer.PrintObj(outputObj, o.Out)
cmdutil.PrintSuccess(false, o.out, info.Object, false, operation)
return nil
}) })
} }

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd package cmd
import ( import (
"bytes"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"reflect" "reflect"
@ -32,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
) )
@ -302,8 +302,7 @@ func TestTaint(t *testing.T) {
} }
tf.ClientConfigVal = defaultClientConfig() tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{}) cmd := NewCmdTaint(tf, genericclioptions.NewTestIOStreamsDiscard())
cmd := NewCmdTaint(tf, buf)
saw_fatal := false saw_fatal := false
func() { func() {