From 6b24fe2215e97ccccc5f7926ea79713d02ade2e8 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 10 Aug 2017 17:22:03 +0800 Subject: [PATCH 1/6] kubectl get show uninitialized resources --- pkg/kubectl/cmd/get.go | 4 ++++ pkg/kubectl/resource/builder.go | 13 ++++++++++--- pkg/kubectl/resource/helper.go | 5 ++++- pkg/kubectl/resource/helper_test.go | 2 +- pkg/kubectl/resource/selector.go | 26 ++++++++++++++------------ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 6daece57a6c..9f6c98a17a0 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -130,6 +130,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).") + cmd.Flags().Bool("include-uninitialized", false, "If present, list uninitialized resource(s).") cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful retrieval.") cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...") @@ -195,6 +196,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ } export := cmdutil.GetFlagBool(cmd, "export") + includeUninitialized := cmdutil.GetFlagBool(cmd, "include-uninitialized") filterFuncs := f.DefaultResourceFilterFunc() filterOpts := f.DefaultResourceFilterOptions(cmd, allNamespaces) @@ -212,6 +214,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ FilenameParam(enforceNamespace, &options.FilenameOptions). SelectorParam(selector). ExportParam(export). + IncludeUninitialized(includeUninitialized). ResourceTypeOrNameArgs(true, args...). SingleResourceType(). Latest(). @@ -301,6 +304,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ FilenameParam(enforceNamespace, &options.FilenameOptions). SelectorParam(selector). ExportParam(export). + IncludeUninitialized(includeUninitialized). ResourceTypeOrNameArgs(true, args...). ContinueOnError(). Latest(). diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index eb563fb51bf..8908bc2ef30 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -51,8 +51,9 @@ type Builder struct { stream bool dir bool - selector labels.Selector - selectAll bool + selector labels.Selector + selectAll bool + includeUninitialized bool resources []string @@ -279,6 +280,12 @@ func (b *Builder) ExportParam(export bool) *Builder { return b } +// IncludeUninitialized accepts the include-uninitialized boolean for these resources +func (b *Builder) IncludeUninitialized(includeUninitialized bool) *Builder { + b.includeUninitialized = includeUninitialized + return b +} + // NamespaceParam accepts the namespace that these resources should be // considered under from - used by DefaultNamespace() and RequireNamespace() func (b *Builder) NamespaceParam(namespace string) *Builder { @@ -607,7 +614,7 @@ func (b *Builder) visitBySelector() *Result { if mapping.Scope.Name() != meta.RESTScopeNameNamespace { selectorNamespace = "" } - visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export)) + visitors = append(visitors, NewSelector(client, mapping, selectorNamespace, b.selector, b.export, b.includeUninitialized)) } if b.continueOnError { result.visitor = EagerVisitorList(visitors) diff --git a/pkg/kubectl/resource/helper.go b/pkg/kubectl/resource/helper.go index e6452244b67..940387295ad 100644 --- a/pkg/kubectl/resource/helper.go +++ b/pkg/kubectl/resource/helper.go @@ -65,7 +65,7 @@ func (m *Helper) Get(namespace, name string, export bool) (runtime.Object, error } // TODO: add field selector -func (m *Helper) List(namespace, apiVersion string, selector labels.Selector, export bool) (runtime.Object, error) { +func (m *Helper) List(namespace, apiVersion string, selector labels.Selector, export, includeUninitialized bool) (runtime.Object, error) { req := m.RESTClient.Get(). NamespaceIfScoped(namespace, m.NamespaceScoped). Resource(m.Resource). @@ -76,6 +76,9 @@ func (m *Helper) List(namespace, apiVersion string, selector labels.Selector, ex // TODO: I should be part of ListOptions req.Param("export", strconv.FormatBool(export)) } + if includeUninitialized { + req.Param("includeUninitialized", strconv.FormatBool(includeUninitialized)) + } return req.Do().Get() } diff --git a/pkg/kubectl/resource/helper_test.go b/pkg/kubectl/resource/helper_test.go index 2829ffaeaf9..00208bdf389 100644 --- a/pkg/kubectl/resource/helper_test.go +++ b/pkg/kubectl/resource/helper_test.go @@ -358,7 +358,7 @@ func TestHelperList(t *testing.T) { RESTClient: client, NamespaceScoped: true, } - obj, err := modifier.List("bar", api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), labels.SelectorFromSet(labels.Set{"foo": "baz"}), false) + obj, err := modifier.List("bar", api.Registry.GroupOrDie(api.GroupName).GroupVersion.String(), labels.SelectorFromSet(labels.Set{"foo": "baz"}), false, false) if (err != nil) != test.Err { t.Errorf("unexpected error: %t %v", test.Err, err) } diff --git a/pkg/kubectl/resource/selector.go b/pkg/kubectl/resource/selector.go index 49431fb79fa..6faed0f4784 100644 --- a/pkg/kubectl/resource/selector.go +++ b/pkg/kubectl/resource/selector.go @@ -27,27 +27,29 @@ import ( // Selector is a Visitor for resources that match a label selector. type Selector struct { - Client RESTClient - Mapping *meta.RESTMapping - Namespace string - Selector labels.Selector - Export bool + Client RESTClient + Mapping *meta.RESTMapping + Namespace string + Selector labels.Selector + Export bool + IncludeUninitialized bool } // NewSelector creates a resource selector which hides details of getting items by their label selector. -func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector, export bool) *Selector { +func NewSelector(client RESTClient, mapping *meta.RESTMapping, namespace string, selector labels.Selector, export, includeUninitialized bool) *Selector { return &Selector{ - Client: client, - Mapping: mapping, - Namespace: namespace, - Selector: selector, - Export: export, + Client: client, + Mapping: mapping, + Namespace: namespace, + Selector: selector, + Export: export, + IncludeUninitialized: includeUninitialized, } } // Visit implements Visitor func (r *Selector) Visit(fn VisitorFunc) error { - list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector, r.Export) + list, err := NewHelper(r.Client, r.Mapping).List(r.Namespace, r.ResourceMapping().GroupVersionKind.GroupVersion().String(), r.Selector, r.Export, r.IncludeUninitialized) if err != nil { if errors.IsBadRequest(err) || errors.IsNotFound(err) { if se, ok := err.(*errors.StatusError); ok { From 884a4b1fa182a95983a82731e1e616debeab654b Mon Sep 17 00:00:00 2001 From: Di Xu Date: Thu, 10 Aug 2017 17:22:33 +0800 Subject: [PATCH 2/6] update related files due to api change --- pkg/kubectl/cmd/apply.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index 6b6172ccd72..b4ace54fa87 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -466,7 +466,7 @@ func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput return err } - objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false) + objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false, false) if err != nil { return err } From 789a95971d46589fd8dd7cd4fa8ac8903189b961 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 11 Aug 2017 13:32:23 +0800 Subject: [PATCH 3/6] kubectl add global flag --include-uninitialized --- pkg/kubectl/cmd/get.go | 1 - pkg/kubectl/cmd/util/helpers.go | 4 ++++ staging/src/k8s.io/client-go/tools/clientcmd/overrides.go | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 9f6c98a17a0..97fa85d4bfc 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -130,7 +130,6 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).") - cmd.Flags().Bool("include-uninitialized", false, "If present, list uninitialized resource(s).") cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful retrieval.") cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...") diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index 78590ab25c1..ca88b0daad8 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -430,6 +430,10 @@ func AddDryRunFlag(cmd *cobra.Command) { cmd.Flags().Bool("dry-run", false, "If true, only print the object that would be sent, without sending it.") } +func AddIncludeUninitializedFlag(cmd *cobra.Command) { + cmd.Flags().Bool("include-uninitialized", false, "If true, include the object(s) that have not been realized yet. Such object(s) have a non-empty initializer list.") +} + func AddPodRunningTimeoutFlag(cmd *cobra.Command, defaultTimeout time.Duration) { cmd.Flags().Duration("pod-running-timeout", defaultTimeout, "The length of time (like 5s, 2m, or 3h, higher than zero) to wait until at least one pod is running") } diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go index 963ac8fae9b..db831440e8f 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go @@ -36,7 +36,7 @@ type ConfigOverrides struct { Timeout string } -// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly +// ConfigOverrideFlags holds the flag names to be used for binding command line flags. Notice that this structure tightly // corresponds to ConfigOverrides type ConfigOverrideFlags struct { AuthOverrideFlags AuthOverrideFlags From be0cadde2eae139b3959a2fcae81d5b63f9d7041 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 11 Aug 2017 14:21:44 +0800 Subject: [PATCH 4/6] enforce include-uninitialized in several kubectl commands --- pkg/kubectl/cmd/annotate.go | 7 ++++-- pkg/kubectl/cmd/apply.go | 16 ++++++++---- pkg/kubectl/cmd/apply_edit_last_applied.go | 3 ++- pkg/kubectl/cmd/create.go | 2 +- pkg/kubectl/cmd/delete.go | 11 +++++--- pkg/kubectl/cmd/delete_test.go | 17 ++++++++++--- pkg/kubectl/cmd/describe.go | 7 ++++++ pkg/kubectl/cmd/edit.go | 3 ++- pkg/kubectl/cmd/get.go | 16 +++++++++--- pkg/kubectl/cmd/label.go | 7 ++++-- pkg/kubectl/cmd/set/helper.go | 4 +-- pkg/kubectl/cmd/set/set_image.go | 7 ++++-- pkg/kubectl/cmd/set/set_resources.go | 8 +++--- pkg/kubectl/cmd/set/set_selector.go | 5 +++- pkg/kubectl/cmd/set/set_serviceaccount.go | 5 +++- pkg/kubectl/cmd/set/set_subject.go | 7 ++++-- pkg/kubectl/cmd/util/editor/BUILD | 1 + pkg/kubectl/cmd/util/editor/editoptions.go | 6 ++++- pkg/kubectl/cmd/util/helpers.go | 29 +++++++++++++++++++--- 19 files changed, 123 insertions(+), 38 deletions(-) diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 5d8eeebe124..318aeb75941 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -128,10 +128,11 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command { ArgAliases: argAliases, } cmdutil.AddPrinterFlags(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) cmd.Flags().Bool("overwrite", false, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.") cmd.Flags().Bool("local", false, "If true, annotation will NOT contact api-server but run locally.") - 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().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).") + cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types.") cmd.Flags().String("resource-version", "", 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) @@ -190,10 +191,12 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) b := builder. ContinueOnError(). NamespaceParam(namespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.local { diff --git a/pkg/kubectl/cmd/apply.go b/pkg/kubectl/cmd/apply.go index b4ace54fa87..5b756c81e7b 100644 --- a/pkg/kubectl/cmd/apply.go +++ b/pkg/kubectl/cmd/apply.go @@ -118,7 +118,7 @@ func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cob cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmd.MarkFlagRequired("filename") cmd.Flags().Bool("overwrite", true, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration") - cmd.Flags().BoolVar(&options.Prune, "prune", false, "Automatically delete resource objects 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.Prune, "prune", false, "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", true, "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", -1, "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", false, fmt.Sprintf("Delete and re-create the specified resource, when PATCH encounters conflict and has retried for %d times.", maxPatchRetry)) @@ -131,6 +131,7 @@ func NewCmdApply(baseName string, f cmdutil.Factory, out, errOut io.Writer) *cob cmdutil.AddPrinterFlags(cmd) cmdutil.AddRecordFlag(cmd) cmdutil.AddInclude3rdPartyFlags(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) // apply subcommands cmd.AddCommand(NewCmdApplyViewLastApplied(f, out, errOut)) @@ -214,12 +215,17 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti return err } + // include the uninitialized objects by default if --prune is true + // unless explicitly set --include-uninitialized=false + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, options.Prune) + r := builder. Schema(schema). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &options.FilenameOptions). SelectorParam(options.Selector). + IncludeUninitialized(includeUninitialized). Flatten(). Do() err = r.Err() @@ -381,13 +387,13 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti for n := range visitedNamespaces { for _, m := range namespacedRESTMappings { - if err := p.prune(n, m, shortOutput); err != nil { + if err := p.prune(n, m, shortOutput, includeUninitialized); err != nil { return fmt.Errorf("error pruning namespaced object %v: %v", m.GroupVersionKind, err) } } } for _, m := range nonNamespacedRESTMappings { - if err := p.prune(metav1.NamespaceNone, m, shortOutput); err != nil { + if err := p.prune(metav1.NamespaceNone, m, shortOutput, includeUninitialized); err != nil { return fmt.Errorf("error pruning nonNamespaced object %v: %v", m.GroupVersionKind, err) } } @@ -460,13 +466,13 @@ type pruner struct { out io.Writer } -func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput bool) error { +func (p *pruner) prune(namespace string, mapping *meta.RESTMapping, shortOutput, includeUninitialized bool) error { c, err := p.clientFunc(mapping) if err != nil { return err } - objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false, false) + objList, err := resource.NewHelper(c, mapping).List(namespace, mapping.GroupVersionKind.Version, p.selector, false, includeUninitialized) if err != nil { return err } diff --git a/pkg/kubectl/cmd/apply_edit_last_applied.go b/pkg/kubectl/cmd/apply_edit_last_applied.go index 519737e0d2e..ca4868fa5b8 100644 --- a/pkg/kubectl/cmd/apply_edit_last_applied.go +++ b/pkg/kubectl/cmd/apply_edit_last_applied.go @@ -82,7 +82,7 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra Example: applyEditLastAppliedExample, Run: func(cmd *cobra.Command, args []string) { options.ChangeCause = f.Command(cmd, false) - if err := options.Complete(f, out, errOut, args); err != nil { + if err := options.Complete(f, out, errOut, args, cmd); err != nil { cmdutil.CheckErr(err) } if err := options.Run(); err != nil { @@ -99,6 +99,7 @@ func NewCmdApplyEditLastApplied(f cmdutil.Factory, out, errOut io.Writer) *cobra cmd.Flags().BoolVar(&options.WindowsLineEndings, "windows-line-endings", runtime.GOOS == "windows", "Defaults to the line ending native to your platform.") cmdutil.AddRecordVarFlag(cmd, &options.Record) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 7447d50c3a9..77405db897e 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -209,7 +209,7 @@ func RunEditOnCreate(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Comman ChangeCause: f.Command(cmd, false), Include3rdParty: cmdutil.GetFlagBool(cmd, "include-extended-apis"), } - err := editOptions.Complete(f, out, errOut, []string{}) + err := editOptions.Complete(f, out, errOut, []string{}, cmd) if err != nil { return err } diff --git a/pkg/kubectl/cmd/delete.go b/pkg/kubectl/cmd/delete.go index 7e815411298..814b6aa4f17 100644 --- a/pkg/kubectl/cmd/delete.go +++ b/pkg/kubectl/cmd/delete.go @@ -133,7 +133,7 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { Example: delete_example, Run: func(cmd *cobra.Command, args []string) { cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) - if err := options.Complete(f, out, errOut, args); err != nil { + if err := options.Complete(f, out, errOut, args, cmd); err != nil { cmdutil.CheckErr(err) } if err := options.Validate(cmd); err != nil { @@ -149,8 +149,8 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { } usage := "containing the resource to delete." cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) - cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on.") - cmd.Flags().BoolVar(&options.DeleteAll, "all", false, "Select all resources in the namespace of the specified resource types.") + cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.") + cmd.Flags().BoolVar(&options.DeleteAll, "all", false, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.") cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.") cmd.Flags().BoolVar(&options.Cascade, "cascade", true, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.") cmd.Flags().IntVar(&options.GracePeriod, "grace-period", -1, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative.") @@ -159,10 +159,11 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { cmd.Flags().DurationVar(&options.Timeout, "timeout", 0, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object") cmdutil.AddOutputVarFlagsForMutation(cmd, &options.Output) cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } -func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string) error { +func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string, cmd *cobra.Command) error { cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { return err @@ -180,11 +181,13 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args } o.Mapper = mapper + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) r := builder. ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). SelectorParam(o.Selector). + IncludeUninitialized(includeUninitialized). SelectAllParam(o.DeleteAll). ResourceTypeOrNameArgs(false, args...).RequireObject(false). Flatten(). diff --git a/pkg/kubectl/cmd/delete_test.go b/pkg/kubectl/cmd/delete_test.go index 677e29660ff..5cd244d391e 100644 --- a/pkg/kubectl/cmd/delete_test.go +++ b/pkg/kubectl/cmd/delete_test.go @@ -26,6 +26,8 @@ import ( "testing" "time" + "github.com/spf13/cobra" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -41,6 +43,13 @@ import ( var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer +var fakecmd = &cobra.Command{ + Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])", + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) + }, +} + func TestDeleteObjectByTuple(t *testing.T) { initTestErrorHandler(t) _, _, rc := testData() @@ -345,7 +354,7 @@ func TestDeleteObjectNotFound(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(f, buf, errBuf, []string{}) + err := options.Complete(f, buf, errBuf, []string{}, fakecmd) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -426,7 +435,7 @@ func TestDeleteAllNotFound(t *testing.T) { IgnoreNotFound: false, Output: "name", } - err := options.Complete(f, buf, errBuf, []string{"services"}) + err := options.Complete(f, buf, errBuf, []string{"services"}, fakecmd) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -546,7 +555,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(f, buf, errBuf, []string{}) + err := options.Complete(f, buf, errBuf, []string{}, fakecmd) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -714,7 +723,7 @@ func TestResourceErrors(t *testing.T) { Cascade: false, Output: "name", } - err := options.Complete(f, buf, errBuf, testCase.args) + err := options.Complete(f, buf, errBuf, testCase.args, fakecmd) if !testCase.errFn(err) { t.Errorf("%s: unexpected error: %v", k, err) continue diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 06f465834a5..993c74ceab0 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -39,6 +39,7 @@ import ( var ( describe_long = templates.LongDesc(` Show details of a specific resource or group of resources. + It includes the uninitialized objects, unless --include-uninitialized=false is explicitly set. This command joins many API calls together to form a detailed description of a given resource or group of resources. @@ -97,6 +98,7 @@ func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") cmd.Flags().BoolVar(&describerSettings.ShowEvents, "show-events", true, "If true, display events related to the described object.") cmdutil.AddInclude3rdPartyFlags(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -120,11 +122,16 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a return err } + // include the uninitialized objects by default + // unless user explicitly set --include-uninitialized=false + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, true) + r := builder. ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). FilenameParam(enforceNamespace, options). SelectorParam(selector). + IncludeUninitialized(includeUninitialized). ResourceTypeOrNameArgs(true, args...). Flatten(). Do() diff --git a/pkg/kubectl/cmd/edit.go b/pkg/kubectl/cmd/edit.go index 4aadfff3598..11ef23c6bd5 100644 --- a/pkg/kubectl/cmd/edit.go +++ b/pkg/kubectl/cmd/edit.go @@ -93,7 +93,7 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { 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); err != nil { + if err := options.Complete(f, out, errOut, args, cmd); err != nil { cmdutil.CheckErr(err) } if err := options.Run(); err != nil { @@ -115,5 +115,6 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command { cmdutil.AddApplyAnnotationVarFlags(cmd, &options.ApplyAnnotation) cmdutil.AddRecordVarFlag(cmd, &options.Record) cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/get.go index 97fa85d4bfc..3c1873d76b6 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/get.go @@ -58,7 +58,8 @@ var ( This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases. You can see the full results for any - resource by providing the '--show-all' flag. + resource by providing the '--show-all' flag, but this flag does not include + the uninitialized objects by default, unless '--include-uninitialized' is explicitly set. By specifying the output as 'template' and providing a Go template as the value of the --template flag, you can filter the attributes of the fetched resources.`) @@ -127,7 +128,8 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman } cmdutil.AddPrinterFlags(cmd) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") - cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") + cmdutil.AddIncludeUninitializedFlag(cmd) + cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes. Uninitialized objects are excluded if no object name is provided.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") cmd.Flags().Bool("show-kind", false, "If present, list the resource type for the requested object(s).") cmd.Flags().Bool("all-namespaces", false, "If present, list the requested object(s) across all namespaces. Namespace in current context is ignored even if specified with --namespace.") @@ -195,13 +197,21 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ } export := cmdutil.GetFlagBool(cmd, "export") - includeUninitialized := cmdutil.GetFlagBool(cmd, "include-uninitialized") filterFuncs := f.DefaultResourceFilterFunc() filterOpts := f.DefaultResourceFilterOptions(cmd, allNamespaces) // handle watch separately since we cannot watch multiple resource types isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") + + var includeUninitialized bool + if isWatch && len(args) == 2 { + // include the uninitialized one for watching on a single object + // unless explicitly set --include-uninitialized=false + includeUninitialized = true + } + includeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, includeUninitialized) + if isWatch || isWatchOnly { builder, err := f.NewUnstructuredBuilder(true) if err != nil { diff --git a/pkg/kubectl/cmd/label.go b/pkg/kubectl/cmd/label.go index 9b9d81e7605..0819a5e6332 100644 --- a/pkg/kubectl/cmd/label.go +++ b/pkg/kubectl/cmd/label.go @@ -128,14 +128,15 @@ func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command { cmdutil.AddPrinterFlags(cmd) cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.") cmd.Flags().Bool("local", false, "If true, label will NOT contact api-server but run locally.") - 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().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2).") + cmd.Flags().Bool("all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") cmd.Flags().String("resource-version", "", i18n.T("If non-empty, the labels 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 labels" cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddDryRunFlag(cmd) cmdutil.AddRecordFlag(cmd) cmdutil.AddInclude3rdPartyFlags(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -185,10 +186,12 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error { return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) b := builder. ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.local { diff --git a/pkg/kubectl/cmd/set/helper.go b/pkg/kubectl/cmd/set/helper.go index 35d49dc46e2..5fd7f2b94e2 100644 --- a/pkg/kubectl/cmd/set/helper.go +++ b/pkg/kubectl/cmd/set/helper.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/api" - kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" ) @@ -61,7 +61,7 @@ func handlePodUpdateError(out io.Writer, err error, resource string) { return } } else { - if ok := kcmdutil.PrintErrorWithCauses(err, out); ok { + if ok := cmdutil.PrintErrorWithCauses(err, out); ok { return } } diff --git a/pkg/kubectl/cmd/set/set_image.go b/pkg/kubectl/cmd/set/set_image.go index a865bd80f1d..83fa12171b0 100644 --- a/pkg/kubectl/cmd/set/set_image.go +++ b/pkg/kubectl/cmd/set/set_image.go @@ -105,11 +105,12 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command { cmdutil.AddPrinterFlags(cmd) usage := "identifying the resource to get from a server." cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) - cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources in the namespace of the specified resource types") - cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") + cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "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", false, "If true, set image will NOT contact api-server but run locally.") cmdutil.AddRecordFlag(cmd) cmdutil.AddDryRunFlag(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -137,10 +138,12 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) builder := f.NewBuilder(!o.Local). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.Local { builder = builder. diff --git a/pkg/kubectl/cmd/set/set_resources.go b/pkg/kubectl/cmd/set/set_resources.go index fa2aee7f54e..e3cff84942c 100644 --- a/pkg/kubectl/cmd/set/set_resources.go +++ b/pkg/kubectl/cmd/set/set_resources.go @@ -114,12 +114,13 @@ func NewCmdResources(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra. //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", false, "Select all resources in the namespace of the specified resource types") - cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") + cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "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", "*", "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", false, "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.") return cmd @@ -141,11 +142,12 @@ func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) builder := f.NewBuilder(!o.Local). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). - //FilenameParam(enforceNamespace, o.Filenames...). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.Local { diff --git a/pkg/kubectl/cmd/set/set_selector.go b/pkg/kubectl/cmd/set/set_selector.go index 05398c95c8d..ae3f074099d 100644 --- a/pkg/kubectl/cmd/set/set_selector.go +++ b/pkg/kubectl/cmd/set/set_selector.go @@ -89,13 +89,14 @@ func NewCmdSelector(f cmdutil.Factory, out io.Writer) *cobra.Command { }, } cmdutil.AddPrinterFlags(cmd) - cmd.Flags().Bool("all", false, "Select all resources in the namespace of the specified resource types") + 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.AddDryRunFlag(cmd) cmdutil.AddRecordFlag(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -121,10 +122,12 @@ func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [ return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) o.builder = f.NewBuilder(!o.local). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.fileOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.local { diff --git a/pkg/kubectl/cmd/set/set_serviceaccount.go b/pkg/kubectl/cmd/set/set_serviceaccount.go index ea088c53331..f983c170890 100644 --- a/pkg/kubectl/cmd/set/set_serviceaccount.go +++ b/pkg/kubectl/cmd/set/set_serviceaccount.go @@ -95,10 +95,11 @@ func NewCmdServiceAccount(f cmdutil.Factory, out, err io.Writer) *cobra.Command usage := "identifying the resource to get from a server." cmdutil.AddFilenameOptionFlags(cmd, &saConfig.fileNameOptions, usage) - cmd.Flags().BoolVar(&saConfig.all, "all", false, "Select all resources in the namespace of the specified resource types") + cmd.Flags().BoolVar(&saConfig.all, "all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") cmd.Flags().BoolVar(&saConfig.local, "local", false, "If true, set image will NOT contact api-server but run locally.") cmdutil.AddRecordFlag(cmd) cmdutil.AddDryRunFlag(cmd) + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -124,9 +125,11 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com } saConfig.serviceAccountName = args[len(args)-1] resources := args[:len(args)-1] + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) builder := f.NewBuilder(!saConfig.local).ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &saConfig.fileNameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !saConfig.local { builder.ResourceTypeOrNameArgs(saConfig.all, resources...). diff --git a/pkg/kubectl/cmd/set/set_subject.go b/pkg/kubectl/cmd/set/set_subject.go index 899f504ab31..791021b9b2f 100644 --- a/pkg/kubectl/cmd/set/set_subject.go +++ b/pkg/kubectl/cmd/set/set_subject.go @@ -98,13 +98,14 @@ func NewCmdSubject(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Co cmdutil.AddPrinterFlags(cmd) usage := "the resource to update the subjects" cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) - cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources in the namespace of the specified resource types") - cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)") + cmd.Flags().BoolVar(&options.All, "all", false, "Select all resources, including uninitialized ones, in the namespace of the specified resource types") + cmd.Flags().StringVarP(&options.Selector, "selector", "l", "", "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", false, "If true, set resources will NOT contact api-server but run locally.") cmdutil.AddDryRunFlag(cmd) cmd.Flags().StringArrayVar(&options.Users, "user", []string{}, "Usernames to bind to the role") cmd.Flags().StringArrayVar(&options.Groups, "group", []string{}, "Groups to bind to the role") cmd.Flags().StringArrayVar(&options.ServiceAccounts, "serviceaccount", []string{}, "Service accounts to bind to the role") + cmdutil.AddIncludeUninitializedFlag(cmd) return cmd } @@ -123,10 +124,12 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [] return err } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) builder := f.NewBuilder(!o.Local). ContinueOnError(). NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). Flatten() if !o.Local { diff --git a/pkg/kubectl/cmd/util/editor/BUILD b/pkg/kubectl/cmd/util/editor/BUILD index 31abf38cc4c..d0bcd1b1368 100644 --- a/pkg/kubectl/cmd/util/editor/BUILD +++ b/pkg/kubectl/cmd/util/editor/BUILD @@ -23,6 +23,7 @@ go_library( "//pkg/printers:go_default_library", "//vendor/github.com/evanphx/json-patch:go_default_library", "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", diff --git a/pkg/kubectl/cmd/util/editor/editoptions.go b/pkg/kubectl/cmd/util/editor/editoptions.go index cc15bd59a0a..2374ec2d8fc 100644 --- a/pkg/kubectl/cmd/util/editor/editoptions.go +++ b/pkg/kubectl/cmd/util/editor/editoptions.go @@ -29,6 +29,7 @@ import ( "github.com/evanphx/json-patch" "github.com/golang/glog" + "github.com/spf13/cobra" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -86,7 +87,7 @@ type editPrinterOptions struct { } // Complete completes all the required options -func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string) error { +func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []string, cmd *cobra.Command) error { if o.EditMode != NormalEditMode && o.EditMode != EditBeforeCreateMode && o.EditMode != ApplyEditMode { return fmt.Errorf("unsupported edit mode %q", o.EditMode) } @@ -118,8 +119,10 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args [] // when do normal edit or apply edit we need to always retrieve the latest resource from server b = b.ResourceTypeOrNameArgs(true, args...).Latest() } + includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) r := b.NamespaceParam(cmdNamespace).DefaultNamespace(). FilenameParam(enforceNamespace, &o.FilenameOptions). + IncludeUninitialized(includeUninitialized). ContinueOnError(). Flatten(). Do() @@ -133,6 +136,7 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args [] // resource builder to read objects from edited data return resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme). Stream(bytes.NewReader(data), "edited-file"). + IncludeUninitialized(includeUninitialized). ContinueOnError(). Flatten(). Do() diff --git a/pkg/kubectl/cmd/util/helpers.go b/pkg/kubectl/cmd/util/helpers.go index ca88b0daad8..29a5b7b942b 100644 --- a/pkg/kubectl/cmd/util/helpers.go +++ b/pkg/kubectl/cmd/util/helpers.go @@ -52,8 +52,9 @@ import ( ) const ( - ApplyAnnotationsFlag = "save-config" - DefaultErrorExitCode = 1 + ApplyAnnotationsFlag = "save-config" + DefaultErrorExitCode = 1 + IncludeUninitializedFlag = "include-uninitialized" ) type debugError interface { @@ -431,7 +432,7 @@ func AddDryRunFlag(cmd *cobra.Command) { } func AddIncludeUninitializedFlag(cmd *cobra.Command) { - cmd.Flags().Bool("include-uninitialized", false, "If true, include the object(s) that have not been realized yet. Such object(s) have a non-empty initializer list.") + cmd.Flags().Bool(IncludeUninitializedFlag, false, `If true, the kubectl command applies to uninitialized objects. If explicitly set to false, this flag overrides other flags that make the kubectl commands apply to uninitialized objects, e.g., "--all". Objects with empty metadata.initializers are regarded as initialized.`) } func AddPodRunningTimeoutFlag(cmd *cobra.Command, defaultTimeout time.Duration) { @@ -840,3 +841,25 @@ func ManualStrip(file []byte) []byte { } return stripped } + +// ShouldIncludeUninitialized identifies whether to include uninitialized objects. +// includeUninitialized is the default value. +// Assume we can parse `all` and `selector` from cmd. +func ShouldIncludeUninitialized(cmd *cobra.Command, includeUninitialized bool) bool { + shouldIncludeUninitialized := includeUninitialized + if cmd.Flags().Lookup("all") != nil && GetFlagBool(cmd, "all") { + // include the uninitialized objects by default + // unless explicitly set --include-uninitialized=false + shouldIncludeUninitialized = true + } + if cmd.Flags().Lookup("selector") != nil && GetFlagString(cmd, "selector") != "" { + // does not include the uninitialized objects by default + // unless explicitly set --include-uninitialized=true + shouldIncludeUninitialized = false + } + if cmd.Flags().Changed(IncludeUninitializedFlag) { + // get explicit value + shouldIncludeUninitialized = GetFlagBool(cmd, IncludeUninitializedFlag) + } + return shouldIncludeUninitialized +} From 8a6a25f5f0001187fead0b160f669613d9adc5b5 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Mon, 21 Aug 2017 18:13:16 +0800 Subject: [PATCH 5/6] add tests --- hack/make-rules/test-cmd-util.sh | 251 ++++++++++++++++++ .../initializer-clusterrolebinding.yaml | 17 ++ hack/testdata/initializer-deployments.yaml | 25 ++ .../initializer-redis-master-service.yaml | 19 ++ 4 files changed, 312 insertions(+) create mode 100644 hack/testdata/initializer-clusterrolebinding.yaml create mode 100644 hack/testdata/initializer-deployments.yaml create mode 100644 hack/testdata/initializer-redis-master-service.yaml diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 0c2c61ce05b..a446f36a5b2 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -4699,3 +4699,254 @@ runTests() { exit 1 fi } + +run_initializer_tests() { + set -o nounset + set -o errexit + + create_and_use_new_namespace + kube::log::status "Testing --include-uninitialized" + + ### Create a deployment + kubectl create -f hack/testdata/initializer-deployments.yaml 2>&1 "${kube_flags[@]}" || true + + ### Test kubectl get --include-uninitialized + # Command + output_message=$(kubectl get deployments 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should be part of the output + kube::test::if_has_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl get deployments --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should be part of the output + kube::test::if_has_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl get deployments --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should not be part of the output + kube::test::if_has_not_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl get deployments --include-uninitialized=true 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should not be part of the output + kube::test::if_has_not_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl get deployments web 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should not be part of the output + kube::test::if_has_not_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl get deployments --show-all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should be part of the output + kube::test::if_has_string "${output_message}" 'No resources found' + + ### Test kubectl describe --include-uninitialized + # Command + output_message=$(kubectl describe deployments 2>&1 "${kube_flags[@]}") + # Post-condition: The text "run=web" should be part of the output + kube::test::if_has_string "${output_message}" 'run=web' + # Command + output_message=$(kubectl describe deployments --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "run=web" should be part of the output + kube::test::if_has_string "${output_message}" 'run=web' + # Command + output_message=$(kubectl describe deployments --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl describe deployments web --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "run=web" should be part of the output + kube::test::if_has_string "${output_message}" 'run=web' + # Command + output_message=$(kubectl describe deployments web --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The text "run=web" should be part of the output + kube::test::if_has_string "${output_message}" 'run=web' + + ### Test kubectl label --include-uninitialized + # Command + output_message=$(kubectl label deployments labelkey1=labelvalue1 --all 2>&1 "${kube_flags[@]}") + # Post-condition: web is labelled + kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey1}}" 'labelvalue1' + # Command + output_message=$(kubectl label deployments labelkey2=labelvalue2 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl label deployments labelkey3=labelvalue3 -l run=web 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl label deployments labelkey4=labelvalue4 -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: web is labelled + kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey4}}" 'labelvalue4' + # Command + output_message=$(kubectl label deployments labelkey5=labelvalue5 -l run=web --all 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl label deployments labelkey6=labelvalue6 -l run=web --all --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: web is labelled + kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey6}}" 'labelvalue6' + # Command + output_message=$(kubectl label deployments web labelkey7=labelvalue7 2>&1 "${kube_flags[@]}") + # Post-condition: web is labelled + kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey7}}" 'labelvalue7' + + ### Test kubectl annotate --include-uninitialized + # Command + output_message=$(kubectl annotate deployments annotatekey1=annotatevalue1 --all 2>&1 "${kube_flags[@]}") + # Post-condition: DEPLOYMENT has annotation + kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey1}}" 'annotatevalue1' + # Command + output_message=$(kubectl annotate deployments annotatekey2=annotatevalue2 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl annotate deployments annotatekey3=annotatevalue3 -l run=web 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl annotate deployments annotatekey4=annotatevalue4 -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: DEPLOYMENT has annotation + kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey4}}" 'annotatevalue4' + # Command + output_message=$(kubectl annotate deployments annotatekey5=annotatevalue5 -l run=web --all 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl annotate deployments annotatekey6=annotatevalue6 -l run=web --all --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: DEPLOYMENT has annotation + kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey6}}" 'annotatevalue6' + # Command + output_message=$(kubectl annotate deployments web annotatekey7=annotatevalue7 2>&1 "${kube_flags[@]}") + # Post-condition: web DEPLOYMENT has annotation + kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey7}}" 'annotatevalue7' + + ### Test kubectl edit --include-uninitialized + [ "$(EDITOR=cat kubectl edit deployments 2>&1 "${kube_flags[@]}" | grep 'edit cancelled, no objects found')" ] + [ "$(EDITOR=cat kubectl edit deployments --include-uninitialized 2>&1 "${kube_flags[@]}" | grep 'Edit cancelled, no changes made.')" ] + + ### Test kubectl set image --include-uninitialized + # Command + output_message=$(kubectl set image deployments *=nginx:1.11 --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "image updated" should be part of the output + kube::test::if_has_string "${output_message}" 'image updated' + # Command + output_message=$(kubectl set image deployments *=nginx:1.11 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set image deployments *=nginx:1.11 -l run=web 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set image deployments *=nginx:1.12 -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "image updated" should be part of the output + kube::test::if_has_string "${output_message}" 'image updated' + # Command + output_message=$(kubectl set image deployments *=nginx:1.13 -l run=web --include-uninitialized --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "image updated" should be part of the output + kube::test::if_has_string "${output_message}" 'image updated' + + ### Test kubectl set resources --include-uninitialized + # Command + output_message=$(kubectl set resources deployments --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "resource requirements updated" should be part of the output + kube::test::if_has_string "${output_message}" 'resource requirements updated' + # Command + output_message=$(kubectl set resources deployments --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set resources deployments --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=256Mi -l run=web 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set resources deployments --limits=cpu=200m,memory=512Mi --requests=cpu=200m,memory=256Mi -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "resource requirements updated" should be part of the output + kube::test::if_has_string "${output_message}" 'resource requirements updated' + # Command + output_message=$(kubectl set resources deployments --limits=cpu=200m,memory=512Mi --requests=cpu=100m,memory=512Mi -l run=web --include-uninitialized --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "resource requirements updated" should be part of the output + kube::test::if_has_string "${output_message}" 'resource requirements updated' + + ### Test kubectl set selector --include-uninitialized + # Create a service with initializer + kubectl create -f hack/testdata/initializer-redis-master-service.yaml 2>&1 "${kube_flags[@]}" || true + # Command + output_message=$(kubectl set selector services role=padawan --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "selector updated" should be part of the output + kube::test::if_has_string "${output_message}" 'selector updated' + # Command + output_message=$(kubectl set selector services role=padawan --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + + ### Test kubectl set subject --include-uninitialized + # Create a create clusterrolebinding with initializer + kubectl create -f hack/testdata/initializer-clusterrolebinding.yaml 2>&1 "${kube_flags[@]}" || true + kube::test::get_object_assert clusterrolebinding/super-admin "{{range.subjects}}{{.name}}:{{end}}" 'super-admin:' + # Command + output_message=$(kubectl set subject clusterrolebinding --user=foo --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "subjects updated" should be part of the output + kube::test::if_has_string "${output_message}" 'subjects updated' + # Command + output_message=$(kubectl set subject clusterrolebinding --user=foo --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set subject clusterrolebinding --user=foo -l clusterrolebinding=super 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + # Command + output_message=$(kubectl set subject clusterrolebinding --user=foo -l clusterrolebinding=super --include-uninitialized 2>&1 "${kube_flags[@]}") + # Post-condition: The text "subjects updated" should be part of the output + kube::test::if_has_string "${output_message}" 'subjects updated' + # Command + output_message=$(kubectl set subject clusterrolebinding --user=foo -l clusterrolebinding=super --include-uninitialized --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "subjects updated" should be part of the output + kube::test::if_has_string "${output_message}" 'subjects updated' + + ### Test kubectl set serviceaccount --include-uninitialized + # Command + output_message=$(kubectl set serviceaccount deployment serviceaccount1 --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "serviceaccount updated" should be part of the output + kube::test::if_has_string "${output_message}" 'serviceaccount updated' + # Command + output_message=$(kubectl set serviceaccount deployment serviceaccount1 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The output should be empty + kube::test::if_empty_string "${output_message}" + + ### Test kubectl delete --include-uninitialized + kube::test::get_object_assert clusterrolebinding/super-admin "{{range.subjects}}{{.name}}:{{end}}" 'super-admin:' + # Command + output_message=$(kubectl delete clusterrolebinding --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") + # Post-condition: The text "No resources found" should be part of the output + kube::test::if_has_string "${output_message}" 'No resources found' + # Command + output_message=$(kubectl delete clusterrolebinding --all 2>&1 "${kube_flags[@]}") + # Post-condition: The text "deleted" should be part of the output + kube::test::if_has_string "${output_message}" 'deleted' + kube::test::get_object_assert clusterrolebinding/super-admin "{{range.items}}{{$id_field}}:{{end}}" '' + + ### Test kubectl apply --include-uninitialized + # Pre-Condition: no POD exists + kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' + # apply pod a + kubectl apply --prune --request-timeout=20 --include-uninitialized=false --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 "${kube_flags[@]}" + # check right pod exists + kube::test::get_object_assert pods/a "{{${id_field}}}" 'a' + # Post-condition: Other uninitialized resources should not be pruned + kube::test::get_object_assert deployments "{{range.items}}{{$id_field}}:{{end}}" 'web' + kube::test::get_object_assert services/redis-master "{{range.items}}{{$id_field}}:{{end}}" 'redis-master' + # cleanup + kubectl delete pod a + # apply pod a and prune uninitialized deployments web + kubectl apply --prune --request-timeout=20 --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 "${kube_flags[@]}" + # check right pod exists + kube::test::get_object_assert pods/a "{{${id_field}}}" 'a' + # Post-condition: Other resources should be pruned + kube::test::get_object_assert deployments "{{range.items}}{{$id_field}}:{{end}}" '' + kube::test::get_object_assert services/redis-master "{{range.items}}{{$id_field}}:{{end}}" '' + # cleanup + kubectl delete pod a + + set +o nounset + set +o errexit +} diff --git a/hack/testdata/initializer-clusterrolebinding.yaml b/hack/testdata/initializer-clusterrolebinding.yaml new file mode 100644 index 00000000000..694d76d2d27 --- /dev/null +++ b/hack/testdata/initializer-clusterrolebinding.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: super-admin + initializers: + pending: + - name: podimage.initializer.com + labels: + clusterrolebinding: super +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: admin +subjects: +- apiGroup: rbac.authorization.k8s.io + kind: User + name: super-admin diff --git a/hack/testdata/initializer-deployments.yaml b/hack/testdata/initializer-deployments.yaml new file mode 100644 index 00000000000..8564a874a3e --- /dev/null +++ b/hack/testdata/initializer-deployments.yaml @@ -0,0 +1,25 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: web + initializers: + pending: + - name: podimage.initializer.com + labels: + run: web +spec: + replicas: 5 + selector: + matchLabels: + run: web + template: + metadata: + labels: + run: web + spec: + containers: + - image: nginx:1.10 + name: web + ports: + - containerPort: 80 + protocol: TCP diff --git a/hack/testdata/initializer-redis-master-service.yaml b/hack/testdata/initializer-redis-master-service.yaml new file mode 100644 index 00000000000..557a199f17f --- /dev/null +++ b/hack/testdata/initializer-redis-master-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: redis-master + labels: + app: redis + role: master + tier: backend + initializers: + pending: + - name: podimage.initializer.com +spec: + ports: + - port: 6379 + targetPort: 6379 + selector: + app: redis + role: master + tier: backend From d80ff0f60c7d903305c740a6271616297f1570f7 Mon Sep 17 00:00:00 2001 From: Di Xu Date: Fri, 25 Aug 2017 14:32:05 +0800 Subject: [PATCH 6/6] test fix --- hack/make-rules/test-cmd-util.sh | 49 +++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index a446f36a5b2..e5ea3b356c5 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -4708,7 +4708,7 @@ run_initializer_tests() { kube::log::status "Testing --include-uninitialized" ### Create a deployment - kubectl create -f hack/testdata/initializer-deployments.yaml 2>&1 "${kube_flags[@]}" || true + kubectl create --request-timeout=1 -f hack/testdata/initializer-deployments.yaml 2>&1 "${kube_flags[@]}" || true ### Test kubectl get --include-uninitialized # Command @@ -4721,16 +4721,12 @@ run_initializer_tests() { kube::test::if_has_string "${output_message}" 'No resources found' # Command output_message=$(kubectl get deployments --include-uninitialized 2>&1 "${kube_flags[@]}") - # Post-condition: The text "No resources found" should not be part of the output - kube::test::if_has_not_string "${output_message}" 'No resources found' - # Command - output_message=$(kubectl get deployments --include-uninitialized=true 2>&1 "${kube_flags[@]}") - # Post-condition: The text "No resources found" should not be part of the output - kube::test::if_has_not_string "${output_message}" 'No resources found' + # Post-condition: I assume "web" is the deployment name + kube::test::if_has_string "${output_message}" 'web' # Command output_message=$(kubectl get deployments web 2>&1 "${kube_flags[@]}") - # Post-condition: The text "No resources found" should not be part of the output - kube::test::if_has_not_string "${output_message}" 'No resources found' + # Post-condition: I assume "web" is the deployment name + kube::test::if_has_string "${output_message}" 'web' # Command output_message=$(kubectl get deployments --show-all 2>&1 "${kube_flags[@]}") # Post-condition: The text "No resources found" should be part of the output @@ -4762,6 +4758,7 @@ run_initializer_tests() { # Command output_message=$(kubectl label deployments labelkey1=labelvalue1 --all 2>&1 "${kube_flags[@]}") # Post-condition: web is labelled + kube::test::if_has_string "${output_message}" 'deployment "web" labeled' kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey1}}" 'labelvalue1' # Command output_message=$(kubectl label deployments labelkey2=labelvalue2 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") @@ -4774,6 +4771,7 @@ run_initializer_tests() { # Command output_message=$(kubectl label deployments labelkey4=labelvalue4 -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") # Post-condition: web is labelled + kube::test::if_has_string "${output_message}" 'deployment "web" labeled' kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey4}}" 'labelvalue4' # Command output_message=$(kubectl label deployments labelkey5=labelvalue5 -l run=web --all 2>&1 "${kube_flags[@]}") @@ -4782,16 +4780,21 @@ run_initializer_tests() { # Command output_message=$(kubectl label deployments labelkey6=labelvalue6 -l run=web --all --include-uninitialized 2>&1 "${kube_flags[@]}") # Post-condition: web is labelled + kube::test::if_has_string "${output_message}" 'deployment "web" labeled' kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey6}}" 'labelvalue6' # Command output_message=$(kubectl label deployments web labelkey7=labelvalue7 2>&1 "${kube_flags[@]}") # Post-condition: web is labelled + kube::test::if_has_string "${output_message}" 'deployment "web" labeled' kube::test::get_object_assert 'deployments web' "{{${labels_field}.labelkey7}}" 'labelvalue7' + # Found All Labels + kube::test::get_object_assert 'deployments web' "{{${labels_field}}}" 'map[labelkey1:labelvalue1 labelkey4:labelvalue4 labelkey6:labelvalue6 labelkey7:labelvalue7 run:web]' ### Test kubectl annotate --include-uninitialized # Command output_message=$(kubectl annotate deployments annotatekey1=annotatevalue1 --all 2>&1 "${kube_flags[@]}") # Post-condition: DEPLOYMENT has annotation + kube::test::if_has_string "${output_message}" 'deployment "web" annotated' kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey1}}" 'annotatevalue1' # Command output_message=$(kubectl annotate deployments annotatekey2=annotatevalue2 --all --include-uninitialized=false 2>&1 "${kube_flags[@]}") @@ -4804,6 +4807,7 @@ run_initializer_tests() { # Command output_message=$(kubectl annotate deployments annotatekey4=annotatevalue4 -l run=web --include-uninitialized 2>&1 "${kube_flags[@]}") # Post-condition: DEPLOYMENT has annotation + kube::test::if_has_string "${output_message}" 'deployment "web" annotated' kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey4}}" 'annotatevalue4' # Command output_message=$(kubectl annotate deployments annotatekey5=annotatevalue5 -l run=web --all 2>&1 "${kube_flags[@]}") @@ -4812,10 +4816,12 @@ run_initializer_tests() { # Command output_message=$(kubectl annotate deployments annotatekey6=annotatevalue6 -l run=web --all --include-uninitialized 2>&1 "${kube_flags[@]}") # Post-condition: DEPLOYMENT has annotation + kube::test::if_has_string "${output_message}" 'deployment "web" annotated' kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey6}}" 'annotatevalue6' # Command output_message=$(kubectl annotate deployments web annotatekey7=annotatevalue7 2>&1 "${kube_flags[@]}") # Post-condition: web DEPLOYMENT has annotation + kube::test::if_has_string "${output_message}" 'deployment "web" annotated' kube::test::get_object_assert 'deployments web' "{{${annotations_field}.annotatekey7}}" 'annotatevalue7' ### Test kubectl edit --include-uninitialized @@ -4868,7 +4874,7 @@ run_initializer_tests() { ### Test kubectl set selector --include-uninitialized # Create a service with initializer - kubectl create -f hack/testdata/initializer-redis-master-service.yaml 2>&1 "${kube_flags[@]}" || true + kubectl create --request-timeout=1 -f hack/testdata/initializer-redis-master-service.yaml 2>&1 "${kube_flags[@]}" || true # Command output_message=$(kubectl set selector services role=padawan --all 2>&1 "${kube_flags[@]}") # Post-condition: The text "selector updated" should be part of the output @@ -4880,7 +4886,7 @@ run_initializer_tests() { ### Test kubectl set subject --include-uninitialized # Create a create clusterrolebinding with initializer - kubectl create -f hack/testdata/initializer-clusterrolebinding.yaml 2>&1 "${kube_flags[@]}" || true + kubectl create --request-timeout=1 -f hack/testdata/initializer-clusterrolebinding.yaml 2>&1 "${kube_flags[@]}" || true kube::test::get_object_assert clusterrolebinding/super-admin "{{range.subjects}}{{.name}}:{{end}}" 'super-admin:' # Command output_message=$(kubectl set subject clusterrolebinding --user=foo --all 2>&1 "${kube_flags[@]}") @@ -4929,7 +4935,7 @@ run_initializer_tests() { # Pre-Condition: no POD exists kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" '' # apply pod a - kubectl apply --prune --request-timeout=20 --include-uninitialized=false --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 "${kube_flags[@]}" + kubectl apply --prune --request-timeout=20 --include-uninitialized=false --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 # check right pod exists kube::test::get_object_assert pods/a "{{${id_field}}}" 'a' # Post-condition: Other uninitialized resources should not be pruned @@ -4938,14 +4944,25 @@ run_initializer_tests() { # cleanup kubectl delete pod a # apply pod a and prune uninitialized deployments web - kubectl apply --prune --request-timeout=20 --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 "${kube_flags[@]}" + kubectl apply --prune --request-timeout=20 --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 # check right pod exists kube::test::get_object_assert pods/a "{{${id_field}}}" 'a' - # Post-condition: Other resources should be pruned - kube::test::get_object_assert deployments "{{range.items}}{{$id_field}}:{{end}}" '' - kube::test::get_object_assert services/redis-master "{{range.items}}{{$id_field}}:{{end}}" '' + # Post-condition: Other uninitialized resources should not be pruned + kube::test::get_object_assert deployments/web "{{range.items}}{{$id_field}}:{{end}}" 'web' + kube::test::get_object_assert services/redis-master "{{range.items}}{{$id_field}}:{{end}}" 'redis-master' # cleanup kubectl delete pod a + # apply pod a and prune uninitialized deployments web + kubectl apply --prune --request-timeout=20 --include-uninitialized --all -f hack/testdata/prune/a.yaml "${kube_flags[@]}" 2>&1 + # check right pod exists + kube::test::get_object_assert pods/a "{{${id_field}}}" 'a' + # Post-condition: Other uninitialized resources should not be pruned + kube::test::get_object_assert deployments/web "{{range.items}}{{$id_field}}:{{end}}" 'web' + kube::test::get_object_assert services/redis-master "{{range.items}}{{$id_field}}:{{end}}" 'redis-master' + # cleanup + kubectl delete pod a + kubectl delete --request-timeout=1 deploy web + kubectl delete --request-timeout=1 service redis-master set +o nounset set +o errexit