diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index 82807b442c0..d9cbb9fc9a7 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -4759,3 +4759,271 @@ 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 --request-timeout=1 -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: 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: 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 + 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::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[@]}") + # 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::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[@]}") + # 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::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[@]}") + # 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::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[@]}") + # 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::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 + [ "$(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 --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 + 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 --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[@]}") + # 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 + # 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 + # 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 + # 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 +} 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 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 6b6172ccd72..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) + 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 33dadb10e3f..8593f14152a 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 b6d046fc791..91b88927438 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.") @@ -200,6 +202,15 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ // 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 { @@ -211,6 +222,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(). @@ -300,6 +312,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/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 f85f2b1fcf1..e0be8d82afc 100644 --- a/pkg/kubectl/cmd/set/helper.go +++ b/pkg/kubectl/cmd/set/helper.go @@ -26,7 +26,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "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" ) @@ -62,7 +62,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 cfd2fb85511..b41af0f842f 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 { @@ -420,6 +421,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(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) { 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") } @@ -826,3 +831,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 +} 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 { 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