Merge pull request #62794 from deads2k/cli-27-recordflags

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

fix up and use record flags

This pull starts a genericclioptions package with a dependency enforcer to ensure that we have no kube/kube links.  It makes the recordflags nil-able and still behave as expected.  And it also updates several commands to use the record flags.

@kubernetes/sig-cli-maintainers @soltysh @juanvallejo 

```release-note
NONE
```
pull/8/head
Kubernetes Submit Queue 2018-04-18 16:35:19 -07:00 committed by GitHub
commit b9ed99604d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 293 additions and 142 deletions

View File

@ -17,6 +17,10 @@
ignoredSubTrees:
- "./pkg/apis/core/validation"
- baseImportPath: "./pkg/kubectl/genericclioptions/"
allowedImports:
- k8s.io/apimachinery
- baseImportPath: "./vendor/k8s.io/apimachinery/"
allowedImports:
- k8s.io/apimachinery

View File

@ -206,6 +206,7 @@ filegroup(
"//pkg/kubectl/categories:all-srcs",
"//pkg/kubectl/cmd:all-srcs",
"//pkg/kubectl/explain:all-srcs",
"//pkg/kubectl/genericclioptions:all-srcs",
"//pkg/kubectl/metricsutil:all-srcs",
"//pkg/kubectl/plugins:all-srcs",
"//pkg/kubectl/proxy:all-srcs",

View File

@ -41,7 +41,6 @@ go_library(
"plugin.go",
"portforward.go",
"proxy.go",
"record_flags.go",
"replace.go",
"rollingupdate.go",
"run.go",
@ -79,6 +78,7 @@ go_library(
"//pkg/kubectl/cmd/util/editor:go_default_library",
"//pkg/kubectl/cmd/util/openapi:go_default_library",
"//pkg/kubectl/explain:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/metricsutil:go_default_library",
"//pkg/kubectl/plugins:go_default_library",
"//pkg/kubectl/proxy:go_default_library",

View File

@ -34,6 +34,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"
)
@ -42,21 +43,22 @@ import (
type AnnotateOptions struct {
// Filename options
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
// Common user flags
overwrite bool
local bool
dryrun bool
all bool
resourceVersion string
selector string
outputFormat string
recordChangeCause bool
overwrite bool
local bool
dryrun bool
all bool
resourceVersion string
selector string
outputFormat string
// results of arg parsing
resources []string
newAnnotations map[string]string
removeAnnotations []string
Recorder genericclioptions.Recorder
// Common share fields
out io.Writer
@ -97,8 +99,15 @@ var (
kubectl annotate pods foo description-`))
)
func NewAnnotateOptions(out io.Writer) *AnnotateOptions {
return &AnnotateOptions{
out: out,
RecordFlags: genericclioptions.NewRecordFlags(),
}
}
func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &AnnotateOptions{}
options := NewAnnotateOptions(out)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -108,7 +117,7 @@ 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(out, cmd, args); err != nil {
if err := options.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
if err := options.Validate(); err != nil {
@ -119,6 +128,10 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs
options.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.")
@ -129,18 +142,23 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
usage := "identifying the resource to update the annotation"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
return cmd
}
// Complete adapts from the command line args and factory to the data required.
func (o *AnnotateOptions) Complete(out io.Writer, cmd *cobra.Command, args []string) (err error) {
o.out = out
func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
o.dryrun = cmdutil.GetDryRunFlag(cmd)
o.recordChangeCause = cmdutil.GetRecordFlag(cmd)
// retrieves resource and annotation args from args
// also checks args to verify that all resources are specified before annotations
@ -174,8 +192,6 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
return err
}
changeCause := f.Command(cmd, false)
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
Unstructured().
@ -232,9 +248,8 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
if err != nil {
return err
}
// If we should record change-cause, add it to new annotations
if cmdutil.ContainsChangeCause(info) || o.recordChangeCause {
o.newAnnotations[kubectl.ChangeCauseAnnotation] = changeCause
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
if err := o.updateAnnotations(obj); err != nil {
return err

View File

@ -431,8 +431,8 @@ func TestAnnotateErrors(t *testing.T) {
for k, v := range testCase.flags {
cmd.Flags().Set(k, v)
}
options := &AnnotateOptions{}
err := options.Complete(buf, cmd, testCase.args)
options := NewAnnotateOptions(buf)
err := options.Complete(tf, cmd, testCase.args)
if err == nil {
err = options.Validate()
}
@ -488,9 +488,9 @@ func TestAnnotateObject(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
options := &AnnotateOptions{}
options := NewAnnotateOptions(buf)
args := []string{"pods/foo", "a=b", "c-"}
if err := options.Complete(buf, cmd, args); err != nil {
if err := options.Complete(tf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.Validate(); err != nil {
@ -542,10 +542,10 @@ func TestAnnotateObjectFromFile(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
options := &AnnotateOptions{}
options := NewAnnotateOptions(buf)
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
args := []string{"a=b", "c-"}
if err := options.Complete(buf, cmd, args); err != nil {
if err := options.Complete(tf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.Validate(); err != nil {
@ -573,10 +573,11 @@ func TestAnnotateLocal(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(tf, buf)
options := &AnnotateOptions{local: true}
options := NewAnnotateOptions(buf)
options.local = true
options.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
args := []string{"a=b"}
if err := options.Complete(buf, cmd, args); err != nil {
if err := options.Complete(tf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.Validate(); err != nil {
@ -629,9 +630,10 @@ func TestAnnotateMultipleObjects(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdAnnotate(tf, buf)
cmd.SetOutput(buf)
options := &AnnotateOptions{all: true}
options := NewAnnotateOptions(buf)
options.all = true
args := []string{"pods", "a=b", "c-"}
if err := options.Complete(buf, cmd, args); err != nil {
if err := options.Complete(tf, cmd, args); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.Validate(); err != nil {

View File

@ -45,16 +45,17 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
type ApplyOptions struct {
RecordFlags *RecordFlags
RecordFlags *genericclioptions.RecordFlags
FilenameOptions resource.FilenameOptions
Recorder Recorder
Recorder genericclioptions.Recorder
Selector string
Force bool
@ -111,7 +112,7 @@ var (
func NewApplyOptions(out, errout io.Writer) *ApplyOptions {
return &ApplyOptions{
RecordFlags: NewRecordFlags(),
RecordFlags: genericclioptions.NewRecordFlags(),
Overwrite: true,
Cascade: true,

View File

@ -18,7 +18,6 @@ package cmd
import (
"io"
"runtime"
"github.com/spf13/cobra"
@ -59,11 +58,8 @@ var (
)
func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &editor.EditOptions{
EditMode: editor.ApplyEditMode,
Output: "yaml",
WindowsLineEndings: runtime.GOOS == "windows",
}
o := editor.NewEditOptions(editor.ApplyEditMode, out, errOut)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -73,11 +69,10 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra
Long: applyEditLastAppliedLong,
Example: applyEditLastAppliedExample,
Run: func(cmd *cobra.Command, args []string) {
options.ChangeCause = f.Command(cmd, false)
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
if err := o.Complete(f, args, cmd); err != nil {
cmdutil.CheckErr(err)
}
if err := options.Run(); err != nil {
if err := o.Run(); err != nil {
cmdutil.CheckErr(err)
}
},
@ -85,12 +80,14 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)
usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of: yaml|json.")
cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", options.WindowsLineEndings,
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: yaml|json.")
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.")
cmdutil.AddRecordVarFlag(cmd, &options.Record)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd

View File

@ -54,8 +54,9 @@ type SetLastAppliedOptions struct {
Output string
PatchBufferList []PatchBuffer
Factory cmdutil.Factory
Out io.Writer
ErrOut io.Writer
Out io.Writer
ErrOut io.Writer
}
type PatchBuffer struct {
@ -81,8 +82,15 @@ var (
`))
)
func NewCmdApplySetLastApplied(f cmdutil.Factory, out, err io.Writer) *cobra.Command {
options := &SetLastAppliedOptions{Out: out, ErrOut: err}
func NewSetLastAppliedOptions(out, errout io.Writer) *SetLastAppliedOptions {
return &SetLastAppliedOptions{
Out: out,
ErrOut: errout,
}
}
func NewCmdApplySetLastApplied(f cmdutil.Factory, out, errout io.Writer) *cobra.Command {
options := NewSetLastAppliedOptions(out, errout)
cmd := &cobra.Command{
Use: "set-last-applied -f FILENAME",
DisableFlagsInUseLine: true,
@ -97,7 +105,6 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, out, err io.Writer) *cobra.Com
}
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().BoolVar(&options.CreateAnnotation, "create-annotation", options.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
usage := "that contains the last-applied-configuration annotations"
@ -111,12 +118,9 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command)
o.Output = cmdutil.GetFlagString(cmd, "output")
o.ShortOutput = o.Output == "name"
var err error
o.Mapper, o.Typer = f.Object()
if err != nil {
return err
}
var err error
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
return err
}

View File

@ -24,9 +24,11 @@ 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"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
@ -45,8 +47,22 @@ var (
kubectl autoscale rc foo --max=5 --cpu-percent=80`))
)
type AutoscaleOptions struct {
FilenameOptions resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
Recorder genericclioptions.Recorder
}
func NewAutoscaleOptions() *AutoscaleOptions {
return &AutoscaleOptions{
FilenameOptions: resource.FilenameOptions{},
RecordFlags: genericclioptions.NewRecordFlags(),
}
}
func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}
o := NewAutoscaleOptions()
validArgs := []string{"deployment", "replicaset", "replicationcontroller"}
argAliases := kubectl.ResourceAliases(validArgs)
@ -58,12 +74,16 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: autoscaleLong,
Example: autoscaleExample,
Run: func(cmd *cobra.Command, args []string) {
err := RunAutoscale(f, out, cmd, args, options)
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.RunAutoscale(f, out, cmd, args))
},
ValidArgs: validArgs,
ArgAliases: argAliases,
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", cmdutil.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
cmd.Flags().Int32("min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.")
@ -73,14 +93,25 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().String("name", "", i18n.T("The name for the newly created object. If not specified, the name of the input resource will be used."))
cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to autoscale."
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
return cmd
}
func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
return nil
}
func (o *AutoscaleOptions) RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
namespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
@ -95,7 +126,7 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
Internal().
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(false, args...).
Flatten().
Do()
@ -149,11 +180,8 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
if err != nil {
return err
}
if cmdutil.ShouldRecord(cmd, hpa) {
if err := cmdutil.RecordChangeCause(hpa.Object, f.Command(cmd, false)); err != nil {
return err
}
object = hpa.Object
if err := o.Recorder.Record(hpa.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
if cmdutil.GetDryRunFlag(cmd) {
return cmdutil.PrintObject(cmd, object, out)

View File

@ -27,9 +27,11 @@ go_library(
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/cmd/util/editor: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/batch/v1:go_default_library",
"//vendor/k8s.io/api/batch/v1beta1:go_default_library",

View File

@ -27,6 +27,7 @@ import (
"net/url"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
kruntime "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -34,13 +35,14 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
)
type CreateOptions struct {
PrintFlags *PrintFlags
PrintObj func(obj kruntime.Object) error
PrintFlags *PrintFlags
RecordFlags *genericclioptions.RecordFlags
DryRun bool
@ -50,6 +52,9 @@ type CreateOptions struct {
Raw string
Out io.Writer
ErrOut io.Writer
Recorder genericclioptions.Recorder
PrintObj func(obj kruntime.Object) error
}
var (
@ -69,13 +74,18 @@ var (
kubectl create -f docker-registry.yaml --edit -o json`))
)
func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &CreateOptions{
PrintFlags: NewPrintFlags("created"),
func NewCreateOptions(out, errOut io.Writer) *CreateOptions {
return &CreateOptions{
PrintFlags: NewPrintFlags("created"),
RecordFlags: genericclioptions.NewRecordFlags(),
Out: out,
ErrOut: errOut,
}
}
func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := NewCreateOptions(out, errOut)
cmd := &cobra.Command{
Use: "create -f FILENAME",
@ -84,32 +94,34 @@ func NewCmdCreate(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: createLong,
Example: createExample,
Run: func(cmd *cobra.Command, args []string) {
if cmdutil.IsFilenameSliceEmpty(options.FilenameOptions.Filenames) {
if cmdutil.IsFilenameSliceEmpty(o.FilenameOptions.Filenames) {
defaultRunFunc := cmdutil.DefaultSubCommandRun(errOut)
defaultRunFunc(cmd, args)
return
}
cmdutil.CheckErr(options.Complete(cmd))
cmdutil.CheckErr(options.ValidateArgs(cmd, args))
cmdutil.CheckErr(options.RunCreate(f, cmd))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.ValidateArgs(cmd, args))
cmdutil.CheckErr(o.RunCreate(f, cmd))
},
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)
usage := "to use to create the resource"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.MarkFlagRequired("filename")
cmdutil.AddValidateFlags(cmd)
cmd.Flags().BoolVar(&options.EditBeforeCreate, "edit", options.EditBeforeCreate, "Edit the API resource before creating")
cmd.Flags().BoolVar(&o.EditBeforeCreate, "edit", o.EditBeforeCreate, "Edit the API resource before creating")
cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows",
"Only relevant if --edit=true. Defaults to the line ending native to your platform.")
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(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().StringVar(&options.Raw, "raw", options.Raw, "Raw URI to POST to the server. Uses the transport specified by the kubeconfig file.")
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().StringVar(&o.Raw, "raw", o.Raw, "Raw URI to POST to the server. Uses the transport specified by the kubeconfig file.")
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
// create subcommands
cmd.AddCommand(NewCmdCreateNamespace(f, out))
@ -160,7 +172,15 @@ func (o *CreateOptions) ValidateArgs(cmd *cobra.Command, args []string) error {
return nil
}
func (o *CreateOptions) Complete(cmd *cobra.Command) error {
func (o *CreateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.DryRun = cmdutil.GetDryRunFlag(cmd)
if o.DryRun {
@ -186,7 +206,7 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
}
if o.EditBeforeCreate {
return RunEditOnCreate(f, o.Out, o.ErrOut, cmd, &o.FilenameOptions)
return RunEditOnCreate(f, o.RecordFlags, o.Out, o.ErrOut, cmd, &o.FilenameOptions)
}
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
if err != nil {
@ -221,10 +241,8 @@ func (o *CreateOptions) RunCreate(f cmdutil.Factory, cmd *cobra.Command) error {
return cmdutil.AddSourceToErr("creating", info.Source, err)
}
if cmdutil.ShouldRecord(cmd, info) {
if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err)
}
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
if !o.DryRun {
@ -273,21 +291,18 @@ func (o *CreateOptions) raw(f cmdutil.Factory) error {
return nil
}
func RunEditOnCreate(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, options *resource.FilenameOptions) error {
editOptions := &editor.EditOptions{
EditMode: editor.EditBeforeCreateMode,
FilenameOptions: *options,
ValidateOptions: cmdutil.ValidateOptions{
EnableValidation: cmdutil.GetFlagBool(cmd, "validate"),
},
Output: cmdutil.GetFlagString(cmd, "output"),
WindowsLineEndings: cmdutil.GetFlagBool(cmd, "windows-line-endings"),
ApplyAnnotation: cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag),
Record: cmdutil.GetFlagBool(cmd, "record"),
ChangeCause: f.Command(cmd, false),
Include3rdParty: cmdutil.GetFlagBool(cmd, "include-extended-apis"),
func RunEditOnCreate(f cmdutil.Factory, recordFlags *genericclioptions.RecordFlags, out, errOut io.Writer, cmd *cobra.Command, options *resource.FilenameOptions) error {
editOptions := editor.NewEditOptions(editor.EditBeforeCreateMode, out, errOut)
editOptions.FilenameOptions = *options
editOptions.ValidateOptions = cmdutil.ValidateOptions{
EnableValidation: cmdutil.GetFlagBool(cmd, "validate"),
}
err := editOptions.Complete(f, out, errOut, []string{}, cmd)
editOptions.Output = cmdutil.GetFlagString(cmd, "output")
editOptions.ApplyAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
editOptions.RecordFlags = recordFlags
editOptions.Include3rdParty = cmdutil.GetFlagBool(cmd, "include-extended-apis")
err := editOptions.Complete(f, []string{}, cmd)
if err != nil {
return err
}

View File

@ -19,7 +19,6 @@ package cmd
import (
"fmt"
"io"
"runtime"
"github.com/spf13/cobra"
@ -70,13 +69,10 @@ var (
)
func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &editor.EditOptions{
EditMode: editor.NormalEditMode,
Output: "yaml",
WindowsLineEndings: runtime.GOOS == "windows",
ValidateOptions: cmdutil.ValidateOptions{EnableValidation: true},
Include3rdParty: true,
}
o := editor.NewEditOptions(editor.NormalEditMode, out, errOut)
o.ValidateOptions = cmdutil.ValidateOptions{EnableValidation: true}
o.Include3rdParty = true
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -86,28 +82,30 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: editLong,
Example: fmt.Sprintf(editExample),
Run: func(cmd *cobra.Command, args []string) {
options.ChangeCause = f.Command(cmd, false)
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
if err := o.Complete(f, args, cmd); err != nil {
cmdutil.CheckErr(err)
}
if err := options.Run(); err != nil {
if err := o.Run(); err != nil {
cmdutil.CheckErr(err)
}
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
// bind flag structs
o.RecordFlags.AddFlags(cmd)
usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmdutil.AddValidateOptionFlags(cmd, &options.ValidateOptions)
cmd.Flags().StringVarP(&options.Output, "output", "o", options.Output, "Output format. One of: yaml|json.")
cmd.Flags().BoolVarP(&options.OutputPatch, "output-patch", "", options.OutputPatch, "Output the patch if the resource is edited.")
cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", options.WindowsLineEndings,
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddValidateOptionFlags(cmd, &o.ValidateOptions)
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: yaml|json.")
cmd.Flags().BoolVarP(&o.OutputPatch, "output-patch", "", o.OutputPatch, "Output the patch if the resource is edited.")
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.")
cmdutil.AddApplyAnnotationVarFlags(cmd, &options.ApplyAnnotation)
cmdutil.AddRecordVarFlag(cmd, &options.Record)
cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty)
cmdutil.AddApplyAnnotationVarFlags(cmd, &o.ApplyAnnotation)
cmdutil.AddInclude3rdPartyVarFlags(cmd, &o.Include3rdParty)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}

View File

@ -1,10 +1,5 @@
{
"data": {
"new-data3": "newivalue"
},
"metadata": {
"annotations": {
"kubernetes.io/change-cause": "edit test cmd invocation"
}
}
}

View File

@ -1,8 +1,5 @@
{
"metadata": {
"annotations": {
"kubernetes.io/change-cause": "edit test cmd invocation"
},
"labels": {
"new-label2": "foo2"
}

View File

@ -18,6 +18,7 @@ go_library(
"//pkg/apis/core:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/genericclioptions:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubectl/scheme:go_default_library",
"//pkg/kubectl/util/crlf:go_default_library",

View File

@ -25,6 +25,7 @@ import (
"os"
"path/filepath"
"reflect"
goruntime "runtime"
"strings"
"github.com/evanphx/json-patch"
@ -44,6 +45,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl"
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/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/crlf"
@ -53,6 +55,7 @@ import (
// EditOptions contains all the options for running edit cli command.
type EditOptions struct {
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
Output string
OutputPatch bool
@ -67,18 +70,32 @@ type EditOptions struct {
CmdNamespace string
ApplyAnnotation bool
Record bool
ChangeCause string
Include3rdParty bool
Out io.Writer
ErrOut io.Writer
Recorder genericclioptions.Recorder
f cmdutil.Factory
editPrinterOptions *editPrinterOptions
updatedResultGetter func(data []byte) *resource.Result
}
func NewEditOptions(editMode EditMode, out, errOut io.Writer) *EditOptions {
return &EditOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
EditMode: editMode,
Output: "yaml",
WindowsLineEndings: goruntime.GOOS == "windows",
Out: out,
ErrOut: errOut,
}
}
type editPrinterOptions struct {
printer printers.ResourcePrinter
ext string
@ -86,7 +103,15 @@ type editPrinterOptions struct {
}
// Complete completes all the required options
func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string, cmd *cobra.Command) error {
func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Command) error {
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
if o.EditMode != NormalEditMode && o.EditMode != EditBeforeCreateMode && o.EditMode != ApplyEditMode {
return fmt.Errorf("unsupported edit mode %q", o.EditMode)
}
@ -138,10 +163,6 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
o.CmdNamespace = cmdNamespace
o.f = f
// Set up writer
o.Out = out
o.ErrOut = errOut
return nil
}
@ -609,10 +630,8 @@ func (o *EditOptions) visitAnnotation(annotationVisitor resource.Visitor) error
return err
}
}
if o.Record || cmdutil.ContainsChangeCause(info) {
if err := cmdutil.RecordChangeCause(info.Object, o.ChangeCause); err != nil {
return err
}
if err := o.Recorder.Record(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
return nil

View File

@ -0,0 +1,30 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"record_flags.go",
],
importpath = "k8s.io/kubernetes/pkg/kubectl/genericclioptions",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/spf13/cobra:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -0,0 +1,19 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
// useful helper functions. Nothing in this package can depend on kube/kube
package genericclioptions

View File

@ -14,18 +14,21 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package cmd
package genericclioptions
import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl"
)
// ChangeCauseAnnotation is the annotation indicating a guess at "why" something was changed
const ChangeCauseAnnotation = "kubernetes.io/change-cause"
// RecordFlags contains all flags associated with the "--record" operation
type RecordFlags struct {
// Record indicates the state of the recording flag. It is a pointer so a caller can opt out or rebind
Record *bool
changeCause string
@ -34,6 +37,10 @@ type RecordFlags struct {
// ToRecorder returns a ChangeCause recorder if --record=false was not
// explicitly given by the user
func (f *RecordFlags) ToRecorder() (Recorder, error) {
if f == nil {
return &NoopRecorder{}, nil
}
shouldRecord := false
if f.Record != nil {
shouldRecord = *f.Record
@ -50,17 +57,29 @@ func (f *RecordFlags) ToRecorder() (Recorder, error) {
}, nil
}
// Complete is called before the command is run, but after it is invoked to finish the state of the struct before use.
func (f *RecordFlags) Complete(changeCause string) error {
if f == nil {
return nil
}
f.changeCause = changeCause
return nil
}
// AddFlags binds the requested flags to the provided flagset
// TODO have this only take a flagset
func (f *RecordFlags) AddFlags(cmd *cobra.Command) {
if f == nil {
return
}
if f.Record != nil {
cmd.Flags().BoolVar(f.Record, "record", *f.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.")
}
}
// NewRecordFlags provides a RecordFlags with reasonable default values set for use
func NewRecordFlags() *RecordFlags {
record := false
@ -69,12 +88,16 @@ func NewRecordFlags() *RecordFlags {
}
}
// Recorder is used to record why a runtime.Object was changed in an annotation.
type Recorder interface {
// Record records why a runtime.Object was changed in an annotation.
Record(runtime.Object) error
}
// NoopRecorder does nothing. It is a "do nothing" that can be returned so code doesn't switch on it.
type NoopRecorder struct{}
// Record implements Recorder
func (r *NoopRecorder) Record(obj runtime.Object) error {
return nil
}
@ -95,7 +118,7 @@ func (r *ChangeCauseRecorder) Record(obj runtime.Object) error {
if annotations == nil {
annotations = make(map[string]string)
}
annotations[kubectl.ChangeCauseAnnotation] = r.changeCause
annotations[ChangeCauseAnnotation] = r.changeCause
accessor.SetAnnotations(annotations)
return nil
}