1
16
17 package apply
18
19 import (
20 "context"
21 "fmt"
22 "io"
23 "net/http"
24
25 "github.com/spf13/cobra"
26 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
27
28 corev1 "k8s.io/api/core/v1"
29 "k8s.io/apimachinery/pkg/api/errors"
30 "k8s.io/apimachinery/pkg/api/meta"
31 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
33 "k8s.io/apimachinery/pkg/runtime"
34 "k8s.io/apimachinery/pkg/types"
35 utilerrors "k8s.io/apimachinery/pkg/util/errors"
36 "k8s.io/apimachinery/pkg/util/sets"
37 "k8s.io/cli-runtime/pkg/genericclioptions"
38 "k8s.io/cli-runtime/pkg/genericiooptions"
39 "k8s.io/cli-runtime/pkg/printers"
40 "k8s.io/cli-runtime/pkg/resource"
41 "k8s.io/client-go/dynamic"
42 "k8s.io/client-go/openapi3"
43 "k8s.io/client-go/util/csaupgrade"
44 "k8s.io/component-base/version"
45 "k8s.io/klog/v2"
46 "k8s.io/kubectl/pkg/cmd/delete"
47 cmdutil "k8s.io/kubectl/pkg/cmd/util"
48 "k8s.io/kubectl/pkg/scheme"
49 "k8s.io/kubectl/pkg/util"
50 "k8s.io/kubectl/pkg/util/i18n"
51 "k8s.io/kubectl/pkg/util/openapi"
52 "k8s.io/kubectl/pkg/util/prune"
53 "k8s.io/kubectl/pkg/util/templates"
54 "k8s.io/kubectl/pkg/validation"
55 )
56
57
58
59
60 type ApplyFlags struct {
61 RecordFlags *genericclioptions.RecordFlags
62 PrintFlags *genericclioptions.PrintFlags
63
64 DeleteFlags *delete.DeleteFlags
65
66 FieldManager string
67 Selector string
68 Prune bool
69 PruneResources []prune.Resource
70 ApplySetRef string
71 All bool
72 Overwrite bool
73 OpenAPIPatch bool
74
75 PruneAllowlist []string
76
77 genericiooptions.IOStreams
78 }
79
80
81 type ApplyOptions struct {
82 Recorder genericclioptions.Recorder
83
84 PrintFlags *genericclioptions.PrintFlags
85 ToPrinter func(string) (printers.ResourcePrinter, error)
86
87 DeleteOptions *delete.DeleteOptions
88
89 ServerSideApply bool
90 ForceConflicts bool
91 FieldManager string
92 Selector string
93 DryRunStrategy cmdutil.DryRunStrategy
94 Prune bool
95 PruneResources []prune.Resource
96 cmdBaseName string
97 All bool
98 Overwrite bool
99 OpenAPIPatch bool
100
101 ValidationDirective string
102 Validator validation.Schema
103 Builder *resource.Builder
104 Mapper meta.RESTMapper
105 DynamicClient dynamic.Interface
106 OpenAPIGetter openapi.OpenAPIResourcesGetter
107 OpenAPIV3Root openapi3.Root
108
109 Namespace string
110 EnforceNamespace bool
111
112 genericiooptions.IOStreams
113
114
115
116
117
118
119
120
121 objects []*resource.Info
122 objectsCached bool
123
124
125
126 VisitedUids sets.Set[types.UID]
127 VisitedNamespaces sets.Set[string]
128
129
130
131
132 PreProcessorFn func() error
133
134
135 PostProcessorFn func() error
136
137
138
139 ApplySet *ApplySet
140 }
141
142 var (
143 applyLong = templates.LongDesc(i18n.T(`
144 Apply a configuration to a resource by file name or stdin.
145 The resource name must be specified. This resource will be created if it doesn't exist yet.
146 To use 'apply', always create the resource initially with either 'apply' or 'create --save-config'.
147
148 JSON and YAML formats are accepted.
149
150 Alpha Disclaimer: the --prune functionality is not yet complete. Do not use unless you are aware of what the current state is. See https://issues.k8s.io/34274.`))
151
152 applyExample = templates.Examples(i18n.T(`
153 # Apply the configuration in pod.json to a pod
154 kubectl apply -f ./pod.json
155
156 # Apply resources from a directory containing kustomization.yaml - e.g. dir/kustomization.yaml
157 kubectl apply -k dir/
158
159 # Apply the JSON passed into stdin to a pod
160 cat pod.json | kubectl apply -f -
161
162 # Apply the configuration from all files that end with '.json'
163 kubectl apply -f '*.json'
164
165 # Note: --prune is still in Alpha
166 # Apply the configuration in manifest.yaml that matches label app=nginx and delete all other resources that are not in the file and match label app=nginx
167 kubectl apply --prune -f manifest.yaml -l app=nginx
168
169 # Apply the configuration in manifest.yaml and delete all the other config maps that are not in the file
170 kubectl apply --prune -f manifest.yaml --all --prune-allowlist=core/v1/ConfigMap`))
171
172 warningNoLastAppliedConfigAnnotation = "Warning: resource %[1]s is missing the %[2]s annotation which is required by %[3]s apply. %[3]s apply should only be used on resources created declaratively by either %[3]s create --save-config or %[3]s apply. The missing annotation will be patched automatically.\n"
173 warningChangesOnDeletingResource = "Warning: Detected changes to resource %[1]s which is currently being deleted.\n"
174 warningMigrationLastAppliedFailed = "Warning: failed to migrate kubectl.kubernetes.io/last-applied-configuration for Server-Side Apply. This is non-fatal and will be retried next time you apply. Error: %[1]s\n"
175 warningMigrationPatchFailed = "Warning: server rejected managed fields migration to Server-Side Apply. This is non-fatal and will be retried next time you apply. Error: %[1]s\n"
176 warningMigrationReapplyFailed = "Warning: failed to re-apply configuration after performing Server-Side Apply migration. This is non-fatal and will be retried next time you apply. Error: %[1]s\n"
177 )
178
179 var ApplySetToolVersion = version.Get().GitVersion
180
181
182 func NewApplyFlags(streams genericiooptions.IOStreams) *ApplyFlags {
183 return &ApplyFlags{
184 RecordFlags: genericclioptions.NewRecordFlags(),
185 DeleteFlags: delete.NewDeleteFlags("The files that contain the configurations to apply."),
186 PrintFlags: genericclioptions.NewPrintFlags("created").WithTypeSetter(scheme.Scheme),
187
188 Overwrite: true,
189 OpenAPIPatch: true,
190
191 IOStreams: streams,
192 }
193 }
194
195
196 func NewCmdApply(baseName string, f cmdutil.Factory, ioStreams genericiooptions.IOStreams) *cobra.Command {
197 flags := NewApplyFlags(ioStreams)
198
199 cmd := &cobra.Command{
200 Use: "apply (-f FILENAME | -k DIRECTORY)",
201 DisableFlagsInUseLine: true,
202 Short: i18n.T("Apply a configuration to a resource by file name or stdin"),
203 Long: applyLong,
204 Example: applyExample,
205 Run: func(cmd *cobra.Command, args []string) {
206 o, err := flags.ToOptions(f, cmd, baseName, args)
207 cmdutil.CheckErr(err)
208 cmdutil.CheckErr(o.Validate())
209 cmdutil.CheckErr(o.Run())
210 },
211 }
212
213 flags.AddFlags(cmd)
214
215
216 cmd.AddCommand(NewCmdApplyViewLastApplied(f, flags.IOStreams))
217 cmd.AddCommand(NewCmdApplySetLastApplied(f, flags.IOStreams))
218 cmd.AddCommand(NewCmdApplyEditLastApplied(f, flags.IOStreams))
219
220 return cmd
221 }
222
223
224 func (flags *ApplyFlags) AddFlags(cmd *cobra.Command) {
225
226 flags.DeleteFlags.AddFlags(cmd)
227 flags.RecordFlags.AddFlags(cmd)
228 flags.PrintFlags.AddFlags(cmd)
229
230 cmdutil.AddValidateFlags(cmd)
231 cmdutil.AddDryRunFlag(cmd)
232 cmdutil.AddServerSideApplyFlags(cmd)
233 cmdutil.AddFieldManagerFlagVar(cmd, &flags.FieldManager, FieldManagerClientSideApply)
234 cmdutil.AddLabelSelectorFlagVar(cmd, &flags.Selector)
235 cmdutil.AddPruningFlags(cmd, &flags.Prune, &flags.PruneAllowlist, &flags.All, &flags.ApplySetRef)
236 cmd.Flags().BoolVar(&flags.Overwrite, "overwrite", flags.Overwrite, "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration")
237 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.")
238 }
239
240
241 func (flags *ApplyFlags) ToOptions(f cmdutil.Factory, cmd *cobra.Command, baseName string, args []string) (*ApplyOptions, error) {
242 if len(args) != 0 {
243 return nil, cmdutil.UsageErrorf(cmd, "Unexpected args: %v", args)
244 }
245
246 serverSideApply := cmdutil.GetServerSideApplyFlag(cmd)
247 forceConflicts := cmdutil.GetForceConflictsFlag(cmd)
248 dryRunStrategy, err := cmdutil.GetDryRunStrategy(cmd)
249 if err != nil {
250 return nil, err
251 }
252
253 dynamicClient, err := f.DynamicClient()
254 if err != nil {
255 return nil, err
256 }
257
258 fieldManager := GetApplyFieldManagerFlag(cmd, serverSideApply)
259
260
261 toPrinter := func(operation string) (printers.ResourcePrinter, error) {
262 flags.PrintFlags.NamePrintFlags.Operation = operation
263 cmdutil.PrintFlagsWithDryRunStrategy(flags.PrintFlags, dryRunStrategy)
264 return flags.PrintFlags.ToPrinter()
265 }
266
267 flags.RecordFlags.Complete(cmd)
268 recorder, err := flags.RecordFlags.ToRecorder()
269 if err != nil {
270 return nil, err
271 }
272
273 deleteOptions, err := flags.DeleteFlags.ToOptions(dynamicClient, flags.IOStreams)
274 if err != nil {
275 return nil, err
276 }
277
278 err = deleteOptions.FilenameOptions.RequireFilenameOrKustomize()
279 if err != nil {
280 return nil, err
281 }
282
283 var openAPIV3Root openapi3.Root
284 if !cmdutil.OpenAPIV3Patch.IsDisabled() {
285 openAPIV3Client, err := f.OpenAPIV3Client()
286 if err == nil {
287 openAPIV3Root = openapi3.NewRoot(openAPIV3Client)
288 } else {
289 klog.V(4).Infof("warning: OpenAPI V3 Patch is enabled but is unable to be loaded. Will fall back to OpenAPI V2")
290 }
291 }
292
293 validationDirective, err := cmdutil.GetValidationDirective(cmd)
294 if err != nil {
295 return nil, err
296 }
297 validator, err := f.Validator(validationDirective)
298 if err != nil {
299 return nil, err
300 }
301 builder := f.NewBuilder()
302 mapper, err := f.ToRESTMapper()
303 if err != nil {
304 return nil, err
305 }
306
307 namespace, enforceNamespace, err := f.ToRawKubeConfigLoader().Namespace()
308 if err != nil {
309 return nil, err
310 }
311
312 var applySet *ApplySet
313 if flags.ApplySetRef != "" {
314 parent, err := ParseApplySetParentRef(flags.ApplySetRef, mapper)
315 if err != nil {
316 return nil, fmt.Errorf("invalid parent reference %q: %w", flags.ApplySetRef, err)
317 }
318
319
320 if enforceNamespace && parent.IsNamespaced() {
321 parent.Namespace = namespace
322 }
323 tooling := ApplySetTooling{Name: baseName, Version: ApplySetToolVersion}
324 restClient, err := f.UnstructuredClientForMapping(parent.RESTMapping)
325 if err != nil {
326 return nil, fmt.Errorf("failed to initialize RESTClient for ApplySet: %w", err)
327 }
328 if restClient == nil {
329 return nil, fmt.Errorf("could not build RESTClient for ApplySet")
330 }
331 applySet = NewApplySet(parent, tooling, mapper, restClient)
332 }
333 if flags.Prune {
334 flags.PruneResources, err = prune.ParseResources(mapper, flags.PruneAllowlist)
335 if err != nil {
336 return nil, err
337 }
338 }
339
340 o := &ApplyOptions{
341
342
343 cmdBaseName: baseName,
344
345 PrintFlags: flags.PrintFlags,
346
347 DeleteOptions: deleteOptions,
348 ToPrinter: toPrinter,
349 ServerSideApply: serverSideApply,
350 ForceConflicts: forceConflicts,
351 FieldManager: fieldManager,
352 Selector: flags.Selector,
353 DryRunStrategy: dryRunStrategy,
354 Prune: flags.Prune,
355 PruneResources: flags.PruneResources,
356 All: flags.All,
357 Overwrite: flags.Overwrite,
358 OpenAPIPatch: flags.OpenAPIPatch,
359
360 Recorder: recorder,
361 Namespace: namespace,
362 EnforceNamespace: enforceNamespace,
363 Validator: validator,
364 ValidationDirective: validationDirective,
365 Builder: builder,
366 Mapper: mapper,
367 DynamicClient: dynamicClient,
368 OpenAPIGetter: f,
369 OpenAPIV3Root: openAPIV3Root,
370
371 IOStreams: flags.IOStreams,
372
373 objects: []*resource.Info{},
374 objectsCached: false,
375
376 VisitedUids: sets.New[types.UID](),
377 VisitedNamespaces: sets.New[string](),
378
379 ApplySet: applySet,
380 }
381
382 o.PostProcessorFn = o.PrintAndPrunePostProcessor()
383
384 return o, nil
385 }
386
387
388 func (o *ApplyOptions) Validate() error {
389 if o.ForceConflicts && !o.ServerSideApply {
390 return fmt.Errorf("--force-conflicts only works with --server-side")
391 }
392
393 if o.DryRunStrategy == cmdutil.DryRunClient && o.ServerSideApply {
394 return fmt.Errorf("--dry-run=client doesn't work with --server-side (did you mean --dry-run=server instead?)")
395 }
396
397 if o.ServerSideApply && o.DeleteOptions.ForceDeletion {
398 return fmt.Errorf("--force cannot be used with --server-side")
399 }
400
401 if o.DryRunStrategy == cmdutil.DryRunServer && o.DeleteOptions.ForceDeletion {
402 return fmt.Errorf("--dry-run=server cannot be used with --force")
403 }
404
405 if o.All && len(o.Selector) > 0 {
406 return fmt.Errorf("cannot set --all and --selector at the same time")
407 }
408
409 if o.ApplySet != nil {
410 if !o.Prune {
411 return fmt.Errorf("--applyset requires --prune")
412 }
413 if err := o.ApplySet.Validate(context.TODO(), o.DynamicClient); err != nil {
414 return err
415 }
416 }
417 if o.Prune {
418
419
420 if o.DeleteOptions.ForceDeletion {
421 return fmt.Errorf("--force cannot be used with --prune")
422 }
423
424 if o.ApplySet != nil {
425 if o.All {
426 return fmt.Errorf("--all is incompatible with --applyset")
427 } else if o.Selector != "" {
428 return fmt.Errorf("--selector is incompatible with --applyset")
429 } else if len(o.PruneResources) > 0 {
430 return fmt.Errorf("--prune-allowlist is incompatible with --applyset")
431 }
432 } else {
433 if !o.All && o.Selector == "" {
434 return fmt.Errorf("all resources selected for prune without explicitly passing --all. To prune all resources, pass the --all flag. If you did not mean to prune all resources, specify a label selector")
435 }
436 if o.ServerSideApply {
437 return fmt.Errorf("--prune is in alpha and doesn't currently work on objects created by server-side apply")
438 }
439 }
440 }
441
442 return nil
443 }
444
445 func isIncompatibleServerError(err error) bool {
446
447
448 if _, ok := err.(*errors.StatusError); !ok {
449
450 return false
451 }
452 return err.(*errors.StatusError).Status().Code == http.StatusUnsupportedMediaType
453 }
454
455
456
457
458
459
460
461 func (o *ApplyOptions) GetObjects() ([]*resource.Info, error) {
462 var err error = nil
463 if !o.objectsCached {
464 r := o.Builder.
465 Unstructured().
466 Schema(o.Validator).
467 ContinueOnError().
468 NamespaceParam(o.Namespace).DefaultNamespace().
469 FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
470 LabelSelectorParam(o.Selector).
471 Flatten().
472 Do()
473
474 o.objects, err = r.Infos()
475
476 if o.ApplySet != nil {
477 if err := o.ApplySet.AddLabels(o.objects...); err != nil {
478 return nil, err
479 }
480 }
481
482 o.objectsCached = true
483 }
484 return o.objects, err
485 }
486
487
488
489 func (o *ApplyOptions) SetObjects(infos []*resource.Info) {
490 o.objects = infos
491 o.objectsCached = true
492 }
493
494
495 func (o *ApplyOptions) Run() error {
496 if o.PreProcessorFn != nil {
497 klog.V(4).Infof("Running apply pre-processor function")
498 if err := o.PreProcessorFn(); err != nil {
499 return err
500 }
501 }
502
503
504 if o.EnforceNamespace {
505 o.VisitedNamespaces.Insert(o.Namespace)
506 }
507
508
509
510 errs := []error{}
511 infos, err := o.GetObjects()
512 if err != nil {
513 errs = append(errs, err)
514 }
515 if len(infos) == 0 && len(errs) == 0 {
516 return fmt.Errorf("no objects passed to apply")
517 }
518
519 if o.ApplySet != nil {
520 if err := o.ApplySet.BeforeApply(infos, o.DryRunStrategy, o.ValidationDirective); err != nil {
521 return err
522 }
523 }
524
525
526 for _, info := range infos {
527 if err := o.applyOneObject(info); err != nil {
528 errs = append(errs, err)
529 }
530 }
531
532
533 if len(errs) == 1 {
534 return errs[0]
535 }
536 if len(errs) > 1 {
537 return utilerrors.NewAggregate(errs)
538 }
539
540 if o.PostProcessorFn != nil {
541 klog.V(4).Infof("Running apply post-processor function")
542 if err := o.PostProcessorFn(); err != nil {
543 return err
544 }
545 }
546
547 return nil
548 }
549
550 func (o *ApplyOptions) applyOneObject(info *resource.Info) error {
551 o.MarkNamespaceVisited(info)
552
553 if err := o.Recorder.Record(info.Object); err != nil {
554 klog.V(4).Infof("error recording current command: %v", err)
555 }
556
557 if len(info.Name) == 0 {
558 metadata, _ := meta.Accessor(info.Object)
559 generatedName := metadata.GetGenerateName()
560 if len(generatedName) > 0 {
561 return fmt.Errorf("from %s: cannot use generate name with apply", generatedName)
562 }
563 }
564
565 helper := resource.NewHelper(info.Client, info.Mapping).
566 DryRun(o.DryRunStrategy == cmdutil.DryRunServer).
567 WithFieldManager(o.FieldManager).
568 WithFieldValidation(o.ValidationDirective)
569
570 if o.ServerSideApply {
571
572 data, err := runtime.Encode(unstructured.UnstructuredJSONScheme, info.Object)
573 if err != nil {
574 return cmdutil.AddSourceToErr("serverside-apply", info.Source, err)
575 }
576
577 options := metav1.PatchOptions{
578 Force: &o.ForceConflicts,
579 }
580 obj, err := helper.Patch(
581 info.Namespace,
582 info.Name,
583 types.ApplyPatchType,
584 data,
585 &options,
586 )
587 if err != nil {
588 if isIncompatibleServerError(err) {
589 err = fmt.Errorf("Server-side apply not available on the server: (%v)", err)
590 }
591 if errors.IsConflict(err) {
592 err = fmt.Errorf(`%v
593 Please review the fields above--they currently have other managers. Here
594 are the ways you can resolve this warning:
595 * If you intend to manage all of these fields, please re-run the apply
596 command with the `+"`--force-conflicts`"+` flag.
597 * If you do not intend to manage all of the fields, please edit your
598 manifest to remove references to the fields that should keep their
599 current managers.
600 * You may co-own fields by updating your manifest to match the existing
601 value; in this case, you'll become the manager if the other manager(s)
602 stop managing the field (remove it from their configuration).
603 See https://kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts`, err)
604 }
605 return err
606 }
607
608 info.Refresh(obj, true)
609
610
611
612
613
614
615
616
617
618 if err = o.saveLastApplyAnnotationIfNecessary(helper, info); err != nil {
619 fmt.Fprintf(o.ErrOut, warningMigrationLastAppliedFailed, err.Error())
620 } else if performedMigration, err := o.migrateToSSAIfNecessary(helper, info); err != nil {
621
622
623
624
625
626
627 fmt.Fprintf(o.ErrOut, warningMigrationPatchFailed, err.Error())
628 } else if performedMigration {
629 if obj, err = helper.Patch(
630 info.Namespace,
631 info.Name,
632 types.ApplyPatchType,
633 data,
634 &options,
635 ); err != nil {
636
637
638 fmt.Fprintf(o.ErrOut, warningMigrationReapplyFailed, err.Error())
639 } else {
640 info.Refresh(obj, false)
641 }
642 }
643
644 WarnIfDeleting(info.Object, o.ErrOut)
645
646 if err := o.MarkObjectVisited(info); err != nil {
647 return err
648 }
649
650 if o.shouldPrintObject() {
651 return nil
652 }
653
654 printer, err := o.ToPrinter("serverside-applied")
655 if err != nil {
656 return err
657 }
658
659 if err = printer.PrintObj(info.Object, o.Out); err != nil {
660 return err
661 }
662 return nil
663 }
664
665
666
667
668 modified, err := util.GetModifiedConfiguration(info.Object, true, unstructured.UnstructuredJSONScheme)
669 if err != nil {
670 return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%s\nfor:", info.String()), info.Source, err)
671 }
672
673 if err := info.Get(); err != nil {
674 if !errors.IsNotFound(err) {
675 return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
676 }
677
678
679
680 if err := util.CreateApplyAnnotation(info.Object, unstructured.UnstructuredJSONScheme); err != nil {
681 return cmdutil.AddSourceToErr("creating", info.Source, err)
682 }
683
684 if o.DryRunStrategy != cmdutil.DryRunClient {
685
686 obj, err := helper.Create(info.Namespace, true, info.Object)
687 if err != nil {
688 return cmdutil.AddSourceToErr("creating", info.Source, err)
689 }
690 info.Refresh(obj, true)
691 }
692
693 if err := o.MarkObjectVisited(info); err != nil {
694 return err
695 }
696
697 if o.shouldPrintObject() {
698 return nil
699 }
700
701 printer, err := o.ToPrinter("created")
702 if err != nil {
703 return err
704 }
705 if err = printer.PrintObj(info.Object, o.Out); err != nil {
706 return err
707 }
708 return nil
709 }
710
711 if err := o.MarkObjectVisited(info); err != nil {
712 return err
713 }
714
715 if o.DryRunStrategy != cmdutil.DryRunClient {
716 metadata, _ := meta.Accessor(info.Object)
717 annotationMap := metadata.GetAnnotations()
718 if _, ok := annotationMap[corev1.LastAppliedConfigAnnotation]; !ok {
719 fmt.Fprintf(o.ErrOut, warningNoLastAppliedConfigAnnotation, info.ObjectName(), corev1.LastAppliedConfigAnnotation, o.cmdBaseName)
720 }
721
722 patcher, err := newPatcher(o, info, helper)
723 if err != nil {
724 return err
725 }
726 patchBytes, patchedObject, err := patcher.Patch(info.Object, modified, info.Source, info.Namespace, info.Name, o.ErrOut)
727 if err != nil {
728 return cmdutil.AddSourceToErr(fmt.Sprintf("applying patch:\n%s\nto:\n%v\nfor:", patchBytes, info), info.Source, err)
729 }
730
731 info.Refresh(patchedObject, true)
732
733 WarnIfDeleting(info.Object, o.ErrOut)
734
735 if string(patchBytes) == "{}" && !o.shouldPrintObject() {
736 printer, err := o.ToPrinter("unchanged")
737 if err != nil {
738 return err
739 }
740 if err = printer.PrintObj(info.Object, o.Out); err != nil {
741 return err
742 }
743 return nil
744 }
745 }
746
747 if o.shouldPrintObject() {
748 return nil
749 }
750
751 printer, err := o.ToPrinter("configured")
752 if err != nil {
753 return err
754 }
755 if err = printer.PrintObj(info.Object, o.Out); err != nil {
756 return err
757 }
758
759 return nil
760 }
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776 func (o *ApplyOptions) saveLastApplyAnnotationIfNecessary(
777 helper *resource.Helper,
778 info *resource.Info,
779 ) error {
780 if o.FieldManager != fieldManagerServerSideApply {
781
782
783
784 return nil
785 }
786
787
788
789 accessor, err := meta.Accessor(info.Object)
790 if err != nil {
791 return err
792 }
793
794
795 annots := accessor.GetAnnotations()
796 if annots == nil {
797 annots = map[string]string{}
798 }
799
800 fieldManager := fieldManagerLastAppliedAnnotation
801 originalAnnotation, hasAnnotation := annots[corev1.LastAppliedConfigAnnotation]
802
803
804 if !hasAnnotation {
805 return nil
806 }
807
808
809
810 if owners := csaupgrade.FindFieldsOwners(
811 accessor.GetManagedFields(),
812 metav1.ManagedFieldsOperationApply,
813 lastAppliedAnnotationFieldPath,
814 ); len(owners) > 0 {
815 return nil
816 }
817
818 justAnnotation := &unstructured.Unstructured{}
819 justAnnotation.SetGroupVersionKind(info.Mapping.GroupVersionKind)
820 justAnnotation.SetName(accessor.GetName())
821 justAnnotation.SetNamespace(accessor.GetNamespace())
822 justAnnotation.SetAnnotations(map[string]string{
823 corev1.LastAppliedConfigAnnotation: originalAnnotation,
824 })
825
826 modified, err := runtime.Encode(unstructured.UnstructuredJSONScheme, justAnnotation)
827 if err != nil {
828 return nil
829 }
830
831 helperCopy := *helper
832 newObj, err := helperCopy.WithFieldManager(fieldManager).Patch(
833 info.Namespace,
834 info.Name,
835 types.ApplyPatchType,
836 modified,
837 nil,
838 )
839
840 if err != nil {
841 return err
842 }
843
844 return info.Refresh(newObj, false)
845 }
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862 func (o *ApplyOptions) migrateToSSAIfNecessary(
863 helper *resource.Helper,
864 info *resource.Info,
865 ) (migrated bool, err error) {
866 accessor, err := meta.Accessor(info.Object)
867 if err != nil {
868 return false, err
869 }
870
871
872
873
874
875
876
877
878
879
880 csaManagers := csaupgrade.FindFieldsOwners(
881 accessor.GetManagedFields(),
882 metav1.ManagedFieldsOperationUpdate,
883 lastAppliedAnnotationFieldPath)
884
885 managerNames := sets.New[string]()
886 for _, entry := range csaManagers {
887 managerNames.Insert(entry.Manager)
888 }
889
890
891
892 for i := 0; i < maxPatchRetry; i++ {
893 var patchData []byte
894 var obj runtime.Object
895
896 patchData, err = csaupgrade.UpgradeManagedFieldsPatch(
897 info.Object, managerNames, o.FieldManager)
898
899 if err != nil {
900
901 return false, err
902 } else if patchData == nil {
903
904 return false, nil
905 }
906
907
908 obj, err = helper.Patch(
909 info.Namespace,
910 info.Name,
911 types.JSONPatchType,
912 patchData,
913 nil,
914 )
915
916 if err == nil {
917
918 info.Refresh(obj, false)
919 return true, nil
920 } else if !errors.IsConflict(err) {
921
922 return false, err
923 }
924
925
926 err = info.Get()
927 if err != nil {
928
929 return false, err
930 }
931 }
932
933
934
935
936 return false, err
937 }
938
939 func (o *ApplyOptions) shouldPrintObject() bool {
940
941 shouldPrint := false
942 output := *o.PrintFlags.OutputFormat
943 shortOutput := output == "name"
944 if len(output) > 0 && !shortOutput {
945 shouldPrint = true
946 }
947 return shouldPrint
948 }
949
950 func (o *ApplyOptions) printObjects() error {
951
952 if !o.shouldPrintObject() {
953 return nil
954 }
955
956 infos, err := o.GetObjects()
957 if err != nil {
958 return err
959 }
960
961 if len(infos) > 0 {
962 printer, err := o.ToPrinter("")
963 if err != nil {
964 return err
965 }
966
967 objToPrint := infos[0].Object
968 if len(infos) > 1 {
969 objs := []runtime.Object{}
970 for _, info := range infos {
971 objs = append(objs, info.Object)
972 }
973 list := &corev1.List{
974 TypeMeta: metav1.TypeMeta{
975 Kind: "List",
976 APIVersion: "v1",
977 },
978 ListMeta: metav1.ListMeta{},
979 }
980 if err := meta.SetList(list, objs); err != nil {
981 return err
982 }
983
984 objToPrint = list
985 }
986 if err := printer.PrintObj(objToPrint, o.Out); err != nil {
987 return err
988 }
989 }
990
991 return nil
992 }
993
994
995
996 func (o *ApplyOptions) MarkNamespaceVisited(info *resource.Info) {
997 if info.Namespaced() {
998 o.VisitedNamespaces.Insert(info.Namespace)
999 }
1000 }
1001
1002
1003
1004 func (o *ApplyOptions) MarkObjectVisited(info *resource.Info) error {
1005 metadata, err := meta.Accessor(info.Object)
1006 if err != nil {
1007 return err
1008 }
1009 o.VisitedUids.Insert(metadata.GetUID())
1010
1011 return nil
1012 }
1013
1014
1015
1016
1017
1018
1019 func (o *ApplyOptions) PrintAndPrunePostProcessor() func() error {
1020
1021 return func() error {
1022 ctx := context.TODO()
1023 if err := o.printObjects(); err != nil {
1024 return err
1025 }
1026
1027 if o.Prune {
1028 if cmdutil.ApplySet.IsEnabled() && o.ApplySet != nil {
1029 if err := o.ApplySet.Prune(ctx, o); err != nil {
1030
1031
1032
1033 return err
1034 }
1035 } else {
1036 p := newPruner(o)
1037 return p.pruneAll(o)
1038 }
1039 }
1040
1041 return nil
1042 }
1043 }
1044
1045 const (
1046
1047
1048
1049
1050 FieldManagerClientSideApply = "kubectl-client-side-apply"
1051
1052
1053
1054
1055 fieldManagerServerSideApply = "kubectl"
1056
1057 fieldManagerLastAppliedAnnotation = "kubectl-last-applied"
1058 )
1059
1060 var (
1061 lastAppliedAnnotationFieldPath = fieldpath.NewSet(
1062 fieldpath.MakePathOrDie(
1063 "metadata", "annotations",
1064 corev1.LastAppliedConfigAnnotation),
1065 )
1066 )
1067
1068
1069
1070
1071
1072
1073 func GetApplyFieldManagerFlag(cmd *cobra.Command, serverSide bool) string {
1074
1075 if cmd.Flag("field-manager").Changed {
1076 return cmdutil.GetFlagString(cmd, "field-manager")
1077 }
1078
1079 if serverSide {
1080 return fieldManagerServerSideApply
1081 }
1082
1083 return FieldManagerClientSideApply
1084 }
1085
1086
1087 func WarnIfDeleting(obj runtime.Object, stderr io.Writer) {
1088 metadata, _ := meta.Accessor(obj)
1089 if metadata != nil && metadata.GetDeletionTimestamp() != nil {
1090
1091 fmt.Fprintf(stderr, warningChangesOnDeletingResource, metadata.GetName())
1092 }
1093 }
1094
View as plain text