2019-01-12 04:58:27 +00:00
/ *
Copyright 2014 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package apply
import (
"fmt"
2020-12-01 01:06:26 +00:00
"io"
2019-04-07 17:07:55 +00:00
"net/http"
2019-01-12 04:58:27 +00:00
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
2020-04-19 06:53:00 +00:00
utilerrors "k8s.io/apimachinery/pkg/util/errors"
2019-01-12 04:58:27 +00:00
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/cli-runtime/pkg/genericclioptions"
2019-04-07 17:07:55 +00:00
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/cli-runtime/pkg/resource"
2019-01-12 04:58:27 +00:00
"k8s.io/client-go/dynamic"
2020-08-10 17:43:49 +00:00
"k8s.io/klog/v2"
2019-09-27 21:51:53 +00:00
"k8s.io/kubectl/pkg/cmd/delete"
cmdutil "k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/scheme"
"k8s.io/kubectl/pkg/util"
"k8s.io/kubectl/pkg/util/i18n"
"k8s.io/kubectl/pkg/util/openapi"
"k8s.io/kubectl/pkg/util/templates"
"k8s.io/kubectl/pkg/validation"
2019-01-12 04:58:27 +00:00
)
2019-08-30 18:33:25 +00:00
// ApplyOptions defines flags and other configuration parameters for the `apply` command
2019-01-12 04:58:27 +00:00
type ApplyOptions struct {
RecordFlags * genericclioptions . RecordFlags
Recorder genericclioptions . Recorder
PrintFlags * genericclioptions . PrintFlags
ToPrinter func ( string ) ( printers . ResourcePrinter , error )
DeleteFlags * delete . DeleteFlags
DeleteOptions * delete . DeleteOptions
2019-08-30 18:33:25 +00:00
ServerSideApply bool
ForceConflicts bool
FieldManager string
Selector string
2020-03-26 21:07:15 +00:00
DryRunStrategy cmdutil . DryRunStrategy
DryRunVerifier * resource . DryRunVerifier
2019-08-30 18:33:25 +00:00
Prune bool
PruneResources [ ] pruneResource
cmdBaseName string
All bool
Overwrite bool
OpenAPIPatch bool
PruneWhitelist [ ] string
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
Validator validation . Schema
Builder * resource . Builder
Mapper meta . RESTMapper
DynamicClient dynamic . Interface
OpenAPISchema openapi . Resources
2019-01-12 04:58:27 +00:00
Namespace string
EnforceNamespace bool
genericclioptions . IOStreams
2020-03-26 21:07:15 +00:00
// Objects (and some denormalized data) which are to be
// applied. The standard way to fill in this structure
// is by calling "GetObjects()", which will use the
// resource builder if "objectsCached" is false. The other
// way to set this field is to use "SetObjects()".
// Subsequent calls to "GetObjects()" after setting would
// not call the resource builder; only return the set objects.
objects [ ] * resource . Info
objectsCached bool
// Stores visited objects/namespaces for later use
// calculating the set of objects to prune.
VisitedUids sets . String
VisitedNamespaces sets . String
// Function run after the objects are generated and
// stored in the "objects" field, but before the
// apply is run on these objects.
PreProcessorFn func ( ) error
// Function run after all objects have been applied.
// The standard PostProcessorFn is "PrintAndPrunePostProcessor()".
PostProcessorFn func ( ) error
}
2019-01-12 04:58:27 +00:00
var (
applyLong = templates . LongDesc ( i18n . T ( `
2021-07-02 08:43:15 +00:00
Apply a configuration to a resource by file name or stdin .
2019-01-12 04:58:27 +00:00
The resource name must be specified . This resource will be created if it doesn ' t exist yet .
To use ' apply ' , always create the resource initially with either ' apply ' or ' create -- save - config ' .
JSON and YAML formats are accepted .
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.`))
applyExample = templates . Examples ( i18n . T ( `
2021-07-02 08:43:15 +00:00
# Apply the configuration in pod . json to a pod
2019-01-12 04:58:27 +00:00
kubectl apply - f . / pod . json
2021-07-02 08:43:15 +00:00
# Apply resources from a directory containing kustomization . yaml - e . g . dir / kustomization . yaml
2019-04-07 17:07:55 +00:00
kubectl apply - k dir /
2021-07-02 08:43:15 +00:00
# Apply the JSON passed into stdin to a pod
2019-01-12 04:58:27 +00:00
cat pod . json | kubectl apply - f -
# Note : -- prune is still in Alpha
2021-07-02 08:43:15 +00:00
# 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
2019-01-12 04:58:27 +00:00
kubectl apply -- prune - f manifest . yaml - l app = nginx
2021-07-02 08:43:15 +00:00
# Apply the configuration in manifest . yaml and delete all the other config maps that are not in the file
2019-01-12 04:58:27 +00:00
kubectl apply -- prune - f manifest . yaml -- all -- prune - whitelist = core / v1 / ConfigMap ` ) )
2020-12-01 01:06:26 +00:00
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"
warningChangesOnDeletingResource = "Warning: Detected changes to resource %[1]s which is currently being deleted.\n"
2019-01-12 04:58:27 +00:00
)
2019-08-30 18:33:25 +00:00
// NewApplyOptions creates new ApplyOptions for the `apply` command
2019-01-12 04:58:27 +00:00
func NewApplyOptions ( ioStreams genericclioptions . IOStreams ) * ApplyOptions {
return & ApplyOptions {
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
DeleteFlags : delete . NewDeleteFlags ( "that contains the configuration to apply" ) ,
PrintFlags : genericclioptions . NewPrintFlags ( "created" ) . WithTypeSetter ( scheme . Scheme ) ,
2019-08-30 18:33:25 +00:00
Overwrite : true ,
OpenAPIPatch : true ,
2019-01-12 04:58:27 +00:00
Recorder : genericclioptions . NoopRecorder { } ,
IOStreams : ioStreams ,
2020-03-26 21:07:15 +00:00
objects : [ ] * resource . Info { } ,
objectsCached : false ,
VisitedUids : sets . NewString ( ) ,
VisitedNamespaces : sets . NewString ( ) ,
2019-01-12 04:58:27 +00:00
}
}
2019-04-07 17:07:55 +00:00
// NewCmdApply creates the `apply` command
2019-01-12 04:58:27 +00:00
func NewCmdApply ( baseName string , f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
o := NewApplyOptions ( ioStreams )
// Store baseName for use in printing warnings / messages involving the base command name.
// This is useful for downstream command that wrap this one.
o . cmdBaseName = baseName
cmd := & cobra . Command {
2019-04-07 17:07:55 +00:00
Use : "apply (-f FILENAME | -k DIRECTORY)" ,
2019-01-12 04:58:27 +00:00
DisableFlagsInUseLine : true ,
2021-07-02 08:43:15 +00:00
Short : i18n . T ( "Apply a configuration to a resource by file name or stdin" ) ,
2019-01-12 04:58:27 +00:00
Long : applyLong ,
Example : applyExample ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
cmdutil . CheckErr ( o . Complete ( f , cmd ) )
cmdutil . CheckErr ( validateArgs ( cmd , args ) )
cmdutil . CheckErr ( validatePruneAll ( o . Prune , o . All , o . Selector ) )
cmdutil . CheckErr ( o . Run ( ) )
} ,
}
// bind flag structs
o . DeleteFlags . AddFlags ( cmd )
o . RecordFlags . AddFlags ( cmd )
o . PrintFlags . AddFlags ( cmd )
cmd . Flags ( ) . BoolVar ( & o . Overwrite , "overwrite" , o . Overwrite , "Automatically resolve conflicts between the modified and live configuration by using values from the modified configuration" )
cmd . Flags ( ) . BoolVar ( & o . Prune , "prune" , o . Prune , "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." )
cmdutil . AddValidateFlags ( cmd )
cmd . Flags ( ) . StringVarP ( & o . Selector , "selector" , "l" , o . Selector , "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)" )
cmd . Flags ( ) . BoolVar ( & o . All , "all" , o . All , "Select all resources in the namespace of the specified resource types." )
cmd . Flags ( ) . StringArrayVar ( & o . PruneWhitelist , "prune-whitelist" , o . PruneWhitelist , "Overwrite the default whitelist with <group/version/kind> for --prune" )
2019-08-30 18:33:25 +00:00
cmd . Flags ( ) . BoolVar ( & o . OpenAPIPatch , "openapi-patch" , o . 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." )
2020-03-26 21:07:15 +00:00
cmdutil . AddDryRunFlag ( cmd )
2019-08-30 18:33:25 +00:00
cmdutil . AddServerSideApplyFlags ( cmd )
2020-08-10 17:43:49 +00:00
cmdutil . AddFieldManagerFlagVar ( cmd , & o . FieldManager , FieldManagerClientSideApply )
2019-01-12 04:58:27 +00:00
// apply subcommands
cmd . AddCommand ( NewCmdApplyViewLastApplied ( f , ioStreams ) )
cmd . AddCommand ( NewCmdApplySetLastApplied ( f , ioStreams ) )
cmd . AddCommand ( NewCmdApplyEditLastApplied ( f , ioStreams ) )
return cmd
}
2019-08-30 18:33:25 +00:00
// Complete verifies if ApplyOptions are valid and without conflicts.
2019-01-12 04:58:27 +00:00
func ( o * ApplyOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command ) error {
2020-03-26 21:07:15 +00:00
var err error
2019-08-30 18:33:25 +00:00
o . ServerSideApply = cmdutil . GetServerSideApplyFlag ( cmd )
o . ForceConflicts = cmdutil . GetForceConflictsFlag ( cmd )
2020-03-26 21:07:15 +00:00
o . DryRunStrategy , err = cmdutil . GetDryRunStrategy ( cmd )
if err != nil {
return err
}
o . DynamicClient , err = f . DynamicClient ( )
if err != nil {
return err
}
2021-03-18 22:40:29 +00:00
o . DryRunVerifier = resource . NewDryRunVerifier ( o . DynamicClient , f . OpenAPIGetter ( ) )
2020-08-10 17:43:49 +00:00
o . FieldManager = GetApplyFieldManagerFlag ( cmd , o . ServerSideApply )
2019-01-12 04:58:27 +00:00
2019-08-30 18:33:25 +00:00
if o . ForceConflicts && ! o . ServerSideApply {
2019-09-27 21:51:53 +00:00
return fmt . Errorf ( "--force-conflicts only works with --server-side" )
2019-08-30 18:33:25 +00:00
}
2020-03-26 21:07:15 +00:00
if o . DryRunStrategy == cmdutil . DryRunClient && o . ServerSideApply {
return fmt . Errorf ( "--dry-run=client doesn't work with --server-side (did you mean --dry-run=server instead?)" )
2019-08-30 18:33:25 +00:00
}
2019-01-12 04:58:27 +00:00
// allow for a success message operation to be specified at print time
o . ToPrinter = func ( operation string ) ( printers . ResourcePrinter , error ) {
o . PrintFlags . NamePrintFlags . Operation = operation
2020-03-26 21:07:15 +00:00
cmdutil . PrintFlagsWithDryRunStrategy ( o . PrintFlags , o . DryRunStrategy )
2019-01-12 04:58:27 +00:00
return o . PrintFlags . ToPrinter ( )
}
o . RecordFlags . Complete ( cmd )
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
2020-12-01 01:06:26 +00:00
o . DeleteOptions , err = o . DeleteFlags . ToOptions ( o . DynamicClient , o . IOStreams )
if err != nil {
return err
}
2019-04-07 17:07:55 +00:00
err = o . DeleteOptions . FilenameOptions . RequireFilenameOrKustomize ( )
if err != nil {
return err
}
2019-01-12 04:58:27 +00:00
2020-08-10 17:43:49 +00:00
if o . ServerSideApply && o . DeleteOptions . ForceDeletion {
return fmt . Errorf ( "--force cannot be used with --server-side" )
}
if o . DryRunStrategy == cmdutil . DryRunServer && o . DeleteOptions . ForceDeletion {
return fmt . Errorf ( "--dry-run=server cannot be used with --force" )
}
2019-08-30 18:33:25 +00:00
o . OpenAPISchema , _ = f . OpenAPISchema ( )
2019-01-12 04:58:27 +00:00
o . Validator , err = f . Validator ( cmdutil . GetFlagBool ( cmd , "validate" ) )
2019-12-12 01:27:03 +00:00
if err != nil {
return err
}
2019-01-12 04:58:27 +00:00
o . Builder = f . NewBuilder ( )
o . Mapper , err = f . ToRESTMapper ( )
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
o . Namespace , o . EnforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
if o . Prune {
o . PruneResources , err = parsePruneResources ( o . Mapper , o . PruneWhitelist )
if err != nil {
return err
}
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
o . PostProcessorFn = o . PrintAndPrunePostProcessor ( )
2019-01-12 04:58:27 +00:00
return nil
}
func validateArgs ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) != 0 {
return cmdutil . UsageErrorf ( cmd , "Unexpected args: %v" , args )
}
return nil
}
func validatePruneAll ( prune , all bool , selector string ) error {
if all && len ( selector ) > 0 {
return fmt . Errorf ( "cannot set --all and --selector at the same time" )
}
if prune && ! all && selector == "" {
2019-04-07 17:07:55 +00:00
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" )
2019-01-12 04:58:27 +00:00
}
return nil
}
2019-04-07 17:07:55 +00:00
func isIncompatibleServerError ( err error ) bool {
// 415: Unsupported media type means we're talking to a server which doesn't
// support server-side apply.
if _ , ok := err . ( * errors . StatusError ) ; ! ok {
// Non-StatusError means the error isn't because the server is incompatible.
return false
}
return err . ( * errors . StatusError ) . Status ( ) . Code == http . StatusUnsupportedMediaType
}
2020-04-19 06:53:00 +00:00
// GetObjects returns a (possibly cached) version of all the valid objects to apply
// as a slice of pointer to resource.Info and an error if one or more occurred.
// IMPORTANT: This function can return both valid objects AND an error, since
// "ContinueOnError" is set on the builder. This function should not be called
// until AFTER the "complete" and "validate" methods have been called to ensure that
// the ApplyOptions is filled in and valid.
2020-03-26 21:07:15 +00:00
func ( o * ApplyOptions ) GetObjects ( ) ( [ ] * resource . Info , error ) {
2020-04-19 06:53:00 +00:00
var err error = nil
2020-03-26 21:07:15 +00:00
if ! o . objectsCached {
// include the uninitialized objects by default if --prune is true
// unless explicitly set --include-uninitialized=false
r := o . Builder .
Unstructured ( ) .
Schema ( o . Validator ) .
ContinueOnError ( ) .
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
FilenameParam ( o . EnforceNamespace , & o . DeleteOptions . FilenameOptions ) .
LabelSelectorParam ( o . Selector ) .
Flatten ( ) .
Do ( )
2020-04-19 06:53:00 +00:00
o . objects , err = r . Infos ( )
2020-03-26 21:07:15 +00:00
o . objectsCached = true
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
return o . objects , err
2020-03-26 21:07:15 +00:00
}
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
// SetObjects stores the set of objects (as resource.Info) to be
// subsequently applied.
func ( o * ApplyOptions ) SetObjects ( infos [ ] * resource . Info ) {
o . objects = infos
o . objectsCached = true
}
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
// Run executes the `apply` command.
func ( o * ApplyOptions ) Run ( ) error {
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
if o . PreProcessorFn != nil {
klog . V ( 4 ) . Infof ( "Running apply pre-processor function" )
if err := o . PreProcessorFn ( ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
2020-03-26 21:07:15 +00:00
}
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
// Enforce CLI specified namespace on server request.
if o . EnforceNamespace {
o . VisitedNamespaces . Insert ( o . Namespace )
}
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
// Generates the objects using the resource builder if they have not
// already been stored by calling "SetObjects()" in the pre-processor.
2020-04-19 06:53:00 +00:00
errs := [ ] error { }
2020-03-26 21:07:15 +00:00
infos , err := o . GetObjects ( )
if err != nil {
2020-04-19 06:53:00 +00:00
errs = append ( errs , err )
2020-03-26 21:07:15 +00:00
}
2020-04-19 06:53:00 +00:00
if len ( infos ) == 0 && len ( errs ) == 0 {
2020-03-26 21:07:15 +00:00
return fmt . Errorf ( "no objects passed to apply" )
}
2020-04-19 06:53:00 +00:00
// Iterate through all objects, applying each one.
2020-03-26 21:07:15 +00:00
for _ , info := range infos {
2020-04-19 06:53:00 +00:00
if err := o . applyOneObject ( info ) ; err != nil {
errs = append ( errs , err )
}
}
// If any errors occurred during apply, then return error (or
// aggregate of errors).
if len ( errs ) == 1 {
return errs [ 0 ]
}
if len ( errs ) > 1 {
return utilerrors . NewAggregate ( errs )
}
2020-03-26 21:07:15 +00:00
2020-04-19 06:53:00 +00:00
if o . PostProcessorFn != nil {
klog . V ( 4 ) . Infof ( "Running apply post-processor function" )
if err := o . PostProcessorFn ( ) ; err != nil {
return err
}
}
return nil
}
2019-01-12 04:58:27 +00:00
2020-04-19 06:53:00 +00:00
func ( o * ApplyOptions ) applyOneObject ( info * resource . Info ) error {
o . MarkNamespaceVisited ( info )
if err := o . Recorder . Record ( info . Object ) ; err != nil {
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
}
2020-08-10 17:43:49 +00:00
if len ( info . Name ) == 0 {
metadata , _ := meta . Accessor ( info . Object )
generatedName := metadata . GetGenerateName ( )
if len ( generatedName ) > 0 {
return fmt . Errorf ( "from %s: cannot use generate name with apply" , generatedName )
}
}
helper := resource . NewHelper ( info . Client , info . Mapping ) .
DryRun ( o . DryRunStrategy == cmdutil . DryRunServer ) .
WithFieldManager ( o . FieldManager )
if o . DryRunStrategy == cmdutil . DryRunServer {
// Ensure the APIServer supports server-side dry-run for the resource,
// otherwise fail early.
// For APIServers that don't support server-side dry-run will persist
// changes.
if err := o . DryRunVerifier . HasSupport ( info . Mapping . GroupVersionKind ) ; err != nil {
return err
}
}
2020-04-19 06:53:00 +00:00
if o . ServerSideApply {
// Send the full object to be applied on the server side.
data , err := runtime . Encode ( unstructured . UnstructuredJSONScheme , info . Object )
if err != nil {
return cmdutil . AddSourceToErr ( "serverside-apply" , info . Source , err )
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
options := metav1 . PatchOptions {
2020-08-10 17:43:49 +00:00
Force : & o . ForceConflicts ,
2020-04-19 06:53:00 +00:00
}
obj , err := helper . 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 )
2020-03-26 21:07:15 +00:00
}
2020-04-19 06:53:00 +00:00
if errors . IsConflict ( err ) {
err = fmt . Errorf ( ` % v
2019-09-27 21:51:53 +00:00
Please review the fields above -- they currently have other managers . Here
are the ways you can resolve this warning :
* If you intend to manage all of these fields , please re - run the apply
command with the ` +" ` -- force - conflicts ` "+ ` flag .
* If you do not intend to manage all of the fields , please edit your
manifest to remove references to the fields that should keep their
current managers .
* You may co - own fields by updating your manifest to match the existing
value ; in this case , you ' ll become the manager if the other manager ( s )
stop managing the field ( remove it from their configuration ) .
2021-07-02 08:43:15 +00:00
See https : //kubernetes.io/docs/reference/using-api/server-side-apply/#conflicts`, err)
2019-09-27 21:51:53 +00:00
}
2020-04-19 06:53:00 +00:00
return err
}
2019-09-27 21:51:53 +00:00
2020-04-19 06:53:00 +00:00
info . Refresh ( obj , true )
2019-09-27 21:51:53 +00:00
2020-12-01 01:06:26 +00:00
WarnIfDeleting ( info . Object , o . ErrOut )
2020-04-19 06:53:00 +00:00
if err := o . MarkObjectVisited ( info ) ; err != nil {
return err
}
2019-09-27 21:51:53 +00:00
2020-04-19 06:53:00 +00:00
if o . shouldPrintObject ( ) {
return nil
2019-08-30 18:33:25 +00:00
}
2020-04-19 06:53:00 +00:00
printer , err := o . ToPrinter ( "serverside-applied" )
2019-01-12 04:58:27 +00:00
if err != nil {
2020-04-19 06:53:00 +00:00
return err
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
if err = printer . PrintObj ( info . Object , o . Out ) ; err != nil {
return err
}
return nil
}
2019-01-12 04:58:27 +00:00
2020-04-19 06:53:00 +00:00
// Get the modified configuration of the object. Embed the result
// as an annotation in the modified configuration, so that it will appear
// in the patch sent to the server.
modified , err := util . GetModifiedConfiguration ( info . Object , true , unstructured . UnstructuredJSONScheme )
if err != nil {
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "retrieving modified configuration from:\n%s\nfor:" , info . String ( ) ) , info . Source , err )
}
2019-01-12 04:58:27 +00:00
2020-04-19 06:53:00 +00:00
if err := info . Get ( ) ; err != nil {
if ! errors . IsNotFound ( err ) {
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "retrieving current configuration of:\n%s\nfrom server for:" , info . String ( ) ) , info . Source , err )
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
// Create the resource if it doesn't exist
// First, update the annotation used by kubectl apply
if err := util . CreateApplyAnnotation ( info . Object , unstructured . UnstructuredJSONScheme ) ; err != nil {
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
if o . DryRunStrategy != cmdutil . DryRunClient {
2020-04-19 06:53:00 +00:00
// Then create the resource and skip the three-way merge
obj , err := helper . Create ( info . Namespace , true , info . Object )
2019-01-12 04:58:27 +00:00
if err != nil {
2020-04-19 06:53:00 +00:00
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
info . Refresh ( obj , true )
}
2019-01-12 04:58:27 +00:00
2020-04-19 06:53:00 +00:00
if err := o . MarkObjectVisited ( info ) ; err != nil {
return err
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
if o . shouldPrintObject ( ) {
2020-04-19 06:53:00 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
printer , err := o . ToPrinter ( "created" )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
if err = printer . PrintObj ( info . Object , o . Out ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
2020-04-19 06:53:00 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
if err := o . MarkObjectVisited ( info ) ; err != nil {
return err
}
if o . DryRunStrategy != cmdutil . DryRunClient {
metadata , _ := meta . Accessor ( info . Object )
annotationMap := metadata . GetAnnotations ( )
if _ , ok := annotationMap [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2020-12-01 01:06:26 +00:00
fmt . Fprintf ( o . ErrOut , warningNoLastAppliedConfigAnnotation , info . ObjectName ( ) , corev1 . LastAppliedConfigAnnotation , o . cmdBaseName )
2020-04-19 06:53:00 +00:00
}
2020-08-10 17:43:49 +00:00
patcher , err := newPatcher ( o , info , helper )
2020-04-19 06:53:00 +00:00
if err != nil {
2020-03-26 21:07:15 +00:00
return err
2019-01-12 04:58:27 +00:00
}
2020-04-19 06:53:00 +00:00
patchBytes , patchedObject , err := patcher . Patch ( info . Object , modified , info . Source , info . Namespace , info . Name , o . ErrOut )
if err != nil {
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "applying patch:\n%s\nto:\n%v\nfor:" , patchBytes , info ) , info . Source , err )
}
info . Refresh ( patchedObject , true )
2020-12-01 01:06:26 +00:00
WarnIfDeleting ( info . Object , o . ErrOut )
2020-04-19 06:53:00 +00:00
if string ( patchBytes ) == "{}" && ! o . shouldPrintObject ( ) {
printer , err := o . ToPrinter ( "unchanged" )
if err != nil {
return err
}
if err = printer . PrintObj ( info . Object , o . Out ) ; err != nil {
return err
}
return nil
}
}
if o . shouldPrintObject ( ) {
return nil
}
printer , err := o . ToPrinter ( "configured" )
if err != nil {
return err
}
if err = printer . PrintObj ( info . Object , o . Out ) ; err != nil {
return err
2019-01-12 04:58:27 +00:00
}
return nil
}
2020-03-26 21:07:15 +00:00
func ( o * ApplyOptions ) shouldPrintObject ( ) bool {
// Print object only if output format other than "name" is specified
shouldPrint := false
output := * o . PrintFlags . OutputFormat
shortOutput := output == "name"
if len ( output ) > 0 && ! shortOutput {
shouldPrint = true
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
return shouldPrint
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
func ( o * ApplyOptions ) printObjects ( ) error {
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
if ! o . shouldPrintObject ( ) {
return nil
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
infos , err := o . GetObjects ( )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
if len ( infos ) > 0 {
printer , err := o . ToPrinter ( "" )
2019-01-12 04:58:27 +00:00
if err != nil {
return err
}
2020-03-26 21:07:15 +00:00
objToPrint := infos [ 0 ] . Object
if len ( infos ) > 1 {
objs := [ ] runtime . Object { }
for _ , info := range infos {
objs = append ( objs , info . Object )
}
list := & corev1 . List {
TypeMeta : metav1 . TypeMeta {
Kind : "List" ,
APIVersion : "v1" ,
} ,
ListMeta : metav1 . ListMeta { } ,
}
if err := meta . SetList ( list , objs ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
2020-03-26 21:07:15 +00:00
objToPrint = list
}
if err := printer . PrintObj ( objToPrint , o . Out ) ; err != nil {
2019-01-12 04:58:27 +00:00
return err
}
}
2020-03-26 21:07:15 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
// MarkNamespaceVisited keeps track of which namespaces the applied
// objects belong to. Used for pruning.
func ( o * ApplyOptions ) MarkNamespaceVisited ( info * resource . Info ) {
if info . Namespaced ( ) {
o . VisitedNamespaces . Insert ( info . Namespace )
2019-01-12 04:58:27 +00:00
}
}
2021-03-18 22:40:29 +00:00
// MarkObjectVisited keeps track of UIDs of the applied
2020-03-26 21:07:15 +00:00
// objects. Used for pruning.
func ( o * ApplyOptions ) MarkObjectVisited ( info * resource . Info ) error {
metadata , err := meta . Accessor ( info . Object )
2019-01-12 04:58:27 +00:00
if err != nil {
2020-03-26 21:07:15 +00:00
return err
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
o . VisitedUids . Insert ( string ( metadata . GetUID ( ) ) )
2019-01-12 04:58:27 +00:00
return nil
}
2021-03-18 22:40:29 +00:00
// PrintAndPrunePostProcessor returns a function which meets the PostProcessorFn
2020-03-26 21:07:15 +00:00
// function signature. This returned function prints all the
// objects as a list (if configured for that), and prunes the
// objects not applied. The returned function is the standard
// apply post processor.
func ( o * ApplyOptions ) PrintAndPrunePostProcessor ( ) func ( ) error {
2019-01-12 04:58:27 +00:00
2020-03-26 21:07:15 +00:00
return func ( ) error {
if err := o . printObjects ( ) ; err != nil {
return err
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
if o . Prune {
p := newPruner ( o )
return p . pruneAll ( o )
2019-01-12 04:58:27 +00:00
}
2020-03-26 21:07:15 +00:00
return nil
2019-01-12 04:58:27 +00:00
}
}
2020-08-10 17:43:49 +00:00
const (
// FieldManagerClientSideApply is the default client-side apply field manager.
//
// The default field manager is not `kubectl-apply` to distinguish from
// server-side apply.
FieldManagerClientSideApply = "kubectl-client-side-apply"
// The default server-side apply field manager is `kubectl`
// instead of a field manager like `kubectl-server-side-apply`
// for backward compatibility to not conflict with old versions
// of kubectl server-side apply where `kubectl` has already been the field manager.
fieldManagerServerSideApply = "kubectl"
)
// GetApplyFieldManagerFlag gets the field manager for kubectl apply
// if it is not set.
//
// The default field manager is not `kubectl-apply` to distinguish between
// client-side and server-side apply.
func GetApplyFieldManagerFlag ( cmd * cobra . Command , serverSide bool ) string {
// The field manager flag was set
if cmd . Flag ( "field-manager" ) . Changed {
return cmdutil . GetFlagString ( cmd , "field-manager" )
}
if serverSide {
return fieldManagerServerSideApply
}
return FieldManagerClientSideApply
}
2020-12-01 01:06:26 +00:00
// WarnIfDeleting prints a warning if a resource is being deleted
func WarnIfDeleting ( obj runtime . Object , stderr io . Writer ) {
metadata , _ := meta . Accessor ( obj )
if metadata != nil && metadata . GetDeletionTimestamp ( ) != nil {
// just warn the user about the conflict
fmt . Fprintf ( stderr , warningChangesOnDeletingResource , metadata . GetName ( ) )
}
}