2015-06-25 18:56:30 -04:00
/ *
Copyright 2014 The Kubernetes Authors All rights reserved .
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 cmd
import (
2015-08-06 05:24:02 -04:00
"fmt"
2015-06-25 18:56:30 -04:00
"io"
2016-02-11 15:52:42 -05:00
"strings"
2015-06-25 18:56:30 -04:00
"github.com/spf13/cobra"
2015-09-30 11:58:42 -04:00
2015-08-05 18:03:47 -04:00
"k8s.io/kubernetes/pkg/api"
2015-08-06 05:24:02 -04:00
"k8s.io/kubernetes/pkg/kubectl"
2015-08-05 18:03:47 -04:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
2016-02-11 15:52:42 -05:00
"k8s.io/kubernetes/pkg/util/sets"
2015-09-30 11:58:42 -04:00
"k8s.io/kubernetes/pkg/util/yaml"
2015-06-25 18:56:30 -04:00
)
2016-02-11 15:52:42 -05:00
var patchTypes = map [ string ] api . PatchType { "json" : api . JSONPatchType , "merge" : api . MergePatchType , "strategic" : api . StrategicMergePatchType }
2015-08-14 14:46:43 -04:00
// PatchOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of
// referencing the cmd.Flags()
type PatchOptions struct {
Filenames [ ] string
}
2015-06-25 18:56:30 -04:00
const (
patch_long = ` Update field ( s ) of a resource using strategic merge patch
2015-07-13 16:46:51 -04:00
JSON and YAML formats are accepted .
2015-11-16 20:13:49 -05: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.`
2015-06-25 18:56:30 -04:00
patch_example = `
2015-08-12 12:50:09 -04:00
# Partially update a node using strategic merge patch
2015-07-13 16:46:51 -04:00
kubectl patch node k8s - node - 1 - p ' { "spec" : { "unschedulable" : true } } '
2015-08-06 05:24:02 -04:00
# Partially update a node identified by the type and name specified in "node.json" using strategic merge patch
kubectl patch - f node . json - p ' { "spec" : { "unschedulable" : true } } '
2015-08-12 12:50:09 -04:00
# Update a container ' s image ; spec . containers [ * ] . name is required because it ' s a merge key
2016-02-11 15:52:42 -05:00
kubectl patch pod valid - pod - p ' { "spec" : { "containers" : [ { "name" : "kubernetes-serve-hostname" , "image" : "new image" } ] } } '
# Update a container ' s image using a json patch with positional arrays
kubectl patch pod valid - pod - type = ' json ' - p = ' [ { "op" : "replace" , "path" : "/spec/containers/0/image" , "value" : "new image" } ] ' `
2015-06-25 18:56:30 -04:00
)
func NewCmdPatch ( f * cmdutil . Factory , out io . Writer ) * cobra . Command {
2015-08-14 14:46:43 -04:00
options := & PatchOptions { }
2015-06-25 18:56:30 -04:00
cmd := & cobra . Command {
2015-08-06 05:24:02 -04:00
Use : "patch (-f FILENAME | TYPE NAME) -p PATCH" ,
2015-11-10 14:55:20 -05:00
Short : "Update field(s) of a resource using strategic merge patch." ,
2015-06-25 18:56:30 -04:00
Long : patch_long ,
Example : patch_example ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
2015-07-01 18:47:43 -04:00
cmdutil . CheckErr ( cmdutil . ValidateOutputArgs ( cmd ) )
shortOutput := cmdutil . GetFlagString ( cmd , "output" ) == "name"
2015-08-14 14:46:43 -04:00
err := RunPatch ( f , out , cmd , args , shortOutput , options )
2015-07-31 19:43:39 -04:00
cmdutil . CheckErr ( err )
2015-06-25 18:56:30 -04:00
} ,
}
2015-07-13 12:36:12 -04:00
cmd . Flags ( ) . StringP ( "patch" , "p" , "" , "The patch to be applied to the resource JSON file." )
2015-06-25 18:56:30 -04:00
cmd . MarkFlagRequired ( "patch" )
2016-02-11 15:52:42 -05:00
cmd . Flags ( ) . String ( "type" , "strategic" , fmt . Sprintf ( "The type of patch being provided; one of %v" , sets . StringKeySet ( patchTypes ) . List ( ) ) )
2015-07-01 18:47:43 -04:00
cmdutil . AddOutputFlagsForMutation ( cmd )
2016-01-22 13:33:23 -05:00
cmdutil . AddRecordFlag ( cmd )
2015-08-06 05:24:02 -04:00
usage := "Filename, directory, or URL to a file identifying the resource to update"
2015-08-14 14:46:43 -04:00
kubectl . AddJsonFilenameFlag ( cmd , & options . Filenames , usage )
2015-06-25 18:56:30 -04:00
return cmd
}
2015-08-14 14:46:43 -04:00
func RunPatch ( f * cmdutil . Factory , out io . Writer , cmd * cobra . Command , args [ ] string , shortOutput bool , options * PatchOptions ) error {
2015-08-06 05:24:02 -04:00
cmdNamespace , enforceNamespace , err := f . DefaultNamespace ( )
2015-06-25 18:56:30 -04:00
if err != nil {
return err
}
2016-02-11 15:52:42 -05:00
patchType := api . StrategicMergePatchType
patchTypeString := strings . ToLower ( cmdutil . GetFlagString ( cmd , "type" ) )
if len ( patchTypeString ) != 0 {
ok := false
patchType , ok = patchTypes [ patchTypeString ]
if ! ok {
return cmdutil . UsageError ( cmd , fmt . Sprintf ( "--type must be one of %v, not %q" , sets . StringKeySet ( patchTypes ) . List ( ) , patchTypeString ) )
}
}
2015-06-25 18:56:30 -04:00
patch := cmdutil . GetFlagString ( cmd , "patch" )
if len ( patch ) == 0 {
return cmdutil . UsageError ( cmd , "Must specify -p to patch" )
}
2015-09-30 11:58:42 -04:00
patchBytes , err := yaml . ToJSON ( [ ] byte ( patch ) )
if err != nil {
return fmt . Errorf ( "unable to parse %q: %v" , patch , err )
}
2015-06-25 18:56:30 -04:00
mapper , typer := f . Object ( )
2015-12-21 00:37:49 -05:00
r := resource . NewBuilder ( mapper , typer , resource . ClientMapperFunc ( f . ClientForMapping ) , f . Decoder ( true ) ) .
2015-06-25 18:56:30 -04:00
ContinueOnError ( ) .
NamespaceParam ( cmdNamespace ) . DefaultNamespace ( ) .
2015-08-14 14:46:43 -04:00
FilenameParam ( enforceNamespace , options . Filenames ... ) .
2015-06-25 18:56:30 -04:00
ResourceTypeOrNameArgs ( false , args ... ) .
Flatten ( ) .
Do ( )
err = r . Err ( )
if err != nil {
return err
}
2015-08-06 05:24:02 -04:00
infos , err := r . Infos ( )
2015-06-25 18:56:30 -04:00
if err != nil {
return err
}
2015-08-06 05:24:02 -04:00
if len ( infos ) > 1 {
return fmt . Errorf ( "multiple resources provided" )
2015-06-25 18:56:30 -04:00
}
2015-08-06 05:24:02 -04:00
info := infos [ 0 ]
name , namespace := info . Name , info . Namespace
mapping := info . ResourceMapping ( )
2015-12-21 00:37:49 -05:00
client , err := f . ClientForMapping ( mapping )
2015-06-25 18:56:30 -04:00
if err != nil {
return err
}
helper := resource . NewHelper ( client , mapping )
2016-02-12 14:20:08 -05:00
patchedObject , err := helper . Patch ( namespace , name , patchType , patchBytes )
2015-06-25 18:56:30 -04:00
if err != nil {
return err
}
2016-01-22 13:33:23 -05:00
if cmdutil . ShouldRecord ( cmd , info ) {
2016-02-12 14:20:08 -05:00
if err := cmdutil . RecordChangeCause ( patchedObject , f . Command ( ) ) ; err == nil {
// don't return an error on failure. The patch itself succeeded, its only the hint for that change that failed
// don't bother checking for failures of this replace, because a failure to indicate the hint doesn't fail the command
// also, don't force the replacement. If the replacement fails on a resourceVersion conflict, then it means this
// record hint is likely to be invalid anyway, so avoid the bad hint
resource . NewHelper ( client , mapping ) . Replace ( namespace , name , false , patchedObject )
2016-01-22 13:33:23 -05:00
}
}
2015-07-01 18:47:43 -04:00
cmdutil . PrintSuccess ( mapper , shortOutput , out , "" , name , "patched" )
2015-06-25 18:56:30 -04:00
return nil
}