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:",
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),
},
},
{

View File

@ -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 {

View File

@ -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 {

View File

@ -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",

View File

@ -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
}

View File

@ -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)

View File

@ -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())
}

View File

@ -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)

View File

@ -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

View File

@ -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)
}

View File

@ -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

View File

@ -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 <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).
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)
})
}

View File

@ -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() {