2014-10-05 21:24:19 -04:00
/ *
2016-06-02 20:25:58 -04:00
Copyright 2014 The Kubernetes Authors .
2014-10-05 21:24:19 -04: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 07:06:12 -04:00
package replace
2014-10-05 21:24:19 -04:00
import (
2014-10-26 22:21:31 -04:00
"fmt"
2015-07-15 08:10:47 -04:00
"io/ioutil"
2015-06-27 00:25:08 -04:00
"os"
2015-07-15 08:10:47 -04:00
"path/filepath"
2018-05-17 11:27:44 -04:00
"time"
2014-10-05 21:24:19 -04:00
2015-04-11 02:06:05 -04:00
"github.com/spf13/cobra"
2018-11-09 13:49:10 -05:00
"k8s.io/klog"
2018-04-09 19:13:50 -04:00
2017-01-13 12:48:50 -05:00
"k8s.io/apimachinery/pkg/api/errors"
2018-04-09 19:13:50 -04:00
"k8s.io/apimachinery/pkg/runtime"
2017-01-11 09:09:48 -05:00
"k8s.io/apimachinery/pkg/util/wait"
2018-08-21 06:46:39 -04:00
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/kubectl"
2018-10-05 08:38:38 -04:00
"k8s.io/kubernetes/pkg/kubectl/cmd/delete"
2015-08-05 18:03:47 -04:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
2018-09-14 18:34:16 -04:00
"k8s.io/kubernetes/pkg/kubectl/scheme"
2017-07-07 00:04:11 -04:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2018-10-10 14:29:30 -04:00
"k8s.io/kubernetes/pkg/kubectl/util/templates"
2018-04-09 19:13:50 -04:00
"k8s.io/kubernetes/pkg/kubectl/validation"
2014-10-05 21:24:19 -04:00
)
2016-05-20 13:49:56 -04:00
var (
2017-02-15 22:47:00 -05:00
replaceLong = templates . LongDesc ( i18n . T ( `
2016-05-20 13:49:56 -04:00
Replace a resource by filename or stdin .
2015-02-20 16:28:43 -05:00
2016-05-20 13:49:56 -04:00
JSON and YAML formats are accepted . If replacing an existing resource , the
complete resource spec must be provided . This can be obtained by
2016-10-07 18:24:42 -04:00
$ kubectl get TYPE NAME - o yaml
2015-07-13 16:46:51 -04:00
2017-03-14 23:49:10 -04:00
Please refer to the models in https : //htmlpreview.github.io/?https://github.com/kubernetes/kubernetes/blob/HEAD/docs/api-reference/v1/definitions.html to find if a field is mutable.`))
2016-10-07 18:24:42 -04:00
2017-02-15 22:47:00 -05:00
replaceExample = templates . Examples ( i18n . T ( `
2016-05-20 13:49:56 -04:00
# Replace a pod using the data in pod . json .
kubectl replace - f . / pod . json
2015-02-20 16:28:43 -05:00
2016-05-20 13:49:56 -04:00
# Replace a pod based on the JSON passed into stdin .
cat pod . json | kubectl replace - f -
2015-06-17 19:56:55 -04:00
2016-05-20 13:49:56 -04:00
# Update a single - container pod ' s image version ( tag ) to v4
kubectl get pod mypod - o yaml | sed ' s / \ ( image : myimage \ ) : . * $ / \ 1 : v4 / ' | kubectl replace - f -
2015-08-07 18:21:31 -04:00
2016-05-20 13:49:56 -04:00
# Force replace , delete and then re - create the resource
2017-03-14 23:49:10 -04:00
kubectl replace -- force - f . / pod . json ` ) )
2015-02-20 16:28:43 -05:00
)
2018-04-19 10:41:17 -04:00
type ReplaceOptions struct {
2018-05-02 15:15:47 -04:00
PrintFlags * genericclioptions . PrintFlags
2018-04-19 10:41:17 -04:00
RecordFlags * genericclioptions . RecordFlags
2018-04-13 20:28:28 -04:00
2018-10-05 08:38:38 -04:00
DeleteFlags * delete . DeleteFlags
DeleteOptions * delete . DeleteOptions
2018-04-09 19:13:50 -04:00
PrintObj func ( obj runtime . Object ) error
createAnnotation bool
validate bool
Schema validation . Schema
Builder func ( ) * resource . Builder
BuilderArgs [ ] string
Namespace string
EnforceNamespace bool
2018-04-19 10:41:17 -04:00
Recorder genericclioptions . Recorder
2018-05-08 09:02:34 -04:00
genericclioptions . IOStreams
2018-04-09 19:13:50 -04:00
}
2018-05-08 09:02:34 -04:00
func NewReplaceOptions ( streams genericclioptions . IOStreams ) * ReplaceOptions {
2018-04-19 10:41:17 -04:00
return & ReplaceOptions {
2018-07-02 10:05:24 -04:00
PrintFlags : genericclioptions . NewPrintFlags ( "replaced" ) ,
2018-10-05 08:38:38 -04:00
DeleteFlags : delete . NewDeleteFlags ( "to use to replace the resource." ) ,
2018-04-09 19:13:50 -04:00
2018-05-08 09:02:34 -04:00
IOStreams : streams ,
2018-04-09 19:13:50 -04:00
}
2018-04-19 10:41:17 -04:00
}
2018-05-08 09:02:34 -04:00
func NewCmdReplace ( f cmdutil . Factory , streams genericclioptions . IOStreams ) * cobra . Command {
o := NewReplaceOptions ( streams )
2015-08-14 14:46:43 -04:00
2014-10-05 21:24:19 -04:00
cmd := & cobra . Command {
2018-10-05 15:59:38 -04:00
Use : "replace -f FILENAME" ,
2017-10-11 02:26:02 -04:00
DisableFlagsInUseLine : true ,
2018-10-05 15:59:38 -04:00
Short : i18n . T ( "Replace a resource by filename or stdin" ) ,
Long : replaceLong ,
Example : replaceExample ,
2014-10-05 21:24:19 -04:00
Run : func ( cmd * cobra . Command , args [ ] string ) {
2018-04-19 10:41:17 -04:00
cmdutil . CheckErr ( o . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( o . Validate ( cmd ) )
cmdutil . CheckErr ( o . Run ( ) )
2014-10-05 21:24:19 -04:00
} ,
}
2018-04-09 19:13:50 -04:00
2018-04-19 10:41:17 -04:00
o . PrintFlags . AddFlags ( cmd )
o . DeleteFlags . AddFlags ( cmd )
o . RecordFlags . AddFlags ( cmd )
2018-04-09 19:13:50 -04:00
2015-03-17 11:49:35 -04:00
cmd . MarkFlagRequired ( "filename" )
2015-09-10 17:58:09 -04:00
cmdutil . AddValidateFlags ( cmd )
2015-11-04 16:47:08 -05:00
cmdutil . AddApplyAnnotationFlags ( cmd )
2016-03-09 20:27:19 -05:00
2014-10-05 21:24:19 -04:00
return cmd
}
2015-01-15 16:55:53 -05:00
2018-04-19 10:41:17 -04:00
func ( o * ReplaceOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
var err error
2018-04-09 19:13:50 -04:00
2018-05-21 15:27:11 -04:00
o . RecordFlags . Complete ( cmd )
2018-04-19 10:41:17 -04:00
o . Recorder , err = o . RecordFlags . ToRecorder ( )
if err != nil {
return err
2018-04-09 19:13:50 -04:00
}
2018-04-19 10:41:17 -04:00
o . validate = cmdutil . GetFlagBool ( cmd , "validate" )
o . createAnnotation = cmdutil . GetFlagBool ( cmd , cmdutil . ApplyAnnotationsFlag )
2018-04-09 19:13:50 -04:00
printer , err := o . PrintFlags . ToPrinter ( )
2015-03-09 18:08:16 -04:00
if err != nil {
return err
}
2018-04-09 19:13:50 -04:00
o . PrintObj = func ( obj runtime . Object ) error {
return printer . PrintObj ( obj , o . Out )
}
2018-05-18 08:12:55 -04:00
dynamicClient , err := f . DynamicClient ( )
if err != nil {
return err
}
deleteOpts := o . DeleteFlags . ToOptions ( dynamicClient , o . IOStreams )
2018-04-09 19:13:50 -04:00
2018-04-13 20:28:28 -04:00
//Replace will create a resource if it doesn't exist already, so ignore not found error
deleteOpts . IgnoreNotFound = true
2018-04-09 19:13:50 -04:00
if o . PrintFlags . OutputFormat != nil {
2018-04-13 20:28:28 -04:00
deleteOpts . Output = * o . PrintFlags . OutputFormat
2018-04-09 19:13:50 -04:00
}
2018-04-13 20:28:28 -04:00
if deleteOpts . GracePeriod == 0 {
2018-04-09 19:13:50 -04:00
// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
// into --grace-period=1 and wait until the object is successfully deleted.
2018-04-13 20:28:28 -04:00
deleteOpts . GracePeriod = 1
deleteOpts . WaitForDeletion = true
2018-04-09 19:13:50 -04:00
}
2018-04-13 20:28:28 -04:00
o . DeleteOptions = deleteOpts
2018-04-09 19:13:50 -04:00
schema , err := f . Validator ( o . validate )
2015-03-09 18:08:16 -04:00
if err != nil {
return err
}
2018-04-09 19:13:50 -04:00
o . Schema = schema
o . Builder = f . NewBuilder
o . BuilderArgs = args
2015-03-09 18:08:16 -04:00
2018-05-24 09:33:36 -04:00
o . Namespace , o . EnforceNamespace , err = f . ToRawKubeConfigLoader ( ) . Namespace ( )
2018-04-09 19:13:50 -04:00
if err != nil {
return err
2015-06-24 20:33:46 -04:00
}
2018-04-09 19:13:50 -04:00
return nil
}
2018-04-19 10:41:17 -04:00
func ( o * ReplaceOptions ) Validate ( cmd * cobra . Command ) error {
2018-04-09 19:13:50 -04:00
if o . DeleteOptions . GracePeriod >= 0 && ! o . DeleteOptions . ForceDeletion {
2016-08-31 14:48:47 -04:00
return fmt . Errorf ( "--grace-period must have --force specified" )
}
2018-04-09 19:13:50 -04:00
if o . DeleteOptions . Timeout != 0 && ! o . DeleteOptions . ForceDeletion {
2016-08-31 14:48:47 -04:00
return fmt . Errorf ( "--timeout must have --force specified" )
}
2018-04-13 20:28:28 -04:00
if cmdutil . IsFilenameSliceEmpty ( o . DeleteOptions . FilenameOptions . Filenames ) {
2018-04-09 19:13:50 -04:00
return cmdutil . UsageErrorf ( cmd , "Must specify --filename to replace" )
}
return nil
}
2018-04-19 10:41:17 -04:00
func ( o * ReplaceOptions ) Run ( ) error {
2018-04-09 19:13:50 -04:00
if o . DeleteOptions . ForceDeletion {
return o . forceReplace ( )
}
r := o . Builder ( ) .
2017-11-13 23:01:51 -05:00
Unstructured ( ) .
2018-04-09 19:13:50 -04:00
Schema ( o . Schema ) .
2015-03-09 18:08:16 -04:00
ContinueOnError ( ) .
2018-04-09 19:13:50 -04:00
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
2018-04-13 20:28:28 -04:00
FilenameParam ( o . EnforceNamespace , & o . DeleteOptions . FilenameOptions ) .
2015-03-09 18:08:16 -04:00
Flatten ( ) .
Do ( )
2017-11-13 23:01:51 -05:00
if err := r . Err ( ) ; err != nil {
2015-03-09 18:08:16 -04:00
return err
}
2018-04-13 20:28:28 -04:00
return r . Visit ( func ( info * resource . Info , err error ) error {
2015-06-14 22:48:56 -04:00
if err != nil {
return err
}
2015-09-10 17:32:57 -04:00
2018-09-14 18:34:16 -04:00
if err := kubectl . CreateOrUpdateAnnotation ( o . createAnnotation , info . Object , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
2015-11-04 16:47:08 -05:00
return cmdutil . AddSourceToErr ( "replacing" , info . Source , err )
2015-09-10 17:32:57 -04:00
}
2018-04-19 10:41:17 -04:00
if err := o . Recorder . Record ( info . Object ) ; err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
2016-01-22 13:33:23 -05:00
}
2015-09-10 17:32:57 -04:00
// Serialize the object with the annotation applied.
2015-10-28 12:43:21 -04:00
obj , err := resource . NewHelper ( info . Client , info . Mapping ) . Replace ( info . Namespace , info . Name , true , info . Object )
2015-03-09 18:08:16 -04:00
if err != nil {
2015-06-27 00:25:08 -04:00
return cmdutil . AddSourceToErr ( "replacing" , info . Source , err )
2015-03-09 18:08:16 -04:00
}
2015-09-10 17:32:57 -04:00
2015-03-09 18:08:16 -04:00
info . Refresh ( obj , true )
2018-04-26 11:21:41 -04:00
return o . PrintObj ( info . Object )
2015-03-09 18:08:16 -04:00
} )
}
2018-04-19 10:41:17 -04:00
func ( o * ReplaceOptions ) forceReplace ( ) error {
2018-04-13 20:28:28 -04:00
for i , filename := range o . DeleteOptions . FilenameOptions . Filenames {
2015-07-15 08:10:47 -04:00
if filename == "-" {
tempDir , err := ioutil . TempDir ( "" , "kubectl_replace_" )
if err != nil {
return err
}
defer os . RemoveAll ( tempDir )
tempFilename := filepath . Join ( tempDir , "resource.stdin" )
err = cmdutil . DumpReaderToFile ( os . Stdin , tempFilename )
if err != nil {
return err
}
2018-04-13 20:28:28 -04:00
o . DeleteOptions . FilenameOptions . Filenames [ i ] = tempFilename
2015-07-15 08:10:47 -04:00
}
}
2018-04-09 19:13:50 -04:00
r := o . Builder ( ) .
2017-11-15 01:10:30 -05:00
Unstructured ( ) .
2015-06-24 20:33:46 -04:00
ContinueOnError ( ) .
2018-04-09 19:13:50 -04:00
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
ResourceTypeOrNameArgs ( false , o . BuilderArgs ... ) . RequireObject ( false ) .
2018-04-13 20:28:28 -04:00
FilenameParam ( o . EnforceNamespace , & o . DeleteOptions . FilenameOptions ) .
2015-06-24 20:33:46 -04:00
Flatten ( ) .
Do ( )
2017-11-13 23:01:51 -05:00
if err := r . Err ( ) ; err != nil {
2015-06-24 20:33:46 -04:00
return err
}
2017-11-13 23:01:51 -05:00
2018-05-17 11:27:44 -04:00
if err := o . DeleteOptions . DeleteResult ( r ) ; err != nil {
return err
2015-06-24 20:33:46 -04:00
}
2018-05-17 11:27:44 -04:00
timeout := o . DeleteOptions . Timeout
2016-08-31 20:12:19 -04:00
if timeout == 0 {
2018-05-17 11:27:44 -04:00
timeout = 5 * time . Minute
2016-08-31 20:12:19 -04:00
}
2018-05-17 11:27:44 -04:00
err := r . Visit ( func ( info * resource . Info , err error ) error {
2016-08-31 20:12:19 -04:00
if err != nil {
return err
}
2018-05-17 11:27:44 -04:00
return wait . PollImmediate ( 1 * time . Second , timeout , func ( ) ( bool , error ) {
2016-08-31 20:12:19 -04:00
if err := info . Get ( ) ; ! errors . IsNotFound ( err ) {
return false , err
}
return true , nil
} )
} )
2017-11-02 08:15:34 -04:00
if err != nil {
return err
}
2016-08-31 20:12:19 -04:00
2018-04-09 19:13:50 -04:00
r = o . Builder ( ) .
2017-11-13 23:01:51 -05:00
Unstructured ( ) .
2018-04-09 19:13:50 -04:00
Schema ( o . Schema ) .
2015-06-24 20:33:46 -04:00
ContinueOnError ( ) .
2018-04-09 19:13:50 -04:00
NamespaceParam ( o . Namespace ) . DefaultNamespace ( ) .
2018-04-13 20:28:28 -04:00
FilenameParam ( o . EnforceNamespace , & o . DeleteOptions . FilenameOptions ) .
2015-06-24 20:33:46 -04:00
Flatten ( ) .
Do ( )
err = r . Err ( )
if err != nil {
return err
}
count := 0
2015-06-14 22:48:56 -04:00
err = r . Visit ( func ( info * resource . Info , err error ) error {
if err != nil {
return err
}
2015-09-10 17:32:57 -04:00
2018-09-14 18:34:16 -04:00
if err := kubectl . CreateOrUpdateAnnotation ( o . createAnnotation , info . Object , scheme . DefaultJSONEncoder ( ) ) ; err != nil {
2015-09-10 17:32:57 -04:00
return err
}
2018-04-19 10:41:17 -04:00
if err := o . Recorder . Record ( info . Object ) ; err != nil {
2018-11-09 13:49:10 -05:00
klog . V ( 4 ) . Infof ( "error recording current command: %v" , err )
2016-01-22 13:33:23 -05:00
}
2018-08-30 09:33:34 -04:00
obj , err := resource . NewHelper ( info . Client , info . Mapping ) . Create ( info . Namespace , true , info . Object , nil )
2015-06-24 20:33:46 -04:00
if err != nil {
return err
}
2015-09-10 17:32:57 -04:00
2015-06-24 20:33:46 -04:00
count ++
info . Refresh ( obj , true )
2018-04-26 11:21:41 -04:00
return o . PrintObj ( info . Object )
2015-06-24 20:33:46 -04:00
} )
if err != nil {
return err
}
if count == 0 {
2015-06-27 00:25:08 -04:00
return fmt . Errorf ( "no objects passed to replace" )
2015-06-24 20:33:46 -04:00
}
return nil
}