use recordFlags

This commit is contained in:
David Eads 2018-04-19 08:32:15 -04:00
parent b1357da473
commit 484d81ab01
18 changed files with 318 additions and 237 deletions

View File

@ -196,6 +196,7 @@ go_test(
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",

View File

@ -103,11 +103,13 @@ func NewAnnotateOptions(out io.Writer) *AnnotateOptions {
return &AnnotateOptions{
out: out,
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
}
}
func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := NewAnnotateOptions(out)
o := NewAnnotateOptions(out)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -117,30 +119,30 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: annotateLong + "\n\n" + cmdutil.ValidResourceTypeList(f),
Example: annotateExample,
Run: func(cmd *cobra.Command, args []string) {
if err := options.Complete(f, cmd, args); err != nil {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
if err := options.Validate(); err != nil {
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
cmdutil.CheckErr(options.RunAnnotate(f, cmd))
cmdutil.CheckErr(o.RunAnnotate(f, cmd))
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs
options.RecordFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
cmd.Flags().BoolVar(&options.overwrite, "overwrite", options.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, annotation will NOT contact api-server but run locally.")
cmd.Flags().StringVarP(&options.selector, "selector", "l", options.selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().BoolVar(&options.all, "all", options.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().StringVar(&options.resourceVersion, "resource-version", options.resourceVersion, i18n.T("If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
cmd.Flags().BoolVar(&o.overwrite, "overwrite", o.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, annotation will NOT contact api-server but run locally.")
cmd.Flags().StringVarP(&o.selector, "selector", "l", o.selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).")
cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.")
cmd.Flags().StringVar(&o.resourceVersion, "resource-version", o.resourceVersion, i18n.T("If non-empty, the annotation update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource."))
usage := "identifying the resource to update the annotation"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
@ -149,9 +151,9 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
// Complete adapts from the command line args and factory to the data required.
func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err

View File

@ -119,17 +119,19 @@ func NewApplyOptions(out, errout io.Writer) *ApplyOptions {
GracePeriod: -1,
OpenApiPatch: true,
Recorder: genericclioptions.NoopRecorder{},
Out: out,
ErrOut: errout,
}
}
func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := NewApplyOptions(out, errOut)
o := NewApplyOptions(out, errOut)
// Store baseName for use in printing warnings / messages involving the base command name.
// This is useful for downstream command that wrap this one.
options.cmdBaseName = baseName
o.cmdBaseName = baseName
cmd := &cobra.Command{
Use: "apply -f FILENAME",
@ -139,29 +141,29 @@ func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cob
Example: applyExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(validateArgs(cmd, args))
cmdutil.CheckErr(validatePruneAll(options.Prune, options.All, options.Selector))
cmdutil.CheckErr(options.Complete(f, cmd))
cmdutil.CheckErr(options.Run(f, cmd))
cmdutil.CheckErr(validatePruneAll(o.Prune, o.All, o.Selector))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run(f, cmd))
},
}
// bind flag structs
options.RecordFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
usage := "that contains the configuration to apply"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.MarkFlagRequired("filename")
cmd.Flags().BoolVar(&options.Overwrite, "overwrite", options.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
cmd.Flags().BoolVar(&options.Prune, "prune", options.Prune, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")
cmd.Flags().BoolVar(&options.Cascade, "cascade", options.Cascade, "Only relevant during a prune or a force apply. If true, cascade the deletion of the resources managed by pruned or deleted resources (e.g. Pods created by a ReplicationController).")
cmd.Flags().IntVar(&options.GracePeriod, "grace-period", options.GracePeriod, "Only relevant during a prune or a force apply. Period of time in seconds given to pruned or deleted resources to terminate gracefully. Ignored if negative.")
cmd.Flags().BoolVar(&options.Force, "force", options.Force, fmt.Sprintf("Delete and re-create the specified resource, when PATCH encounters conflict and has retried for %d times.", maxPatchRetry))
cmd.Flags().DurationVar(&options.Timeout, "timeout", options.Timeout, "Only relevant during a force apply. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
cmd.Flags().BoolVar(&o.Prune, "prune", o.Prune, "Automatically delete resource objects, including the uninitialized ones, that do not appear in the configs and are created by either apply or create --save-config. Should be used with either -l or --all.")
cmd.Flags().BoolVar(&o.Cascade, "cascade", o.Cascade, "Only relevant during a prune or a force apply. If true, cascade the deletion of the resources managed by pruned or deleted resources (e.g. Pods created by a ReplicationController).")
cmd.Flags().IntVar(&o.GracePeriod, "grace-period", o.GracePeriod, "Only relevant during a prune or a force apply. Period of time in seconds given to pruned or deleted resources to terminate gracefully. Ignored if negative.")
cmd.Flags().BoolVar(&o.Force, "force", o.Force, fmt.Sprintf("Delete and re-create the specified resource, when PATCH encounters conflict and has retried for %d times.", maxPatchRetry))
cmd.Flags().DurationVar(&o.Timeout, "timeout", o.Timeout, "Only relevant during a force apply. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
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.All, "all", options.All, "Select all resources in the namespace of the specified resource types.")
cmd.Flags().StringArrayVar(&options.PruneWhitelist, "prune-whitelist", options.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")
cmd.Flags().BoolVar(&options.OpenApiPatch, "openapi-patch", options.OpenApiPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
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().StringArrayVar(&o.PruneWhitelist, "prune-whitelist", o.PruneWhitelist, "Overwrite the default whitelist with <group/version/kind> for --prune")
cmd.Flags().BoolVar(&o.OpenApiPatch, "openapi-patch", o.OpenApiPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
@ -224,9 +226,9 @@ func parsePruneResources(mapper meta.RESTMapper, gvks []string) ([]pruneResource
}
func (o *ApplyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err

View File

@ -58,6 +58,7 @@ func NewAutoscaleOptions() *AutoscaleOptions {
return &AutoscaleOptions{
FilenameOptions: resource.FilenameOptions{},
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
}
}
@ -100,9 +101,9 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
}
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err

View File

@ -79,6 +79,8 @@ func NewCreateOptions(out, errOut io.Writer) *CreateOptions {
PrintFlags: NewPrintFlags("created"),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
Out: out,
ErrOut: errOut,
}
@ -173,9 +175,9 @@ func (o *CreateOptions) ValidateArgs(cmd *cobra.Command, args []string) error {
}
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err

View File

@ -25,6 +25,7 @@ import (
"github.com/docker/distribution/reference"
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -36,6 +37,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"
@ -93,14 +95,16 @@ type RunObject struct {
Mapping *meta.RESTMapping
}
type RunOpts struct {
type RunOptions struct {
PrintFlags *printers.PrintFlags
DeleteFlags *DeleteFlags
DeleteOptions *DeleteOptions
RecordFlags *genericclioptions.RecordFlags
DryRun bool
PrintObj func(runtime.Object) error
Recorder genericclioptions.Recorder
ArgsLenAtDash int
Attach bool
@ -111,7 +115,6 @@ type RunOpts struct {
LeaveStdinOpen bool
Port string
Quiet bool
Record bool
Schedule string
TTY bool
@ -120,15 +123,22 @@ type RunOpts struct {
ErrOut io.Writer
}
func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
options := &RunOpts{
func NewRunOptions(in io.Reader, out, errOut io.Writer) *RunOptions {
return &RunOptions{
PrintFlags: printers.NewPrintFlags("created"),
DeleteFlags: NewDeleteFlags("to use to replace the resource."),
RecordFlags: genericclioptions.NewRecordFlags(),
In: cmdIn,
Out: cmdOut,
ErrOut: cmdErr,
Recorder: genericclioptions.NoopRecorder{},
In: in,
Out: out,
ErrOut: errOut,
}
}
func NewCmdRun(f cmdutil.Factory, in io.Reader, out, errOut io.Writer) *cobra.Command {
o := NewRunOptions(in, out, errOut)
cmd := &cobra.Command{
Use: "run NAME --image=image [--env=\"key=value\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]",
@ -137,17 +147,17 @@ func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *co
Long: runLong,
Example: runExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd))
cmdutil.CheckErr(options.Run(f, cmd, args))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run(f, cmd, args))
},
}
options.DeleteFlags.AddFlags(cmd)
options.PrintFlags.AddFlags(cmd)
o.DeleteFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
addRunFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
return cmd
}
@ -181,7 +191,15 @@ func addRunFlags(cmd *cobra.Command) {
cmd.Flags().String("schedule", "", i18n.T("A schedule in the Cron format the job should be run with."))
}
func (o *RunOpts) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
func (o *RunOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.ArgsLenAtDash = cmd.ArgsLenAtDash()
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.Expose = cmdutil.GetFlagBool(cmd, "expose")
@ -191,7 +209,6 @@ func (o *RunOpts) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.LeaveStdinOpen = cmdutil.GetFlagBool(cmd, "leave-stdin-open")
o.Port = cmdutil.GetFlagString(cmd, "port")
o.Quiet = cmdutil.GetFlagBool(cmd, "quiet")
o.Record = cmdutil.GetRecordFlag(cmd)
o.Schedule = cmdutil.GetFlagString(cmd, "schedule")
o.TTY = cmdutil.GetFlagBool(cmd, "tty")
@ -223,7 +240,7 @@ func (o *RunOpts) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return nil
}
func (o *RunOpts) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func (o *RunOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
// Let kubectl run follow rules for `--`, see #13004 issue
if len(args) == 0 || o.ArgsLenAtDash == 0 {
return cmdutil.UsageErrorf(cmd, "NAME is required for run")
@ -432,7 +449,7 @@ func (o *RunOpts) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) erro
return utilerrors.NewAggregate(allErrs)
}
func (o *RunOpts) removeCreatedObjects(f cmdutil.Factory, createdObjects []*RunObject) error {
func (o *RunOptions) removeCreatedObjects(f cmdutil.Factory, createdObjects []*RunObject) error {
for _, obj := range createdObjects {
namespace, err := obj.Mapping.MetadataAccessor.Namespace(obj.Object)
if err != nil {
@ -571,7 +588,7 @@ func verifyImagePullPolicy(cmd *cobra.Command) error {
return cmdutil.UsageErrorf(cmd, "invalid image pull policy: %s", pullPolicy)
}
func (o *RunOpts) generateService(f cmdutil.Factory, cmd *cobra.Command, serviceGenerator string, paramsIn map[string]interface{}, namespace string) (*RunObject, error) {
func (o *RunOptions) generateService(f cmdutil.Factory, cmd *cobra.Command, serviceGenerator string, paramsIn map[string]interface{}, namespace string) (*RunObject, error) {
generators := f.Generators("expose")
generator, found := generators[serviceGenerator]
if !found {
@ -617,7 +634,7 @@ func (o *RunOpts) generateService(f cmdutil.Factory, cmd *cobra.Command, service
return runObject, nil
}
func (o *RunOpts) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (*RunObject, error) {
func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (*RunObject, error) {
err := kubectl.ValidateParams(names, params)
if err != nil {
return nil, err
@ -653,14 +670,8 @@ func (o *RunOpts) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, g
return nil, err
}
annotations, err := mapping.MetadataAccessor.Annotations(obj)
if err != nil {
return nil, err
}
if o.Record || len(annotations[kubectl.ChangeCauseAnnotation]) > 0 {
if err := cmdutil.RecordChangeCause(obj, f.Command(cmd, false)); err != nil {
return nil, err
}
if err := o.Recorder.Record(obj); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
versioned := obj

View File

@ -39,6 +39,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
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"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
@ -205,7 +206,7 @@ func TestRunArgsFollowDashRules(t *testing.T) {
}
deleteFlags := NewDeleteFlags("to use to replace the resource.")
opts := &RunOpts{
opts := &RunOptions{
PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr),
@ -219,6 +220,7 @@ func TestRunArgsFollowDashRules(t *testing.T) {
PrintObj: func(obj runtime.Object) error {
return printer.PrintObj(obj, os.Stdout)
},
Recorder: genericclioptions.NoopRecorder{},
ArgsLenAtDash: test.argsLenAtDash,
}
@ -375,15 +377,15 @@ func TestGenerateService(t *testing.T) {
deleteFlags := NewDeleteFlags("to use to replace the resource.")
buff := &bytes.Buffer{}
opts := &RunOpts{
opts := &RunOptions{
PrintFlags: printFlags,
DeleteOptions: deleteFlags.ToOptions(os.Stdout, os.Stderr),
Out: buff,
ErrOut: buff,
Port: test.port,
Record: false,
Port: test.port,
Recorder: genericclioptions.NoopRecorder{},
PrintObj: func(obj runtime.Object) error {
return printer.PrintObj(obj, buff)

View File

@ -24,9 +24,11 @@ go_library(
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/env: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",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/printers"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -29,16 +30,18 @@ import (
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"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"
)
// ImageOptions 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 ImageOptions struct {
type SetImageOptions struct {
resource.FilenameOptions
PrintFlags *printers.PrintFlags
PrintFlags *printers.PrintFlags
RecordFlags *genericclioptions.RecordFlags
Infos []*resource.Info
Selector string
@ -46,7 +49,6 @@ type ImageOptions struct {
Err io.Writer
DryRun bool
All bool
Record bool
Output string
ChangeCause string
Local bool
@ -54,6 +56,7 @@ type ImageOptions struct {
ResolveImage func(in string) (string, error)
PrintObj printers.ResourcePrinterFunc
Recorder genericclioptions.Recorder
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
Resources []string
@ -84,13 +87,20 @@ var (
kubectl set image -f path/to/file.yaml nginx=nginx:1.9.1 --local -o yaml`)
)
func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &ImageOptions{
PrintFlags: printers.NewPrintFlags("image updated"),
func NewImageOptions(out, errOut io.Writer) *SetImageOptions {
return &SetImageOptions{
PrintFlags: printers.NewPrintFlags("image updated"),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
Out: out,
Err: err,
Err: errOut,
}
}
func NewCmdImage(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := NewImageOptions(out, errOut)
cmd := &cobra.Command{
Use: "image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N",
@ -99,28 +109,35 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
Long: image_long,
Example: image_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&options.Local, "local", options.Local, "If true, set image will NOT contact api-server but run locally.")
cmdutil.AddRecordFlag(cmd)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set image will NOT contact api-server but run locally.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}
func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func (o *SetImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command(cmd, false)
o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.Output = cmdutil.GetFlagString(cmd, "output")
@ -178,7 +195,7 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
return nil
}
func (o *ImageOptions) Validate() error {
func (o *SetImageOptions) Validate() error {
errors := []error{}
if o.All && len(o.Selector) > 0 {
errors = append(errors, fmt.Errorf("cannot set --all and --selector at the same time"))
@ -194,7 +211,7 @@ func (o *ImageOptions) Validate() error {
return utilerrors.NewAggregate(errors)
}
func (o *ImageOptions) Run() error {
func (o *SetImageOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
@ -236,10 +253,18 @@ func (o *ImageOptions) Run() error {
}
return nil
})
if transformed && err == nil {
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
if err != nil {
return nil, err
}
return nil, err
if !transformed {
return nil, nil
}
// record this change (for rollout history)
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
})
for _, patch := range patches {
@ -269,17 +294,6 @@ func (o *ImageOptions) Run() error {
}
info.Refresh(obj, true)
// record this change (for rollout history)
if o.Record || cmdutil.ContainsChangeCause(info) {
if patch, patchType, err := cmdutil.ChangeResourcePatch(info, o.ChangeCause); err == nil {
if obj, err = resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, patchType, patch); err != nil {
fmt.Fprintf(o.Err, "WARNING: changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err)
}
}
}
info.Refresh(obj, true)
if err := o.PrintObj(info.AsVersioned(), o.Out); err != nil {
return err
}

View File

@ -71,7 +71,7 @@ func TestImageLocal(t *testing.T) {
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := ImageOptions{
opts := SetImageOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -105,17 +105,17 @@ func TestSetImageValidation(t *testing.T) {
testCases := []struct {
name string
imageOptions *ImageOptions
imageOptions *SetImageOptions
expectErr string
}{
{
name: "test resource < 1 and filenames empty",
imageOptions: &ImageOptions{PrintFlags: printFlags},
imageOptions: &SetImageOptions{PrintFlags: printFlags},
expectErr: "[one or more resources must be specified as <resource> <name> or <resource>/<name>, at least one image update is required]",
},
{
name: "test containerImages < 1",
imageOptions: &ImageOptions{
imageOptions: &SetImageOptions{
PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
@ -127,7 +127,7 @@ func TestSetImageValidation(t *testing.T) {
},
{
name: "test containerImages > 1 and all containers are already specified by *",
imageOptions: &ImageOptions{
imageOptions: &SetImageOptions{
PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
FilenameOptions: resource.FilenameOptions{
@ -142,7 +142,7 @@ func TestSetImageValidation(t *testing.T) {
},
{
name: "success case",
imageOptions: &ImageOptions{
imageOptions: &SetImageOptions{
PrintFlags: printFlags,
Resources: []string{"a", "b", "c"},
FilenameOptions: resource.FilenameOptions{
@ -193,7 +193,7 @@ func TestSetMultiResourcesImageLocal(t *testing.T) {
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := ImageOptions{
opts := SetImageOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -587,7 +587,7 @@ func TestSetImageRemote(t *testing.T) {
cmd := NewCmdImage(tf, out, out)
cmd.SetOutput(out)
cmd.Flags().Set("output", outputFormat)
opts := ImageOptions{
opts := SetImageOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),

View File

@ -26,12 +26,14 @@ import (
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"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"
)
@ -60,10 +62,11 @@ var (
// ResourcesOptions 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 ResourcesOptions struct {
type SetResourcesOptions struct {
resource.FilenameOptions
PrintFlags *printers.PrintFlags
PrintFlags *printers.PrintFlags
RecordFlags *genericclioptions.RecordFlags
Infos []*resource.Info
Out io.Writer
@ -72,7 +75,6 @@ type ResourcesOptions struct {
ContainerSelector string
Output string
All bool
Record bool
ChangeCause string
Local bool
Cmd *cobra.Command
@ -80,6 +82,7 @@ type ResourcesOptions struct {
DryRun bool
PrintObj printers.ResourcePrinterFunc
Recorder genericclioptions.Recorder
Limits string
Requests string
@ -91,9 +94,12 @@ type ResourcesOptions struct {
// NewResourcesOptions returns a ResourcesOptions indicating all containers in the selected
// pod templates are selected by default.
func NewResourcesOptions(out io.Writer, errOut io.Writer) *ResourcesOptions {
return &ResourcesOptions{
PrintFlags: printers.NewPrintFlags("resource requirements updated"),
func NewResourcesOptions(out io.Writer, errOut io.Writer) *SetResourcesOptions {
return &SetResourcesOptions{
PrintFlags: printers.NewPrintFlags("resource requirements updated"),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
Out: out,
Err: errOut,
@ -102,7 +108,7 @@ func NewResourcesOptions(out io.Writer, errOut io.Writer) *ResourcesOptions {
}
func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
options := NewResourcesOptions(out, errOut)
o := NewResourcesOptions(out, errOut)
resourceTypesWithPodTemplate := []string{}
for _, resource := range f.SuggestedPodTemplateResources() {
@ -116,34 +122,41 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.
Long: fmt.Sprintf(resources_long, strings.Join(resourceTypesWithPodTemplate, ", ")),
Example: resources_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run())
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
//usage := "Filename, directory, or URL to a file identifying the resource to get from the server"
//kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, not including uninitialized ones,supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().StringVarP(&options.ContainerSelector, "containers", "c", options.ContainerSelector, "The names of containers in the selected pod templates to change, all containers are selected by default - may use wildcards")
cmd.Flags().BoolVar(&options.Local, "local", options.Local, "If true, set resources will NOT contact api-server but run locally.")
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, not including uninitialized ones,supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().StringVarP(&o.ContainerSelector, "containers", "c", o.ContainerSelector, "The names of containers in the selected pod templates to change, all containers are selected by default - may use wildcards")
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set resources will NOT contact api-server but run locally.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
cmd.Flags().StringVar(&options.Limits, "limits", options.Limits, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
cmd.Flags().StringVar(&options.Requests, "requests", options.Requests, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
cmd.Flags().StringVar(&o.Limits, "limits", o.Limits, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
cmd.Flags().StringVar(&o.Requests, "requests", o.Requests, "The resource requirement requests for this container. For example, 'cpu=100m,memory=256Mi'. Note that server side components may assign requests depending on the server configuration, such as limit ranges.")
return cmd
}
func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func (o *SetResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.Output = cmdutil.GetFlagString(cmd, "output")
o.Record = cmdutil.GetRecordFlag(cmd)
o.ChangeCause = f.Command(cmd, false)
o.Cmd = cmd
o.DryRun = cmdutil.GetDryRunFlag(o.Cmd)
@ -194,7 +207,7 @@ func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return nil
}
func (o *ResourcesOptions) Validate() error {
func (o *SetResourcesOptions) Validate() error {
var err error
if o.All && len(o.Selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
@ -211,7 +224,7 @@ func (o *ResourcesOptions) Validate() error {
return nil
}
func (o *ResourcesOptions) Run() error {
func (o *SetResourcesOptions) Run() error {
allErrs := []error{}
patches := CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
transformed := false
@ -240,10 +253,18 @@ func (o *ResourcesOptions) Run() error {
}
return nil
})
if transformed && err == nil {
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
if err != nil {
return nil, err
}
return nil, err
if !transformed {
return nil, nil
}
// record this change (for rollout history)
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
})
for _, patch := range patches {
@ -273,16 +294,6 @@ func (o *ResourcesOptions) Run() error {
}
info.Refresh(obj, true)
//record this change (for rollout history)
if o.Record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(obj, o.ChangeCause); err == nil {
if obj, err = resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, obj); err != nil {
allErrs = append(allErrs, fmt.Errorf("changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err))
}
}
}
info.Refresh(obj, true)
if err := o.PrintObj(info.AsVersioned(), o.Out); err != nil {
return err
}

View File

@ -70,7 +70,7 @@ func TestResourcesLocal(t *testing.T) {
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := ResourcesOptions{
opts := SetResourcesOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -124,7 +124,7 @@ func TestSetMultiResourcesLimitsLocal(t *testing.T) {
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := ResourcesOptions{
opts := SetResourcesOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -505,7 +505,7 @@ func TestSetResourcesRemote(t *testing.T) {
cmd := NewCmdResources(tf, buf, buf)
cmd.SetOutput(buf)
cmd.Flags().Set("output", outputFormat)
opts := ResourcesOptions{
opts := SetResourcesOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),

View File

@ -22,6 +22,7 @@ import (
"k8s.io/kubernetes/pkg/printers"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta"
@ -31,16 +32,18 @@ import (
"k8s.io/apimachinery/pkg/util/validation"
"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"
)
// SelectorOptions 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 SelectorOptions struct {
type SetSelectorOptions struct {
fileOptions resource.FilenameOptions
PrintFlags *printers.PrintFlags
PrintFlags *printers.PrintFlags
RecordFlags *genericclioptions.RecordFlags
local bool
dryrun bool
@ -56,6 +59,7 @@ type SelectorOptions struct {
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
PrintObj printers.ResourcePrinterFunc
Recorder genericclioptions.Recorder
builder *resource.Builder
mapper meta.RESTMapper
@ -75,13 +79,20 @@ var (
kubectl create deployment my-dep -o yaml --dry-run | kubectl label --local -f - environment=qa -o yaml | kubectl create -f -`)
)
// NewCmdSelector is the "set selector" command.
func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &SelectorOptions{
PrintFlags: printers.NewPrintFlags("selector updated"),
func NewSelectorOptions(out io.Writer) *SetSelectorOptions {
return &SetSelectorOptions{
PrintFlags: printers.NewPrintFlags("selector updated"),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
out: out,
}
}
// NewCmdSelector is the "set selector" command.
func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
o := NewSelectorOptions(out)
cmd := &cobra.Command{
Use: "selector (-f FILENAME | TYPE NAME) EXPRESSIONS [--resource-version=version]",
@ -90,31 +101,38 @@ func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: fmt.Sprintf(selectorLong, validation.LabelValueMaxLength),
Example: selectorExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.RunSelector())
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunSelector())
},
}
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().Bool("local", false, "If true, set selector will NOT contact api-server but run locally.")
cmd.Flags().String("resource-version", "", "If non-empty, the selectors update will only succeed if this is the current resource-version for the object. Only valid when specifying a single resource.")
usage := "the resource to update the selectors"
cmdutil.AddFilenameOptionFlags(cmd, &options.fileOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.fileOptions, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}
// Complete assigns the SelectorOptions from args.
func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.local = cmdutil.GetFlagBool(cmd, "local")
o.all = cmdutil.GetFlagBool(cmd, "all")
o.record = cmdutil.GetRecordFlag(cmd)
o.dryrun = cmdutil.GetDryRunFlag(cmd)
o.output = cmdutil.GetFlagString(cmd, "output")
@ -171,7 +189,7 @@ func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
}
// Validate basic inputs
func (o *SelectorOptions) Validate() error {
func (o *SetSelectorOptions) Validate() error {
if len(o.resources) < 1 && cmdutil.IsFilenameSliceEmpty(o.fileOptions.Filenames) {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
@ -182,7 +200,7 @@ func (o *SelectorOptions) Validate() error {
}
// RunSelector executes the command.
func (o *SelectorOptions) RunSelector() error {
func (o *SetSelectorOptions) RunSelector() error {
r := o.builder.Do()
err := r.Err()
if err != nil {
@ -195,11 +213,16 @@ func (o *SelectorOptions) RunSelector() error {
versioned := info.AsVersioned()
patch.Info.Object = versioned
selectErr := updateSelectorForObject(info.Object, *o.selector)
if selectErr == nil {
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
if selectErr != nil {
return nil, selectErr
}
return nil, selectErr
// record this change (for rollout history)
if err := o.Recorder.Record(patch.Info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
})
if patch.Err != nil {
@ -214,14 +237,6 @@ func (o *SelectorOptions) RunSelector() error {
return err
}
if o.record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(patched, o.changeCause); err == nil {
if patched, err = resource.NewHelper(info.Client, info.Mapping).Replace(info.Namespace, info.Name, false, patched); err != nil {
return fmt.Errorf("changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err)
}
}
}
info.Refresh(patched, true)
return o.PrintObj(patch.Info.AsVersioned(), o.out)
})

View File

@ -25,12 +25,14 @@ import (
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"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"
)
@ -55,8 +57,9 @@ var (
)
// serviceAccountConfig encapsulates the data required to perform the operation.
type serviceAccountConfig struct {
PrintFlags *printers.PrintFlags
type SetServiceAccountOptions struct {
PrintFlags *printers.PrintFlags
RecordFlags *genericclioptions.RecordFlags
fileNameOptions resource.FilenameOptions
out io.Writer
@ -74,16 +77,24 @@ type serviceAccountConfig struct {
serviceAccountName string
PrintObj printers.ResourcePrinterFunc
Recorder genericclioptions.Recorder
}
func NewSetServiceAccountOptions(out, errOut io.Writer) *SetServiceAccountOptions {
return &SetServiceAccountOptions{
PrintFlags: printers.NewPrintFlags("serviceaccount updated"),
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
out: out,
err: errOut,
}
}
// NewCmdServiceAccount returns the "set serviceaccount" command.
func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
saConfig := &serviceAccountConfig{
PrintFlags: printers.NewPrintFlags("serviceaccount updated"),
out: out,
err: err,
}
func NewCmdServiceAccount(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := NewSetServiceAccountOptions(out, errOut)
cmd := &cobra.Command{
Use: "serviceaccount (-f FILENAME | TYPE NAME) SERVICE_ACCOUNT",
@ -93,41 +104,49 @@ func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command
Long: serviceaccountLong,
Example: serviceaccountExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(saConfig.Complete(f, cmd, args))
cmdutil.CheckErr(saConfig.Run())
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Run())
},
}
saConfig.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
o.RecordFlags.AddFlags(cmd)
usage := "identifying the resource to get from a server."
cmdutil.AddFilenameOptionFlags(cmd, &saConfig.fileNameOptions, usage)
cmd.Flags().BoolVar(&saConfig.all, "all", saConfig.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().BoolVar(&saConfig.local, "local", saConfig.local, "If true, set serviceaccount will NOT contact api-server but run locally.")
cmdutil.AddRecordFlag(cmd)
cmdutil.AddFilenameOptionFlags(cmd, &o.fileNameOptions, usage)
cmd.Flags().BoolVar(&o.all, "all", o.all, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, set serviceaccount will NOT contact api-server but run locally.")
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}
// Complete configures serviceAccountConfig from command line args.
func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
saConfig.shortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
saConfig.record = cmdutil.GetRecordFlag(cmd)
saConfig.changeCause = f.Command(cmd, false)
saConfig.dryRun = cmdutil.GetDryRunFlag(cmd)
saConfig.output = cmdutil.GetFlagString(cmd, "output")
saConfig.updatePodSpecForObject = f.UpdatePodSpecForObject
saConfig.cmd = cmd
func (o *SetServiceAccountOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if saConfig.dryRun {
saConfig.PrintFlags.Complete("%s (dry run)")
}
printer, err := saConfig.PrintFlags.ToPrinter()
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
saConfig.PrintObj = printer.PrintObj
o.shortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.record = cmdutil.GetRecordFlag(cmd)
o.changeCause = f.Command(cmd, false)
o.dryRun = cmdutil.GetDryRunFlag(cmd)
o.output = cmdutil.GetFlagString(cmd, "output")
o.updatePodSpecForObject = f.UpdatePodSpecForObject
o.cmd = cmd
if o.dryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = printer.PrintObj
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
@ -136,22 +155,22 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
if len(args) == 0 {
return errors.New("serviceaccount is required")
}
saConfig.serviceAccountName = args[len(args)-1]
o.serviceAccountName = args[len(args)-1]
resources := args[:len(args)-1]
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().
Internal().
LocalParam(saConfig.local).
LocalParam(o.local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &saConfig.fileNameOptions).
FilenameParam(enforceNamespace, &o.fileNameOptions).
IncludeUninitialized(includeUninitialized).
Flatten()
if !saConfig.local {
builder.ResourceTypeOrNameArgs(saConfig.all, resources...).
if !o.local {
builder.ResourceTypeOrNameArgs(o.all, resources...).
Latest()
}
saConfig.infos, err = builder.Do().Infos()
o.infos, err = builder.Do().Infos()
if err != nil {
return err
}
@ -159,26 +178,34 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
}
// Run creates and applies the patch either locally or calling apiserver.
func (saConfig *serviceAccountConfig) Run() error {
func (o *SetServiceAccountOptions) Run() error {
patchErrs := []error{}
patchFn := func(info *resource.Info) ([]byte, error) {
info.Object = info.AsVersioned()
saConfig.updatePodSpecForObject(info.Object, func(podSpec *v1.PodSpec) error {
podSpec.ServiceAccountName = saConfig.serviceAccountName
_, err := o.updatePodSpecForObject(info.Object, func(podSpec *v1.PodSpec) error {
podSpec.ServiceAccountName = o.serviceAccountName
return nil
})
if err != nil {
return nil, err
}
// record this change (for rollout history)
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
return runtime.Encode(cmdutil.InternalVersionJSONEncoder(), info.Object)
}
patches := CalculatePatches(saConfig.infos, cmdutil.InternalVersionJSONEncoder(), patchFn)
patches := CalculatePatches(o.infos, cmdutil.InternalVersionJSONEncoder(), patchFn)
for _, patch := range patches {
info := patch.Info
if patch.Err != nil {
patchErrs = append(patchErrs, fmt.Errorf("error: %s/%s %v\n", info.Mapping.Resource, info.Name, patch.Err))
continue
}
if saConfig.local || saConfig.dryRun {
if err := saConfig.PrintObj(patch.Info.AsVersioned(), saConfig.out); err != nil {
if o.local || o.dryRun {
if err := o.PrintObj(patch.Info.AsVersioned(), o.out); err != nil {
return err
}
continue
@ -189,15 +216,8 @@ func (saConfig *serviceAccountConfig) Run() error {
continue
}
info.Refresh(patched, true)
if saConfig.record || cmdutil.ContainsChangeCause(info) {
if patch, patchType, err := cmdutil.ChangeResourcePatch(info, saConfig.changeCause); err == nil {
if patched, err = resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, patchType, patch); err != nil {
fmt.Fprintf(saConfig.err, "WARNING: changes to %s/%s can't be recorded: %v\n", info.Mapping.Resource, info.Name, err)
}
}
}
if err := saConfig.PrintObj(info.AsVersioned(), saConfig.out); err != nil {
if err := o.PrintObj(info.AsVersioned(), o.out); err != nil {
return err
}
}

View File

@ -89,7 +89,7 @@ func TestSetServiceAccountLocal(t *testing.T) {
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
testapi.Default = testapi.Groups[input.apiGroup]
saConfig := serviceAccountConfig{
saConfig := SetServiceAccountOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -133,7 +133,7 @@ func TestSetServiceAccountMultiLocal(t *testing.T) {
cmd.SetOutput(buf)
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := serviceAccountConfig{
opts := SetServiceAccountOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -377,7 +377,7 @@ func TestSetServiceAccountRemote(t *testing.T) {
cmd := NewCmdServiceAccount(tf, out, out)
cmd.SetOutput(out)
cmd.Flags().Set("output", outputFormat)
saConfig := serviceAccountConfig{
saConfig := SetServiceAccountOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
@ -424,7 +424,7 @@ func TestServiceAccountValidation(t *testing.T) {
cmd := NewCmdServiceAccount(tf, out, out)
cmd.SetOutput(out)
saConfig := &serviceAccountConfig{
saConfig := &SetServiceAccountOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),

View File

@ -91,6 +91,8 @@ func NewEditOptions(editMode EditMode, out, errOut io.Writer) *EditOptions {
Output: "yaml",
WindowsLineEndings: goruntime.GOOS == "windows",
Recorder: genericclioptions.NoopRecorder{},
Out: out,
ErrOut: errOut,
}
@ -104,9 +106,9 @@ type editPrinterOptions struct {
// Complete completes all the required options
func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err

View File

@ -521,10 +521,6 @@ func AddRecordFlag(cmd *cobra.Command) {
cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
}
func AddRecordVarFlag(cmd *cobra.Command, record *bool) {
cmd.Flags().BoolVar(record, "record", *record, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
}
func GetRecordFlag(cmd *cobra.Command) bool {
return GetFlagBool(cmd, "record")
}

View File

@ -38,7 +38,7 @@ type RecordFlags struct {
// explicitly given by the user
func (f *RecordFlags) ToRecorder() (Recorder, error) {
if f == nil {
return &NoopRecorder{}, nil
return NoopRecorder{}, nil
}
shouldRecord := false
@ -49,7 +49,7 @@ func (f *RecordFlags) ToRecorder() (Recorder, error) {
// if flag was explicitly set to false by the user,
// do not record
if !shouldRecord {
return &NoopRecorder{}, nil
return NoopRecorder{}, nil
}
return &ChangeCauseRecorder{
@ -98,7 +98,7 @@ type Recorder interface {
type NoopRecorder struct{}
// Record implements Recorder
func (r *NoopRecorder) Record(obj runtime.Object) error {
func (r NoopRecorder) Record(obj runtime.Object) error {
return nil
}