diff --git a/hack/testdata/pod-apply-status.yaml b/hack/testdata/pod-apply-status.yaml new file mode 100644 index 00000000000..d0131674dde --- /dev/null +++ b/hack/testdata/pod-apply-status.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Pod +metadata: + name: test-pod +status: + conditions: + - type: example.io/Foo + status: "True" + message: message + reason: reason + diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go index c3b31e31fb0..3b0b2667ed7 100644 --- a/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go +++ b/staging/src/k8s.io/kubectl/pkg/cmd/apply/apply.go @@ -50,6 +50,7 @@ import ( "k8s.io/kubectl/pkg/util/i18n" "k8s.io/kubectl/pkg/util/openapi" "k8s.io/kubectl/pkg/util/prune" + "k8s.io/kubectl/pkg/util/slice" "k8s.io/kubectl/pkg/util/templates" "k8s.io/kubectl/pkg/validation" ) @@ -71,6 +72,7 @@ type ApplyFlags struct { All bool Overwrite bool OpenAPIPatch bool + Subresource string PruneAllowlist []string @@ -97,6 +99,7 @@ type ApplyOptions struct { All bool Overwrite bool OpenAPIPatch bool + Subresource string ValidationDirective string Validator validation.Schema @@ -178,6 +181,8 @@ var ( var ApplySetToolVersion = version.Get().GitVersion +var supportedSubresources = []string{"status", "scale"} + // NewApplyFlags returns a default ApplyFlags func NewApplyFlags(streams genericiooptions.IOStreams) *ApplyFlags { return &ApplyFlags{ @@ -235,6 +240,7 @@ func (flags *ApplyFlags) AddFlags(cmd *cobra.Command) { cmdutil.AddPruningFlags(cmd, &flags.Prune, &flags.PruneAllowlist, &flags.All, &flags.ApplySetRef) cmd.Flags().BoolVar(&flags.Overwrite, "overwrite", flags.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration") cmd.Flags().BoolVar(&flags.OpenAPIPatch, "openapi-patch", flags.OpenAPIPatch, "If true, use openapi to calculate diff when the openapi presents and the resource can be found in the openapi spec. Otherwise, fall back to use baked-in types.") + cmdutil.AddSubresourceFlags(cmd, &flags.Subresource, "If specified, apply will operate on the subresource of the requested object. Only allowed when using --server-side.", supportedSubresources...) } // ToOptions converts from CLI inputs to runtime inputs @@ -356,6 +362,7 @@ func (flags *ApplyFlags) ToOptions(f cmdutil.Factory, cmd *cobra.Command, baseNa All: flags.All, Overwrite: flags.Overwrite, OpenAPIPatch: flags.OpenAPIPatch, + Subresource: flags.Subresource, Recorder: recorder, Namespace: namespace, @@ -438,6 +445,12 @@ func (o *ApplyOptions) Validate() error { } } } + if len(o.Subresource) > 0 && !slice.ContainsString(supportedSubresources, o.Subresource, nil) { + return fmt.Errorf("invalid subresource value: %q. Must be one of %v", o.Subresource, supportedSubresources) + } + if len(o.Subresource) > 0 && !o.ServerSideApply { + return fmt.Errorf("--subresource can only be specified for --server-side") + } return nil } @@ -577,13 +590,15 @@ func (o *ApplyOptions) applyOneObject(info *resource.Info) error { options := metav1.PatchOptions{ Force: &o.ForceConflicts, } - obj, err := helper.Patch( - info.Namespace, - info.Name, - types.ApplyPatchType, - data, - &options, - ) + obj, err := helper. + WithSubresource(o.Subresource). + Patch( + info.Namespace, + info.Name, + types.ApplyPatchType, + data, + &options, + ) if err != nil { if isIncompatibleServerError(err) { err = fmt.Errorf("Server-side apply not available on the server: (%v)", err) diff --git a/test/cmd/apply.sh b/test/cmd/apply.sh index 27d0b0dfeeb..2da74f60f4f 100755 --- a/test/cmd/apply.sh +++ b/test/cmd/apply.sh @@ -413,6 +413,10 @@ run_kubectl_server_side_apply_tests() { kubectl apply --server-side --field-manager=my-field-manager --force-conflicts -f hack/testdata/pod.yaml "${kube_flags[@]:?}" output_message=$(kubectl get -f hack/testdata/pod.yaml -o=jsonpath='{.metadata.managedFields[*].manager}' "${kube_flags[@]:?}" 2>&1) kube::test::if_has_string "${output_message}" 'my-field-manager' + # can add pod condition + kubectl apply --server-side --subresource=status --field-manager=my-field-manager -f hack/testdata/pod-apply-status.yaml "${kube_flags[@]:?}" + output_message=$(kubectl get -f hack/testdata/pod.yaml -o=jsonpath='{.status.conditions[*].type}' "${kube_flags[@]:?}" 2>&1) + kube::test::if_has_string "${output_message}" 'example.io/Foo' # Clean up kubectl delete pods test-pod "${kube_flags[@]:?}"