...

Source file src/k8s.io/kubectl/pkg/cmd/apply/apply.go

Documentation: k8s.io/kubectl/pkg/cmd/apply

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    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  // ApplyFlags directly reflect the information that CLI is gathering via flags.  They will be converted to Options, which
    58  // reflect the runtime requirements for the command.  This structure reduces the transformation to wiring and makes
    59  // the logic itself easy to unit test
    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  // ApplyOptions defines flags and other configuration parameters for the `apply` command
    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  	// Objects (and some denormalized data) which are to be
   115  	// applied. The standard way to fill in this structure
   116  	// is by calling "GetObjects()", which will use the
   117  	// resource builder if "objectsCached" is false. The other
   118  	// way to set this field is to use "SetObjects()".
   119  	// Subsequent calls to "GetObjects()" after setting would
   120  	// not call the resource builder; only return the set objects.
   121  	objects       []*resource.Info
   122  	objectsCached bool
   123  
   124  	// Stores visited objects/namespaces for later use
   125  	// calculating the set of objects to prune.
   126  	VisitedUids       sets.Set[types.UID]
   127  	VisitedNamespaces sets.Set[string]
   128  
   129  	// Function run after the objects are generated and
   130  	// stored in the "objects" field, but before the
   131  	// apply is run on these objects.
   132  	PreProcessorFn func() error
   133  	// Function run after all objects have been applied.
   134  	// The standard PostProcessorFn is "PrintAndPrunePostProcessor()".
   135  	PostProcessorFn func() error
   136  
   137  	// ApplySet tracks the set of objects that have been applied, for the purposes of pruning.
   138  	// See git.k8s.io/enhancements/keps/sig-cli/3659-kubectl-apply-prune
   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  // NewApplyFlags returns a default ApplyFlags
   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  // NewCmdApply creates the `apply` command
   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  	// apply subcommands
   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  // AddFlags registers flags for a cli
   224  func (flags *ApplyFlags) AddFlags(cmd *cobra.Command) {
   225  	// bind flag structs
   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  // ToOptions converts from CLI inputs to runtime inputs
   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  	// allow for a success message operation to be specified at print time
   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  		// ApplySet uses the namespace value from the flag, but not from the kubeconfig or defaults
   319  		// This means the namespace flag is required when using a namespaced parent.
   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  		// 	Store baseName for use in printing warnings / messages involving the base command name.
   342  		// 	This is useful for downstream command that wrap this one.
   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  // Validate verifies if ApplyOptions are valid and without conflicts.
   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  		// Do not force the recreation of an object(s) if we're pruning; this can cause
   419  		// undefined behavior since object UID's change.
   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  	// 415: Unsupported media type means we're talking to a server which doesn't
   447  	// support server-side apply.
   448  	if _, ok := err.(*errors.StatusError); !ok {
   449  		// Non-StatusError means the error isn't because the server is incompatible.
   450  		return false
   451  	}
   452  	return err.(*errors.StatusError).Status().Code == http.StatusUnsupportedMediaType
   453  }
   454  
   455  // GetObjects returns a (possibly cached) version of all the valid objects to apply
   456  // as a slice of pointer to resource.Info and an error if one or more occurred.
   457  // IMPORTANT: This function can return both valid objects AND an error, since
   458  // "ContinueOnError" is set on the builder. This function should not be called
   459  // until AFTER the "complete" and "validate" methods have been called to ensure that
   460  // the ApplyOptions is filled in and valid.
   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  // SetObjects stores the set of objects (as resource.Info) to be
   488  // subsequently applied.
   489  func (o *ApplyOptions) SetObjects(infos []*resource.Info) {
   490  	o.objects = infos
   491  	o.objectsCached = true
   492  }
   493  
   494  // Run executes the `apply` command.
   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  	// Enforce CLI specified namespace on server request.
   504  	if o.EnforceNamespace {
   505  		o.VisitedNamespaces.Insert(o.Namespace)
   506  	}
   507  
   508  	// Generates the objects using the resource builder if they have not
   509  	// already been stored by calling "SetObjects()" in the pre-processor.
   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  	// Iterate through all objects, applying each one.
   526  	for _, info := range infos {
   527  		if err := o.applyOneObject(info); err != nil {
   528  			errs = append(errs, err)
   529  		}
   530  	}
   531  	// If any errors occurred during apply, then return error (or
   532  	// aggregate of errors).
   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  		// Send the full object to be applied on the server side.
   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  		// Migrate managed fields if necessary.
   611  		//
   612  		// By checking afterward instead of fetching the object beforehand and
   613  		// unconditionally fetching we can make 3 network requests in the rare
   614  		// case of migration and 1 request if migration is unnecessary.
   615  		//
   616  		// To check beforehand means 2 requests for most operations, and 3
   617  		// requests in worst case.
   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  			// Print-error as a warning.
   622  			// This is a non-fatal error because object was successfully applied
   623  			// above, but it might have issues since migration failed.
   624  			//
   625  			// This migration will be re-attempted if necessary upon next
   626  			// apply.
   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  				// Re-send original SSA patch (this will allow dropped fields to
   637  				// finally be removed)
   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  	// Get the modified configuration of the object. Embed the result
   666  	// as an annotation in the modified configuration, so that it will appear
   667  	// in the patch sent to the server.
   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  		// Create the resource if it doesn't exist
   679  		// First, update the annotation used by kubectl apply
   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  			// Then create the resource and skip the three-way merge
   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  // Saves the last-applied-configuration annotation in a separate SSA field manager
   763  // to prevent it from being dropped by users who have transitioned to SSA.
   764  //
   765  // If this operation is not performed, then the last-applied-configuration annotation
   766  // would be removed from the object upon the first SSA usage. We want to keep it
   767  // around for a few releases since it is required to downgrade to
   768  // SSA per [1] and [2]. This code should be removed once the annotation is
   769  // deprecated.
   770  //
   771  // - [1] https://kubernetes.io/docs/reference/using-api/server-side-apply/#downgrading-from-server-side-apply-to-client-side-apply
   772  // - [2] https://github.com/kubernetes/kubernetes/pull/90187
   773  //
   774  // If the annotation is not already present, or if it is already managed by the
   775  // separate SSA fieldmanager, this is a no-op.
   776  func (o *ApplyOptions) saveLastApplyAnnotationIfNecessary(
   777  	helper *resource.Helper,
   778  	info *resource.Info,
   779  ) error {
   780  	if o.FieldManager != fieldManagerServerSideApply {
   781  		// There is no point in preserving the annotation if the field manager
   782  		// will not remain default. This is because the server will not keep
   783  		// the annotation up to date.
   784  		return nil
   785  	}
   786  
   787  	// Send an apply patch with the last-applied-annotation
   788  	// so that it is not orphaned by SSA in the following patch:
   789  	accessor, err := meta.Accessor(info.Object)
   790  	if err != nil {
   791  		return err
   792  	}
   793  
   794  	// Get the current annotations from the object.
   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  	// If the annotation does not already exist, we do not do anything
   804  	if !hasAnnotation {
   805  		return nil
   806  	}
   807  
   808  	// If there is already an SSA field manager which owns the field, then there
   809  	// is nothing to do here.
   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  // Check if the returned object needs to have its kubectl-client-side-apply
   848  // managed fields migrated server-side-apply.
   849  //
   850  // field ownership metadata is stored in three places:
   851  //   - server-side managed fields
   852  //   - client-side managed fields
   853  //   - and the last_applied_configuration annotation.
   854  //
   855  // The migration merges the client-side-managed fields into the
   856  // server-side-managed fields, leaving the last_applied_configuration
   857  // annotation in place. Server will keep the annotation up to date
   858  // after every server-side-apply where the following conditions are ment:
   859  //
   860  //  1. field manager is 'kubectl'
   861  //  2. annotation already exists
   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  	// To determine which field managers were used by kubectl for client-side-apply
   872  	// we search for a manager used in `Update` operations which owns the
   873  	// last-applied-annotation.
   874  	//
   875  	// This is the last client-side-apply manager which changed the field.
   876  	//
   877  	// There may be multiple owners if multiple managers wrote the same exact
   878  	// configuration. In this case there are multiple owners, we want to migrate
   879  	// them all.
   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  	// Re-attempt patch as many times as it is conflicting due to ResourceVersion
   891  	// test failing
   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  			// If patch generation failed there was likely a bug.
   901  			return false, err
   902  		} else if patchData == nil {
   903  			// nil patch data means nothing to do - object is already migrated
   904  			return false, nil
   905  		}
   906  
   907  		// Send the patch to upgrade the managed fields if it is non-nil
   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  			// Stop retrying upon success.
   918  			info.Refresh(obj, false)
   919  			return true, nil
   920  		} else if !errors.IsConflict(err) {
   921  			// Only retry if there was a conflict
   922  			return false, err
   923  		}
   924  
   925  		// Refresh the object for next iteration
   926  		err = info.Get()
   927  		if err != nil {
   928  			// If there was an error fetching, return error
   929  			return false, err
   930  		}
   931  	}
   932  
   933  	// Reaching this point with non-nil error means there was a conflict and
   934  	// max retries was hit
   935  	// Return the last error witnessed (which will be a conflict)
   936  	return false, err
   937  }
   938  
   939  func (o *ApplyOptions) shouldPrintObject() bool {
   940  	// Print object only if output format other than "name" is specified
   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  // MarkNamespaceVisited keeps track of which namespaces the applied
   995  // objects belong to. Used for pruning.
   996  func (o *ApplyOptions) MarkNamespaceVisited(info *resource.Info) {
   997  	if info.Namespaced() {
   998  		o.VisitedNamespaces.Insert(info.Namespace)
   999  	}
  1000  }
  1001  
  1002  // MarkObjectVisited keeps track of UIDs of the applied
  1003  // objects. Used for pruning.
  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  // PrintAndPrunePostProcessor returns a function which meets the PostProcessorFn
  1015  // function signature. This returned function prints all the
  1016  // objects as a list (if configured for that), and prunes the
  1017  // objects not applied. The returned function is the standard
  1018  // apply post processor.
  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  					// Do not update the ApplySet. If pruning failed, we want to keep the superset
  1031  					// of the previous and current resources in the ApplySet, so that the pruning
  1032  					// step of the next apply will be able to clean up the set correctly.
  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  	// FieldManagerClientSideApply is the default client-side apply field manager.
  1047  	//
  1048  	// The default field manager is not `kubectl-apply` to distinguish from
  1049  	// server-side apply.
  1050  	FieldManagerClientSideApply = "kubectl-client-side-apply"
  1051  	// The default server-side apply field manager is `kubectl`
  1052  	// instead of a field manager like `kubectl-server-side-apply`
  1053  	// for backward compatibility to not conflict with old versions
  1054  	// of kubectl server-side apply where `kubectl` has already been the field manager.
  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  // GetApplyFieldManagerFlag gets the field manager for kubectl apply
  1069  // if it is not set.
  1070  //
  1071  // The default field manager is not `kubectl-apply` to distinguish between
  1072  // client-side and server-side apply.
  1073  func GetApplyFieldManagerFlag(cmd *cobra.Command, serverSide bool) string {
  1074  	// The field manager flag was set
  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  // WarnIfDeleting prints a warning if a resource is being deleted
  1087  func WarnIfDeleting(obj runtime.Object, stderr io.Writer) {
  1088  	metadata, _ := meta.Accessor(obj)
  1089  	if metadata != nil && metadata.GetDeletionTimestamp() != nil {
  1090  		// just warn the user about the conflict
  1091  		fmt.Fprintf(stderr, warningChangesOnDeletingResource, metadata.GetName())
  1092  	}
  1093  }
  1094  

View as plain text