2015-09-10 21:32:57 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2014 The Kubernetes Authors .
2015-09-10 21:32:57 +00:00
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 .
* /
2018-10-05 11:06:12 +00:00
package apply
2015-09-10 21:32:57 +00:00
import (
2018-11-16 18:32:47 +00:00
"encoding/json"
2015-09-10 21:32:57 +00:00
"fmt"
"io"
2016-11-01 21:02:00 +00:00
"strings"
2016-06-09 04:14:17 +00:00
"time"
2015-09-10 21:32:57 +00:00
2016-06-09 04:14:17 +00:00
"github.com/jonboulle/clockwork"
2015-09-10 21:32:57 +00:00
"github.com/spf13/cobra"
2018-09-26 21:19:14 +00:00
corev1 "k8s.io/api/core/v1"
2017-01-13 17:48:50 +00:00
"k8s.io/apimachinery/pkg/api/errors"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
2017-01-22 03:36:02 +00:00
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2018-05-07 12:32:20 +00:00
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
2017-01-16 20:13:59 +00:00
"k8s.io/apimachinery/pkg/types"
2017-02-02 23:19:50 +00:00
"k8s.io/apimachinery/pkg/util/jsonmergepatch"
2017-02-03 19:49:37 +00:00
"k8s.io/apimachinery/pkg/util/mergepatch"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/sets"
2017-01-24 14:35:22 +00:00
"k8s.io/apimachinery/pkg/util/strategicpatch"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/util/wait"
2018-08-21 10:46:39 +00:00
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/printers"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
2018-10-05 00:10:08 +00:00
"k8s.io/client-go/discovery"
2018-04-27 15:38:34 +00:00
"k8s.io/client-go/dynamic"
2018-11-09 18:49:10 +00:00
"k8s.io/klog"
2017-11-21 18:21:55 +00:00
oapi "k8s.io/kube-openapi/pkg/util/proto"
2015-09-10 21:32:57 +00:00
"k8s.io/kubernetes/pkg/kubectl"
2018-10-05 12:38:38 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/delete"
2015-09-10 21:32:57 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2017-11-21 18:21:55 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
2017-10-28 01:31:42 +00:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 18:29:30 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2018-05-07 21:47:29 +00:00
"k8s.io/kubernetes/pkg/kubectl/validation"
2015-09-10 21:32:57 +00:00
)
2016-09-13 20:10:21 +00:00
type ApplyOptions struct {
2018-04-12 19:28:39 +00:00
RecordFlags * genericclioptions . RecordFlags
Recorder genericclioptions . Recorder
2018-04-11 23:10:30 +00:00
2018-05-02 19:15:47 +00:00
PrintFlags * genericclioptions . PrintFlags
ToPrinter func ( string ) ( printers . ResourcePrinter , error )
2018-04-12 19:28:39 +00:00
2018-10-05 12:38:38 +00:00
DeleteFlags * delete . DeleteFlags
DeleteOptions * delete . DeleteOptions
2018-04-11 23:10:30 +00:00
2018-05-07 21:47:29 +00:00
Selector string
DryRun bool
2018-08-30 13:33:34 +00:00
ServerDryRun bool
2018-05-07 21:47:29 +00:00
Prune bool
PruneResources [ ] pruneResource
cmdBaseName string
All bool
Overwrite bool
2018-10-30 10:35:24 +00:00
OpenAPIPatch bool
2018-05-07 21:47:29 +00:00
PruneWhitelist [ ] string
ShouldIncludeUninitialized bool
2018-10-05 00:10:08 +00:00
Validator validation . Schema
Builder * resource . Builder
Mapper meta . RESTMapper
DynamicClient dynamic . Interface
DiscoveryClient discovery . DiscoveryInterface
OpenAPISchema openapi . Resources
2018-05-07 21:47:29 +00:00
Namespace string
EnforceNamespace bool
2018-04-11 23:10:30 +00:00
2018-04-19 21:43:28 +00:00
genericclioptions . IOStreams
2016-09-13 20:10:21 +00:00
}
2016-06-09 04:14:17 +00:00
const (
// maxPatchRetry is the maximum number of conflicts retry for during a patch operation before returning failure
maxPatchRetry = 5
// backOffPeriod is the period to back off when apply patch resutls in error.
backOffPeriod = 1 * time . Second
// how many times we can retry before back off
triesBeforeBackOff = 1
)
2016-05-20 17:49:56 +00:00
var (
2017-02-16 03:47:00 +00:00
applyLong = templates . LongDesc ( i18n . T ( `
2016-05-20 17:49:56 +00:00
Apply a configuration to a resource by filename or stdin .
2017-04-18 11:54:41 +00:00
The resource name must be specified . This resource will be created if it doesn ' t exist yet .
2016-05-20 17:49:56 +00:00
To use ' apply ' , always create the resource initially with either ' apply ' or ' create -- save - config ' .
2016-09-20 07:38:31 +00:00
JSON and YAML formats are accepted .
2016-10-07 22:24:42 +00:00
2017-03-15 03:49:10 +00:00
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.`))
2015-09-10 21:32:57 +00:00
2017-02-16 03:47:00 +00:00
applyExample = templates . Examples ( i18n . T ( `
2016-05-20 17:49:56 +00:00
# Apply the configuration in pod . json to a pod .
kubectl apply - f . / pod . json
2015-09-10 21:32:57 +00:00
2016-05-20 17:49:56 +00:00
# Apply the JSON passed into stdin to a pod .
2016-11-22 23:27:01 +00:00
cat pod . json | kubectl apply - f -
# Note : -- prune is still in Alpha
# Apply the configuration in manifest . yaml that matches label app = nginx and delete all the other resources that are not in the file and match label app = nginx .
kubectl apply -- prune - f manifest . yaml - l app = nginx
# Apply the configuration in manifest . yaml and delete all the other configmaps that are not in the file .
2017-03-15 03:49:10 +00:00
kubectl apply -- prune - f manifest . yaml -- all -- prune - whitelist = core / v1 / ConfigMap ` ) )
2017-07-13 18:38:38 +00:00
warningNoLastAppliedConfigAnnotation = "Warning: %[1]s apply should be used on resource created by either %[1]s create --save-config or %[1]s apply\n"
2015-09-10 21:32:57 +00:00
)
2018-04-19 21:43:28 +00:00
func NewApplyOptions ( ioStreams genericclioptions . IOStreams ) * ApplyOptions {
2018-02-24 06:41:35 +00:00
return & ApplyOptions {
2018-04-18 15:20:33 +00:00
RecordFlags : genericclioptions . NewRecordFlags ( ) ,
2018-10-05 12:38:38 +00:00
DeleteFlags : delete . NewDeleteFlags ( "that contains the configuration to apply" ) ,
2018-05-02 19:15:47 +00:00
PrintFlags : genericclioptions . NewPrintFlags ( "created" ) . WithTypeSetter ( scheme . Scheme ) ,
2018-04-11 23:10:30 +00:00
2018-02-24 06:41:35 +00:00
Overwrite : true ,
2018-10-30 10:35:24 +00:00
OpenAPIPatch : true ,
2018-04-11 23:10:30 +00:00
2018-04-19 12:32:15 +00:00
Recorder : genericclioptions . NoopRecorder { } ,
2018-04-19 21:43:28 +00:00
IOStreams : ioStreams ,
2018-02-24 06:41:35 +00:00
}
}
2018-10-30 10:35:24 +00:00
// NewCmdApply creates the `apply` command
2018-04-19 21:43:28 +00:00
func NewCmdApply ( baseName string , f cmdutil . Factory , ioStreams genericclioptions . IOStreams ) * cobra . Command {
o := NewApplyOptions ( ioStreams )
2015-09-10 21:32:57 +00:00
2017-07-13 18:38:38 +00:00
// Store baseName for use in printing warnings / messages involving the base command name.
// This is useful for downstream command that wrap this one.
2018-04-19 12:32:15 +00:00
o . cmdBaseName = baseName
2017-07-13 18:38:38 +00:00
2015-09-10 21:32:57 +00:00
cmd := & cobra . Command {
2018-10-05 19:59:38 +00:00
Use : "apply -f FILENAME" ,
2017-10-11 06:26:02 +00:00
DisableFlagsInUseLine : true ,
2018-10-05 19:59:38 +00:00
Short : i18n . T ( "Apply a configuration to a resource by filename or stdin" ) ,
Long : applyLong ,
Example : applyExample ,
2015-09-10 21:32:57 +00:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-04-12 19:28:39 +00:00
cmdutil . CheckErr ( o . Complete ( f , cmd ) )
2015-09-10 21:32:57 +00:00
cmdutil . CheckErr ( validateArgs ( cmd , args ) )
2018-04-19 12:32:15 +00:00
cmdutil . CheckErr ( validatePruneAll ( o . Prune , o . All , o . Selector ) )
2018-05-07 21:47:29 +00:00
cmdutil . CheckErr ( o . Run ( ) )
2015-09-10 21:32:57 +00:00
} ,
}
2018-04-11 23:10:30 +00:00
// bind flag structs
2018-04-12 19:28:39 +00:00
o . DeleteFlags . AddFlags ( cmd )
2018-04-19 12:32:15 +00:00
o . RecordFlags . AddFlags ( cmd )
2018-04-12 19:28:39 +00:00
o . PrintFlags . AddFlags ( cmd )
2018-04-11 23:10:30 +00:00
2015-09-10 21:32:57 +00:00
cmd . MarkFlagRequired ( "filename" )
2018-04-19 12:32:15 +00:00
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." )
2015-09-10 21:32:57 +00:00
cmdutil . AddValidateFlags ( cmd )
2018-04-19 12:32:15 +00:00
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" )
2018-10-30 10:35:24 +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." )
2018-08-30 13:33:34 +00:00
cmd . Flags ( ) . BoolVar ( & o . ServerDryRun , "server-dry-run" , o . ServerDryRun , "If true, request will be sent to server with dry-run flag, which means the modifications won't be persisted. This is an alpha feature and flag." )
2016-10-11 15:41:02 +00:00
cmdutil . AddDryRunFlag ( cmd )
2017-08-11 06:21:44 +00:00
cmdutil . AddIncludeUninitializedFlag ( cmd )
2017-02-08 17:42:31 +00:00
// apply subcommands
2018-04-19 21:43:28 +00:00
cmd . AddCommand ( NewCmdApplyViewLastApplied ( f , ioStreams ) )
cmd . AddCommand ( NewCmdApplySetLastApplied ( f , ioStreams ) )
cmd . AddCommand ( NewCmdApplyEditLastApplied ( f , ioStreams ) )
2017-02-08 17:42:31 +00:00
2015-09-10 21:32:57 +00:00
return cmd
}
2018-04-12 19:28:39 +00:00
func ( o * ApplyOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command ) error {
o . DryRun = cmdutil . GetDryRunFlag ( cmd )
2018-08-30 13:33:34 +00:00
if o . DryRun && o . ServerDryRun {
return fmt . Errorf ( "--dry-run and --server-dry-run can't be used together" )
}
2018-04-12 19:28:39 +00:00
// allow for a success message operation to be specified at print time
2018-05-02 19:15:47 +00:00
o . ToPrinter = func ( operation string ) ( printers . ResourcePrinter , error ) {
2018-04-12 19:28:39 +00:00
o . PrintFlags . NamePrintFlags . Operation = operation
if o . DryRun {
o . PrintFlags . Complete ( "%s (dry run)" )
}
2018-08-30 13:33:34 +00:00
if o . ServerDryRun {
o . PrintFlags . Complete ( "%s (server dry run)" )
}
2018-05-02 19:15:47 +00:00
return o . PrintFlags . ToPrinter ( )
2018-04-12 19:28:39 +00:00
}
var err error
2018-05-21 19:27:11 +00:00
o . RecordFlags . Complete ( cmd )
2018-04-12 19:28:39 +00:00
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
}
2018-10-05 00:10:08 +00:00
o . DiscoveryClient , err = f . ToDiscoveryClient ( )
if err != nil {
return err
}
2018-05-18 12:12:55 +00:00
dynamicClient , err := f . DynamicClient ( )
if err != nil {
return err
}
o . DeleteOptions = o . DeleteFlags . ToOptions ( dynamicClient , o . IOStreams )
2018-05-07 21:47:29 +00:00
o . ShouldIncludeUninitialized = cmdutil . ShouldIncludeUninitialized ( cmd , o . Prune )
o . OpenAPISchema , _ = f . OpenAPISchema ( )
o . Validator , err = f . Validator ( cmdutil . GetFlagBool ( cmd , "validate" ) )
o . Builder = f . NewBuilder ( )
2018-05-16 14:54:42 +00:00
o . Mapper , err = f . ToRESTMapper ( )
2018-05-07 21:47:29 +00:00
if err != nil {
return err
}
o . DynamicClient , err = f . DynamicClient ( )
if err != nil {
return err
}
2018-05-24 13:33:36 +00:00
o . Namespace , o . EnforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
2018-05-07 21:47:29 +00:00
if err != nil {
return err
}
2018-04-12 19:28:39 +00:00
return nil
}
2015-09-10 21:32:57 +00:00
func validateArgs ( cmd * cobra . Command , args [ ] string ) error {
if len ( args ) != 0 {
2017-06-14 21:14:42 +00:00
return cmdutil . UsageErrorf ( cmd , "Unexpected args: %v" , args )
2015-09-10 21:32:57 +00:00
}
return nil
}
2016-09-20 07:38:31 +00:00
func validatePruneAll ( prune , all bool , selector string ) error {
2017-08-25 08:28:21 +00:00
if all && len ( selector ) > 0 {
return fmt . Errorf ( "cannot set --all and --selector at the same time" )
}
2016-09-20 07:38:31 +00:00
if prune && ! all && selector == "" {
2018-10-30 10:35:24 +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" )
2016-09-20 07:38:31 +00:00
}
return nil
}
2017-02-02 23:19:50 +00:00
func parsePruneResources ( mapper meta . RESTMapper , gvks [ ] string ) ( [ ] pruneResource , error ) {
2016-11-01 21:02:00 +00:00
pruneResources := [ ] pruneResource { }
for _ , groupVersionKind := range gvks {
gvk := strings . Split ( groupVersionKind , "/" )
if len ( gvk ) != 3 {
return nil , fmt . Errorf ( "invalid GroupVersionKind format: %v, please follow <group/version/kind>" , groupVersionKind )
}
if gvk [ 0 ] == "core" {
gvk [ 0 ] = ""
}
2017-02-02 23:19:50 +00:00
mapping , err := mapper . RESTMapping ( schema . GroupKind { Group : gvk [ 0 ] , Kind : gvk [ 2 ] } , gvk [ 1 ] )
if err != nil {
return pruneResources , err
}
var namespaced bool
namespaceScope := mapping . Scope . Name ( )
switch namespaceScope {
case meta . RESTScopeNameNamespace :
namespaced = true
case meta . RESTScopeNameRoot :
namespaced = false
default :
return pruneResources , fmt . Errorf ( "Unknown namespace scope: %q" , namespaceScope )
}
2016-11-01 21:02:00 +00:00
pruneResources = append ( pruneResources , pruneResource { gvk [ 0 ] , gvk [ 1 ] , gvk [ 2 ] , namespaced } )
}
return pruneResources , nil
}
2018-05-07 21:47:29 +00:00
func ( o * ApplyOptions ) Run ( ) error {
2017-11-21 18:21:55 +00:00
var openapiSchema openapi . Resources
2018-10-30 10:35:24 +00:00
if o . OpenAPIPatch {
2018-05-07 21:47:29 +00:00
openapiSchema = o . OpenAPISchema
2015-09-10 21:32:57 +00:00
}
2018-10-12 18:30:49 +00:00
dryRunVerifier := & DryRunVerifier {
Finder : cmdutil . NewCRDFinder ( cmdutil . CRDFromDynamic ( o . DynamicClient ) ) ,
OpenAPIGetter : o . DiscoveryClient ,
}
2017-08-11 06:21:44 +00:00
// include the uninitialized objects by default if --prune is true
// unless explicitly set --include-uninitialized=false
2018-05-07 21:47:29 +00:00
r := o . Builder .
2017-11-14 04:01:51 +00:00
Unstructured ( ) .
2018-05-07 21:47:29 +00:00
Schema ( o . Validator ) .
2015-09-10 21:32:57 +00:00
ContinueOnError ( ) .
2018-05-07 21:47:29 +00:00
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
FilenameParam ( o . EnforceNamespace , & o . DeleteOptions . FilenameOptions ) .
2018-04-11 23:10:30 +00:00
LabelSelectorParam ( o . Selector ) .
2018-05-07 21:47:29 +00:00
IncludeUninitialized ( o . ShouldIncludeUninitialized ) .
2015-09-10 21:32:57 +00:00
Flatten ( ) .
Do ( )
2017-11-14 04:01:51 +00:00
if err := r . Err ( ) ; err != nil {
2015-09-10 21:32:57 +00:00
return err
}
2018-05-07 21:47:29 +00:00
var err error
2018-04-11 23:10:30 +00:00
if o . Prune {
2018-05-07 21:47:29 +00:00
o . PruneResources , err = parsePruneResources ( o . Mapper , o . PruneWhitelist )
2017-11-15 03:10:35 +00:00
if err != nil {
return err
}
}
2018-05-07 21:47:29 +00:00
output := * o . PrintFlags . OutputFormat
2016-12-08 20:26:37 +00:00
shortOutput := output == "name"
2016-10-11 15:41:02 +00:00
2016-09-20 07:38:31 +00:00
visitedUids := sets . NewString ( )
visitedNamespaces := sets . NewString ( )
2018-04-16 21:34:05 +00:00
var objs [ ] runtime . Object
2015-09-10 21:32:57 +00:00
count := 0
err = r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
2018-12-07 17:46:09 +00:00
// If server-dry-run is requested but the type doesn't support it, fail right away.
if o . ServerDryRun {
if err := dryRunVerifier . HasSupport ( info . Mapping . GroupVersionKind ) ; err != nil {
return err
}
}
2016-09-20 07:38:31 +00:00
if info . Namespaced ( ) {
visitedNamespaces . Insert ( info . Namespace )
}
2018-04-11 23:10:30 +00:00
if err := o . Recorder . Record ( info . Object ) ; err != nil {
2018-11-09 18:49:10 +00:00
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
2017-06-08 22:50:57 +00:00
}
2015-09-10 21:32:57 +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.
2018-05-07 12:32:20 +00:00
modified , err := kubectl . GetModifiedConfiguration ( info . Object , true , unstructured . UnstructuredJSONScheme )
2015-09-10 21:32:57 +00:00
if err != nil {
2018-01-29 07:14:04 +00:00
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "retrieving modified configuration from:\n%s\nfor:" , info . String ( ) ) , info . Source , err )
2015-09-10 21:32:57 +00:00
}
2018-01-26 01:49:06 +00:00
// Print object only if output format other than "name" is specified
printObject := len ( output ) > 0 && ! shortOutput
2015-09-10 21:32:57 +00:00
if err := info . Get ( ) ; err != nil {
2015-11-05 02:29:56 +00:00
if ! errors . IsNotFound ( err ) {
2018-01-29 07:14:04 +00:00
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "retrieving current configuration of:\n%s\nfrom server for:" , info . String ( ) ) , info . Source , err )
2015-11-05 02:29:56 +00:00
}
2018-10-12 18:30:49 +00:00
2015-11-05 02:29:56 +00:00
// Create the resource if it doesn't exist
// First, update the annotation used by kubectl apply
2018-05-07 12:32:20 +00:00
if err := kubectl . CreateApplyAnnotation ( info . Object , unstructured . UnstructuredJSONScheme ) ; err != nil {
2015-11-05 02:29:56 +00:00
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
}
2016-01-22 18:33:23 +00:00
2018-04-12 19:28:39 +00:00
if ! o . DryRun {
2016-10-11 15:41:02 +00:00
// Then create the resource and skip the three-way merge
2018-08-30 13:33:34 +00:00
options := metav1 . CreateOptions { }
if o . ServerDryRun {
options . DryRun = [ ] string { metav1 . DryRunAll }
}
obj , err := resource . NewHelper ( info . Client , info . Mapping ) . Create ( info . Namespace , true , info . Object , & options )
2018-04-10 14:21:50 +00:00
if err != nil {
2016-10-11 15:41:02 +00:00
return cmdutil . AddSourceToErr ( "creating" , info . Source , err )
}
2018-04-10 14:21:50 +00:00
info . Refresh ( obj , true )
2016-09-20 07:38:31 +00:00
}
2016-10-11 15:41:02 +00:00
2018-10-02 19:56:57 +00:00
metadata , err := meta . Accessor ( info . Object )
if err != nil {
return err
}
visitedUids . Insert ( string ( metadata . GetUID ( ) ) )
2015-11-05 02:29:56 +00:00
count ++
2018-04-12 19:28:39 +00:00
2018-04-16 21:34:05 +00:00
if printObject {
objs = append ( objs , info . Object )
return nil
}
2018-04-12 19:28:39 +00:00
printer , err := o . ToPrinter ( "created" )
if err != nil {
return err
2016-12-08 20:26:37 +00:00
}
2018-04-26 15:21:41 +00:00
return printer . PrintObj ( info . Object , o . Out )
2015-09-10 21:32:57 +00:00
}
2018-10-02 19:56:57 +00:00
metadata , err := meta . Accessor ( info . Object )
if err != nil {
return err
}
visitedUids . Insert ( string ( metadata . GetUID ( ) ) )
2018-04-23 14:23:01 +00:00
2018-10-02 19:56:57 +00:00
if ! o . DryRun {
2018-04-23 14:23:01 +00:00
annotationMap := metadata . GetAnnotations ( )
2018-09-26 21:19:14 +00:00
if _ , ok := annotationMap [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2018-04-11 23:10:30 +00:00
fmt . Fprintf ( o . ErrOut , warningNoLastAppliedConfigAnnotation , o . cmdBaseName )
2016-11-11 22:38:36 +00:00
}
2018-05-07 21:47:29 +00:00
2016-10-11 15:41:02 +00:00
helper := resource . NewHelper ( info . Client , info . Mapping )
2018-10-05 12:38:38 +00:00
patcher := & Patcher {
2018-10-12 18:30:49 +00:00
Mapping : info . Mapping ,
Helper : helper ,
DynamicClient : o . DynamicClient ,
Overwrite : o . Overwrite ,
BackOff : clockwork . NewRealClock ( ) ,
Force : o . DeleteOptions . ForceDeletion ,
Cascade : o . DeleteOptions . Cascade ,
Timeout : o . DeleteOptions . Timeout ,
GracePeriod : o . DeleteOptions . GracePeriod ,
ServerDryRun : o . ServerDryRun ,
OpenapiSchema : openapiSchema ,
2018-11-16 18:32:47 +00:00
Retries : maxPatchRetry ,
2016-10-11 21:40:01 +00:00
}
2015-09-10 21:32:57 +00:00
2018-10-05 12:38:38 +00:00
patchBytes , patchedObject , err := patcher . Patch ( info . Object , modified , info . Source , info . Namespace , info . Name , o . ErrOut )
2016-01-22 18:33:23 +00:00
if err != nil {
2016-10-11 15:41:02 +00:00
return cmdutil . AddSourceToErr ( fmt . Sprintf ( "applying patch:\n%s\nto:\n%v\nfor:" , patchBytes , info ) , info . Source , err )
2016-01-22 18:33:23 +00:00
}
2016-10-11 15:41:02 +00:00
2017-02-19 02:32:43 +00:00
info . Refresh ( patchedObject , true )
2018-01-26 01:49:06 +00:00
if string ( patchBytes ) == "{}" && ! printObject {
2017-10-17 07:13:56 +00:00
count ++
2018-04-12 19:28:39 +00:00
printer , err := o . ToPrinter ( "unchanged" )
if err != nil {
return err
}
2018-04-26 15:21:41 +00:00
return printer . PrintObj ( info . Object , o . Out )
2017-10-17 07:13:56 +00:00
}
2016-09-20 07:38:31 +00:00
}
2015-09-10 21:32:57 +00:00
count ++
2018-04-12 19:28:39 +00:00
2018-04-16 21:34:05 +00:00
if printObject {
objs = append ( objs , info . Object )
return nil
}
2018-04-12 19:28:39 +00:00
printer , err := o . ToPrinter ( "configured" )
if err != nil {
return err
2016-12-08 20:26:37 +00:00
}
2018-04-26 15:21:41 +00:00
return printer . PrintObj ( info . Object , o . Out )
2015-09-10 21:32:57 +00:00
} )
if err != nil {
return err
}
2018-04-16 21:34:05 +00:00
2015-09-10 21:32:57 +00:00
if count == 0 {
return fmt . Errorf ( "no objects passed to apply" )
}
2018-04-16 21:34:05 +00:00
// print objects
if len ( objs ) > 0 {
printer , err := o . ToPrinter ( "" )
if err != nil {
return err
}
objToPrint := objs [ 0 ]
if len ( objs ) > 1 {
2018-09-26 21:19:14 +00:00
list := & corev1 . List {
2018-04-16 21:34:05 +00:00
TypeMeta : metav1 . TypeMeta {
Kind : "List" ,
APIVersion : "v1" ,
} ,
ListMeta : metav1 . ListMeta { } ,
}
if err := meta . SetList ( list , objs ) ; err != nil {
return err
}
objToPrint = list
}
if err := printer . PrintObj ( objToPrint , o . Out ) ; err != nil {
return err
}
}
2018-04-11 23:10:30 +00:00
if ! o . Prune {
2016-09-20 07:38:31 +00:00
return nil
}
p := pruner {
2018-05-07 21:47:29 +00:00
mapper : o . Mapper ,
dynamicClient : o . DynamicClient ,
2016-09-20 07:38:31 +00:00
2018-04-11 23:10:30 +00:00
labelSelector : o . Selector ,
2017-08-04 09:56:44 +00:00
visitedUids : visitedUids ,
2016-09-20 07:38:31 +00:00
2018-08-30 13:33:34 +00:00
cascade : o . DeleteOptions . Cascade ,
dryRun : o . DryRun ,
serverDryRun : o . ServerDryRun ,
gracePeriod : o . DeleteOptions . GracePeriod ,
2018-04-12 19:28:39 +00:00
toPrinter : o . ToPrinter ,
2016-09-20 07:38:31 +00:00
2018-04-11 23:10:30 +00:00
out : o . Out ,
2016-09-20 07:38:31 +00:00
}
2016-11-01 21:02:00 +00:00
2018-05-07 21:47:29 +00:00
namespacedRESTMappings , nonNamespacedRESTMappings , err := getRESTMappings ( o . Mapper , & ( o . PruneResources ) )
2016-11-01 21:02:00 +00:00
if err != nil {
return fmt . Errorf ( "error retrieving RESTMappings to prune: %v" , err )
}
2016-09-20 07:38:31 +00:00
for n := range visitedNamespaces {
2016-11-01 21:02:00 +00:00
for _ , m := range namespacedRESTMappings {
2018-05-07 21:47:29 +00:00
if err := p . prune ( n , m , o . ShouldIncludeUninitialized ) ; err != nil {
2016-11-01 21:02:00 +00:00
return fmt . Errorf ( "error pruning namespaced object %v: %v" , m . GroupVersionKind , err )
2016-09-20 07:38:31 +00:00
}
}
}
2016-11-01 21:02:00 +00:00
for _ , m := range nonNamespacedRESTMappings {
2018-05-07 21:47:29 +00:00
if err := p . prune ( metav1 . NamespaceNone , m , o . ShouldIncludeUninitialized ) ; err != nil {
2016-11-01 21:02:00 +00:00
return fmt . Errorf ( "error pruning nonNamespaced object %v: %v" , m . GroupVersionKind , err )
2016-09-20 07:38:31 +00:00
}
}
return nil
}
2016-11-01 21:02:00 +00:00
type pruneResource struct {
group string
version string
kind string
namespaced bool
}
func ( pr pruneResource ) String ( ) string {
return fmt . Sprintf ( "%v/%v, Kind=%v, Namespaced=%v" , pr . group , pr . version , pr . kind , pr . namespaced )
}
2017-02-02 23:19:50 +00:00
func getRESTMappings ( mapper meta . RESTMapper , pruneResources * [ ] pruneResource ) ( namespaced , nonNamespaced [ ] * meta . RESTMapping , err error ) {
2016-11-01 21:02:00 +00:00
if len ( * pruneResources ) == 0 {
// default whitelist
// TODO: need to handle the older api versions - e.g. v1beta1 jobs. Github issue: #35991
* pruneResources = [ ] pruneResource {
{ "" , "v1" , "ConfigMap" , true } ,
{ "" , "v1" , "Endpoints" , true } ,
{ "" , "v1" , "Namespace" , false } ,
{ "" , "v1" , "PersistentVolumeClaim" , true } ,
{ "" , "v1" , "PersistentVolume" , false } ,
{ "" , "v1" , "Pod" , true } ,
{ "" , "v1" , "ReplicationController" , true } ,
{ "" , "v1" , "Secret" , true } ,
{ "" , "v1" , "Service" , true } ,
{ "batch" , "v1" , "Job" , true } ,
2018-04-23 06:38:20 +00:00
{ "batch" , "v1beta1" , "CronJob" , true } ,
2016-11-01 21:02:00 +00:00
{ "extensions" , "v1beta1" , "DaemonSet" , true } ,
{ "extensions" , "v1beta1" , "Deployment" , true } ,
{ "extensions" , "v1beta1" , "Ingress" , true } ,
{ "extensions" , "v1beta1" , "ReplicaSet" , true } ,
{ "apps" , "v1beta1" , "StatefulSet" , true } ,
2017-01-10 16:25:32 +00:00
{ "apps" , "v1beta1" , "Deployment" , true } ,
2016-11-01 21:02:00 +00:00
}
}
2017-02-02 23:19:50 +00:00
2016-11-01 21:02:00 +00:00
for _ , resource := range * pruneResources {
2017-02-02 23:19:50 +00:00
addedMapping , err := mapper . RESTMapping ( schema . GroupKind { Group : resource . group , Kind : resource . kind } , resource . version )
2016-11-01 21:02:00 +00:00
if err != nil {
return nil , nil , fmt . Errorf ( "invalid resource %v: %v" , resource , err )
}
if resource . namespaced {
namespaced = append ( namespaced , addedMapping )
} else {
nonNamespaced = append ( nonNamespaced , addedMapping )
}
}
return namespaced , nonNamespaced , nil
}
2016-09-20 07:38:31 +00:00
type pruner struct {
mapper meta . RESTMapper
2018-05-09 16:58:12 +00:00
dynamicClient dynamic . Interface
2016-09-20 07:38:31 +00:00
2017-08-04 09:56:44 +00:00
visitedUids sets . String
labelSelector string
fieldSelector string
2016-09-20 07:38:31 +00:00
2018-08-30 13:33:34 +00:00
cascade bool
serverDryRun bool
dryRun bool
gracePeriod int
2016-09-20 07:38:31 +00:00
2018-05-02 19:15:47 +00:00
toPrinter func ( string ) ( printers . ResourcePrinter , error )
2018-04-12 19:28:39 +00:00
2016-09-20 07:38:31 +00:00
out io . Writer
}
2018-05-07 21:47:29 +00:00
func ( p * pruner ) prune ( namespace string , mapping * meta . RESTMapping , includeUninitialized bool ) error {
2018-05-02 20:25:48 +00:00
objList , err := p . dynamicClient . Resource ( mapping . Resource ) .
2018-04-27 15:38:34 +00:00
Namespace ( namespace ) .
List ( metav1 . ListOptions {
2017-08-04 09:56:44 +00:00
LabelSelector : p . labelSelector ,
FieldSelector : p . fieldSelector ,
2017-10-12 04:06:17 +00:00
IncludeUninitialized : includeUninitialized ,
2018-04-27 15:38:34 +00:00
} )
2016-09-20 07:38:31 +00:00
if err != nil {
return err
}
2018-04-27 15:38:34 +00:00
2016-09-20 07:38:31 +00:00
objs , err := meta . ExtractList ( objList )
if err != nil {
return err
}
for _ , obj := range objs {
2018-04-23 14:23:01 +00:00
metadata , err := meta . Accessor ( obj )
2016-09-20 07:38:31 +00:00
if err != nil {
return err
}
2018-04-23 14:23:01 +00:00
annots := metadata . GetAnnotations ( )
2018-09-26 21:19:14 +00:00
if _ , ok := annots [ corev1 . LastAppliedConfigAnnotation ] ; ! ok {
2016-09-20 07:38:31 +00:00
// don't prune resources not created with apply
continue
}
2018-04-23 14:23:01 +00:00
uid := metadata . GetUID ( )
2016-09-20 07:38:31 +00:00
if p . visitedUids . Has ( string ( uid ) ) {
continue
}
2018-04-23 14:23:01 +00:00
name := metadata . GetName ( )
2016-11-01 21:02:00 +00:00
if ! p . dryRun {
2018-05-17 15:27:44 +00:00
if err := p . delete ( namespace , name , mapping ) ; err != nil {
2016-11-01 21:02:00 +00:00
return err
}
2016-09-20 07:38:31 +00:00
}
2018-04-12 19:28:39 +00:00
printer , err := p . toPrinter ( "pruned" )
if err != nil {
return err
}
printer . PrintObj ( obj , p . out )
2016-09-20 07:38:31 +00:00
}
return nil
}
2018-05-17 15:27:44 +00:00
func ( p * pruner ) delete ( namespace , name string , mapping * meta . RESTMapping ) error {
2018-08-30 13:33:34 +00:00
return runDelete ( namespace , name , mapping , p . dynamicClient , p . cascade , p . gracePeriod , p . serverDryRun )
2016-10-11 21:40:01 +00:00
}
2018-08-30 13:33:34 +00:00
func runDelete ( namespace , name string , mapping * meta . RESTMapping , c dynamic . Interface , cascade bool , gracePeriod int , serverDryRun bool ) error {
2018-05-17 15:27:44 +00:00
options := & metav1 . DeleteOptions { }
2016-10-11 21:40:01 +00:00
if gracePeriod >= 0 {
2017-01-24 15:38:21 +00:00
options = metav1 . NewDeleteOptions ( int64 ( gracePeriod ) )
2016-10-11 21:40:01 +00:00
}
2018-08-30 13:33:34 +00:00
if serverDryRun {
options . DryRun = [ ] string { metav1 . DryRunAll }
}
2018-05-17 15:27:44 +00:00
policy := metav1 . DeletePropagationForeground
if ! cascade {
policy = metav1 . DeletePropagationOrphan
2016-09-20 07:38:31 +00:00
}
2018-05-17 15:27:44 +00:00
options . PropagationPolicy = & policy
return c . Resource ( mapping . Resource ) . Namespace ( namespace ) . Delete ( name , options )
2015-09-10 21:32:57 +00:00
}
2016-06-09 04:14:17 +00:00
2018-10-05 12:38:38 +00:00
func ( p * Patcher ) delete ( namespace , name string ) error {
return runDelete ( namespace , name , p . Mapping , p . DynamicClient , p . Cascade , p . GracePeriod , p . ServerDryRun )
2016-10-11 21:40:01 +00:00
}
2018-10-05 12:38:38 +00:00
type Patcher struct {
2018-10-12 18:30:49 +00:00
Mapping * meta . RESTMapping
Helper * resource . Helper
DynamicClient dynamic . Interface
2016-06-09 04:14:17 +00:00
2018-10-05 12:38:38 +00:00
Overwrite bool
BackOff clockwork . Clock
2016-06-09 04:14:17 +00:00
2018-10-05 12:38:38 +00:00
Force bool
Cascade bool
Timeout time . Duration
GracePeriod int
ServerDryRun bool
2017-11-21 18:21:55 +00:00
2018-11-16 18:32:47 +00:00
// If set, forces the patch against a specific resourceVersion
ResourceVersion * string
// Number of retries to make if the patch fails with conflict
Retries int
2018-10-05 12:38:38 +00:00
OpenapiSchema openapi . Resources
2016-06-09 04:14:17 +00:00
}
2018-10-05 00:10:08 +00:00
// DryRunVerifier verifies if a given group-version-kind supports DryRun
// against the current server. Sending dryRun requests to apiserver that
// don't support it will result in objects being unwillingly persisted.
//
// It reads the OpenAPI to see if the given GVK supports dryRun. If the
// GVK can not be found, we assume that CRDs will have the same level of
// support as "namespaces", and non-CRDs will not be supported. We
// delay the check for CRDs as much as possible though, since it
// requires an extra round-trip to the server.
type DryRunVerifier struct {
Finder cmdutil . CRDFinder
OpenAPIGetter discovery . OpenAPISchemaInterface
}
// HasSupport verifies if the given gvk supports DryRun. An error is
// returned if it doesn't.
func ( v * DryRunVerifier ) HasSupport ( gvk schema . GroupVersionKind ) error {
oapi , err := v . OpenAPIGetter . OpenAPISchema ( )
if err != nil {
return fmt . Errorf ( "failed to download openapi: %v" , err )
}
supports , err := openapi . SupportsDryRun ( oapi , gvk )
if err != nil {
// We assume that we couldn't find the type, then check for namespace:
supports , _ = openapi . SupportsDryRun ( oapi , schema . GroupVersionKind { Group : "" , Version : "v1" , Kind : "Namespace" } )
// If namespace supports dryRun, then we will support dryRun for CRDs only.
if supports {
supports , err = v . Finder . HasCRD ( gvk . GroupKind ( ) )
if err != nil {
return fmt . Errorf ( "failed to check CRD: %v" , err )
}
}
}
if ! supports {
return fmt . Errorf ( "%v doesn't support dry-run" , gvk )
}
return nil
}
2018-11-16 18:32:47 +00:00
func addResourceVersion ( patch [ ] byte , rv string ) ( [ ] byte , error ) {
var patchMap map [ string ] interface { }
err := json . Unmarshal ( patch , & patchMap )
if err != nil {
return nil , err
}
u := unstructured . Unstructured { Object : patchMap }
a , err := meta . Accessor ( & u )
if err != nil {
return nil , err
}
a . SetResourceVersion ( rv )
return json . Marshal ( patchMap )
}
2018-10-05 12:38:38 +00:00
func ( p * Patcher ) patchSimple ( obj runtime . Object , modified [ ] byte , source , namespace , name string , errOut io . Writer ) ( [ ] byte , runtime . Object , error ) {
2016-06-09 04:14:17 +00:00
// Serialize the current configuration of the object from the server.
2018-05-07 12:32:20 +00:00
current , err := runtime . Encode ( unstructured . UnstructuredJSONScheme , obj )
2016-06-09 04:14:17 +00:00
if err != nil {
2017-02-19 02:32:43 +00:00
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( "serializing current configuration from:\n%v\nfor:" , obj ) , source , err )
2016-06-09 04:14:17 +00:00
}
// Retrieve the original configuration of the object from the annotation.
2018-04-23 14:23:01 +00:00
original , err := kubectl . GetOriginalConfiguration ( obj )
2016-06-09 04:14:17 +00:00
if err != nil {
2017-02-19 02:32:43 +00:00
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( "retrieving original configuration from:\n%v\nfor:" , obj ) , source , err )
2016-06-09 04:14:17 +00:00
}
2017-02-02 23:19:50 +00:00
var patchType types . PatchType
var patch [ ] byte
2017-11-21 18:21:55 +00:00
var lookupPatchMeta strategicpatch . LookupPatchMeta
var schema oapi . Schema
2017-02-02 23:19:50 +00:00
createPatchErrFormat := "creating patch with:\noriginal:\n%s\nmodified:\n%s\ncurrent:\n%s\nfor:"
2017-12-01 21:44:11 +00:00
// Create the versioned struct from the type defined in the restmapping
// (which is the API version we'll be submitting the patch to)
2018-10-05 12:38:38 +00:00
versionedObject , err := scheme . Scheme . New ( p . Mapping . GroupVersionKind )
2017-12-01 21:44:11 +00:00
switch {
case runtime . IsNotRegisteredError ( err ) :
// fall back to generic JSON merge patch
patchType = types . MergePatchType
preconditions := [ ] mergepatch . PreconditionFunc { mergepatch . RequireKeyUnchanged ( "apiVersion" ) ,
mergepatch . RequireKeyUnchanged ( "kind" ) , mergepatch . RequireMetadataKeyUnchanged ( "name" ) }
patch , err = jsonmergepatch . CreateThreeWayJSONMergePatch ( original , modified , current , preconditions ... )
if err != nil {
if mergepatch . IsPreconditionFailed ( err ) {
return nil , nil , fmt . Errorf ( "%s" , "At least one of apiVersion, kind and name was changed" )
2017-02-03 19:49:37 +00:00
}
2017-12-01 21:44:11 +00:00
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( createPatchErrFormat , original , modified , current ) , source , err )
2017-02-02 23:19:50 +00:00
}
2017-12-01 21:44:11 +00:00
case err != nil :
2018-10-05 12:38:38 +00:00
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( "getting instance of versioned object for %v:" , p . Mapping . GroupVersionKind ) , source , err )
2017-12-01 21:44:11 +00:00
case err == nil :
// Compute a three way strategic merge patch to send to server.
patchType = types . StrategicMergePatchType
// Try to use openapi first if the openapi spec is available and can successfully calculate the patch.
// Otherwise, fall back to baked-in types.
2018-10-05 12:38:38 +00:00
if p . OpenapiSchema != nil {
if schema = p . OpenapiSchema . LookupResource ( p . Mapping . GroupVersionKind ) ; schema != nil {
2017-12-01 21:44:11 +00:00
lookupPatchMeta = strategicpatch . PatchMetaFromOpenAPI { Schema : schema }
2018-10-05 12:38:38 +00:00
if openapiPatch , err := strategicpatch . CreateThreeWayMergePatch ( original , modified , current , lookupPatchMeta , p . Overwrite ) ; err != nil {
2017-12-01 21:44:11 +00:00
fmt . Fprintf ( errOut , "warning: error calculating patch from openapi spec: %v\n" , err )
} else {
patchType = types . StrategicMergePatchType
patch = openapiPatch
2017-11-21 18:21:55 +00:00
}
}
2017-12-01 21:44:11 +00:00
}
if patch == nil {
2017-11-21 18:21:55 +00:00
lookupPatchMeta , err = strategicpatch . NewPatchMetaFromStruct ( versionedObject )
if err != nil {
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( createPatchErrFormat , original , modified , current ) , source , err )
}
2018-10-05 12:38:38 +00:00
patch , err = strategicpatch . CreateThreeWayMergePatch ( original , modified , current , lookupPatchMeta , p . Overwrite )
2017-11-21 18:21:55 +00:00
if err != nil {
return nil , nil , cmdutil . AddSourceToErr ( fmt . Sprintf ( createPatchErrFormat , original , modified , current ) , source , err )
}
2017-02-02 23:19:50 +00:00
}
2016-06-09 04:14:17 +00:00
}
2017-10-17 07:13:56 +00:00
if string ( patch ) == "{}" {
return patch , obj , nil
}
2018-11-16 18:32:47 +00:00
if p . ResourceVersion != nil {
patch , err = addResourceVersion ( patch , * p . ResourceVersion )
if err != nil {
return nil , nil , cmdutil . AddSourceToErr ( "Failed to insert resourceVersion in patch" , source , err )
}
}
2018-08-30 13:33:34 +00:00
options := metav1 . UpdateOptions { }
2018-10-05 12:38:38 +00:00
if p . ServerDryRun {
2018-08-30 13:33:34 +00:00
options . DryRun = [ ] string { metav1 . DryRunAll }
}
2018-10-05 12:38:38 +00:00
patchedObj , err := p . Helper . Patch ( namespace , name , patchType , patch , & options )
2017-02-19 02:32:43 +00:00
return patch , patchedObj , err
2016-06-09 04:14:17 +00:00
}
2018-10-05 12:38:38 +00:00
func ( p * Patcher ) Patch ( current runtime . Object , modified [ ] byte , source , namespace , name string , errOut io . Writer ) ( [ ] byte , runtime . Object , error ) {
2016-06-09 04:14:17 +00:00
var getErr error
2017-11-21 18:21:55 +00:00
patchBytes , patchObject , err := p . patchSimple ( current , modified , source , namespace , name , errOut )
2018-11-16 18:32:47 +00:00
if p . Retries == 0 {
p . Retries = maxPatchRetry
}
for i := 1 ; i <= p . Retries && errors . IsConflict ( err ) ; i ++ {
2016-06-09 04:14:17 +00:00
if i > triesBeforeBackOff {
2018-10-05 12:38:38 +00:00
p . BackOff . Sleep ( backOffPeriod )
2016-06-09 04:14:17 +00:00
}
2018-10-05 12:38:38 +00:00
current , getErr = p . Helper . Get ( namespace , name , false )
2016-06-09 04:14:17 +00:00
if getErr != nil {
2017-02-19 02:32:43 +00:00
return nil , nil , getErr
2016-06-09 04:14:17 +00:00
}
2017-11-21 18:21:55 +00:00
patchBytes , patchObject , err = p . patchSimple ( current , modified , source , namespace , name , errOut )
2016-06-09 04:14:17 +00:00
}
2018-10-05 12:38:38 +00:00
if err != nil && ( errors . IsConflict ( err ) || errors . IsInvalid ( err ) ) && p . Force {
2018-01-29 22:28:15 +00:00
patchBytes , patchObject , err = p . deleteAndCreate ( current , modified , namespace , name )
2016-10-11 21:40:01 +00:00
}
2017-02-19 02:32:43 +00:00
return patchBytes , patchObject , err
2016-06-09 04:14:17 +00:00
}
2016-10-11 21:40:01 +00:00
2018-10-05 12:38:38 +00:00
func ( p * Patcher ) deleteAndCreate ( original runtime . Object , modified [ ] byte , namespace , name string ) ( [ ] byte , runtime . Object , error ) {
2018-05-17 15:27:44 +00:00
if err := p . delete ( namespace , name ) ; err != nil {
2017-02-19 02:32:43 +00:00
return modified , nil , err
2016-10-11 21:40:01 +00:00
}
2018-05-17 15:27:44 +00:00
// TODO: use wait
2018-10-05 12:38:38 +00:00
if err := wait . PollImmediate ( 1 * time . Second , p . Timeout , func ( ) ( bool , error ) {
if _ , err := p . Helper . Get ( namespace , name , false ) ; ! errors . IsNotFound ( err ) {
2016-10-11 21:40:01 +00:00
return false , err
}
return true , nil
2018-05-17 15:27:44 +00:00
} ) ; err != nil {
2017-02-19 02:32:43 +00:00
return modified , nil , err
2016-10-11 21:40:01 +00:00
}
2018-05-07 12:32:20 +00:00
versionedObject , _ , err := unstructured . UnstructuredJSONScheme . Decode ( modified , nil , nil )
2016-10-11 21:40:01 +00:00
if err != nil {
2017-02-19 02:32:43 +00:00
return modified , nil , err
2016-10-11 21:40:01 +00:00
}
2018-08-30 13:33:34 +00:00
options := metav1 . CreateOptions { }
2018-10-05 12:38:38 +00:00
if p . ServerDryRun {
2018-08-30 13:33:34 +00:00
options . DryRun = [ ] string { metav1 . DryRunAll }
}
2018-10-05 12:38:38 +00:00
createdObject , err := p . Helper . Create ( namespace , true , versionedObject , & options )
2018-01-29 22:28:15 +00:00
if err != nil {
// restore the original object if we fail to create the new one
// but still propagate and advertise error to user
2018-10-05 12:38:38 +00:00
recreated , recreateErr := p . Helper . Create ( namespace , true , original , & options )
2018-01-29 22:28:15 +00:00
if recreateErr != nil {
2018-10-30 10:35:24 +00:00
err = fmt . Errorf ( "An error occurred force-replacing the existing object with the newly provided one:\n\n%v.\n\nAdditionally, an error occurred attempting to restore the original object:\n\n%v" , err , recreateErr )
2018-01-29 22:28:15 +00:00
} else {
createdObject = recreated
}
}
2017-02-19 02:32:43 +00:00
return modified , createdObject , err
2016-10-11 21:40:01 +00:00
}