2016-05-11 00:26:39 +00:00
/ *
2016-06-03 00:25:58 +00:00
Copyright 2016 The Kubernetes Authors .
2016-05-11 00:26:39 +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 .
* /
package set
import (
"fmt"
"io"
"github.com/spf13/cobra"
2017-11-10 18:36:12 +00:00
"k8s.io/api/core/v1"
2017-01-11 14:09:48 +00:00
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
2017-01-16 20:13:59 +00:00
"k8s.io/apimachinery/pkg/types"
2017-01-11 14:09:48 +00:00
utilerrors "k8s.io/apimachinery/pkg/util/errors"
2016-10-07 22:24:42 +00:00
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
2016-05-11 00:26:39 +00:00
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
2017-07-07 04:04:11 +00:00
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
2016-05-11 00:26:39 +00:00
)
// ImageOptions 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 ImageOptions struct {
2016-08-17 18:28:07 +00:00
resource . FilenameOptions
2016-09-20 12:19:01 +00:00
Mapper meta . RESTMapper
Infos [ ] * resource . Info
Encoder runtime . Encoder
2017-11-10 18:36:12 +00:00
Decoder runtime . Decoder
2016-09-20 12:19:01 +00:00
Selector string
Out io . Writer
Err io . Writer
DryRun bool
ShortOutput bool
All bool
Record bool
Output string
ChangeCause string
Local bool
Cmd * cobra . Command
ResolveImage func ( in string ) ( string , error )
2016-05-11 00:26:39 +00:00
2017-10-31 15:58:38 +00:00
PrintSuccess func ( mapper meta . RESTMapper , shortOutput bool , out io . Writer , resource , name string , dryRun bool , operation string )
2017-05-16 21:52:51 +00:00
PrintObject func ( cmd * cobra . Command , isLocal bool , mapper meta . RESTMapper , obj runtime . Object , out io . Writer ) error
2017-11-10 18:36:12 +00:00
UpdatePodSpecForObject func ( obj runtime . Object , fn func ( * v1 . PodSpec ) error ) ( bool , error )
2016-05-11 00:26:39 +00:00
Resources [ ] string
ContainerImages map [ string ] string
}
2016-05-20 17:49:56 +00:00
var (
2016-05-11 00:26:39 +00:00
image_resources = `
2017-09-08 12:16:40 +00:00
pod ( po ) , replicationcontroller ( rc ) , deployment ( deploy ) , daemonset ( ds ) , replicaset ( rs ) `
2016-05-11 00:26:39 +00:00
2016-10-07 22:24:42 +00:00
image_long = templates . LongDesc ( `
2016-05-20 17:49:56 +00:00
Update existing container image ( s ) of resources .
2016-05-11 00:26:39 +00:00
2016-10-07 22:24:42 +00:00
Possible resources include ( case insensitive ) :
` + image_resources )
2016-05-11 00:26:39 +00:00
2016-10-07 22:24:42 +00:00
image_example = templates . Examples ( `
2016-05-20 17:49:56 +00:00
# Set a deployment ' s nginx container image to ' nginx : 1.9 .1 ' , and its busybox container image to ' busybox ' .
kubectl set image deployment / nginx busybox = busybox nginx = nginx : 1.9 .1
2016-05-11 00:26:39 +00:00
2016-05-20 17:49:56 +00:00
# Update all deployments ' and rc ' s nginx container ' s image to ' nginx : 1.9 .1 '
kubectl set image deployments , rc nginx = nginx : 1.9 .1 -- all
2016-05-11 00:26:39 +00:00
2016-05-20 17:49:56 +00:00
# Update image of all containers of daemonset abc to ' nginx : 1.9 .1 '
kubectl set image daemonset abc *= nginx : 1.9 .1
2016-05-11 00:26:39 +00:00
2016-10-07 22:24:42 +00:00
# Print result ( in yaml format ) of updating nginx container image from local file , without hitting the server
2016-05-20 17:49:56 +00:00
kubectl set image - f path / to / file . yaml nginx = nginx : 1.9 .1 -- local - o yaml ` )
2016-05-11 00:26:39 +00:00
)
2016-10-10 07:01:27 +00:00
func NewCmdImage ( f cmdutil . Factory , out , err io . Writer ) * cobra . Command {
2016-05-11 00:26:39 +00:00
options := & ImageOptions {
Out : out ,
2016-10-10 07:01:27 +00:00
Err : err ,
2016-05-11 00:26:39 +00:00
}
cmd := & cobra . Command {
Use : "image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N" ,
2017-01-26 06:21:28 +00:00
Short : i18n . T ( "Update image of a pod template" ) ,
2016-05-11 00:26:39 +00:00
Long : image_long ,
Example : image_example ,
Run : func ( cmd * cobra . Command , args [ ] string ) {
cmdutil . CheckErr ( options . Complete ( f , cmd , args ) )
cmdutil . CheckErr ( options . Validate ( ) )
cmdutil . CheckErr ( options . Run ( ) )
} ,
}
cmdutil . AddPrinterFlags ( cmd )
2016-08-17 18:28:07 +00:00
usage := "identifying the resource to get from a server."
cmdutil . AddFilenameOptionFlags ( cmd , & options . FilenameOptions , usage )
2017-08-11 06:21:44 +00:00
cmd . Flags ( ) . BoolVar ( & options . All , "all" , false , "Select all resources, including uninitialized ones, in the namespace of the specified resource types" )
cmd . Flags ( ) . StringVarP ( & options . Selector , "selector" , "l" , "" , "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)" )
2016-05-11 00:26:39 +00:00
cmd . Flags ( ) . BoolVar ( & options . Local , "local" , false , "If true, set image will NOT contact api-server but run locally." )
cmdutil . AddRecordFlag ( cmd )
2016-11-07 15:05:02 +00:00
cmdutil . AddDryRunFlag ( cmd )
2017-08-11 06:21:44 +00:00
cmdutil . AddIncludeUninitializedFlag ( cmd )
2016-05-11 00:26:39 +00:00
return cmd
}
2016-10-13 00:18:39 +00:00
func ( o * ImageOptions ) Complete ( f cmdutil . Factory , cmd * cobra . Command , args [ ] string ) error {
2017-11-10 18:36:12 +00:00
o . Mapper , _ = f . Object ( )
2017-10-31 15:58:38 +00:00
o . PrintSuccess = f . PrintSuccess
2016-05-11 00:26:39 +00:00
o . UpdatePodSpecForObject = f . UpdatePodSpecForObject
o . Encoder = f . JSONEncoder ( )
2017-11-10 18:36:12 +00:00
o . Decoder = f . Decoder ( true )
2016-05-11 00:26:39 +00:00
o . ShortOutput = cmdutil . GetFlagString ( cmd , "output" ) == "name"
o . Record = cmdutil . GetRecordFlag ( cmd )
2017-02-25 15:40:50 +00:00
o . ChangeCause = f . Command ( cmd , false )
2016-05-11 00:26:39 +00:00
o . PrintObject = f . PrintObject
2017-05-03 14:04:21 +00:00
o . Local = cmdutil . GetFlagBool ( cmd , "local" )
2016-11-07 15:05:02 +00:00
o . DryRun = cmdutil . GetDryRunFlag ( cmd )
o . Output = cmdutil . GetFlagString ( cmd , "output" )
2016-09-20 12:19:01 +00:00
o . ResolveImage = f . ResolveImage
2016-05-11 00:26:39 +00:00
o . Cmd = cmd
cmdNamespace , enforceNamespace , err := f . DefaultNamespace ( )
if err != nil {
return err
}
o . Resources , o . ContainerImages , err = getResourcesAndImages ( args )
if err != nil {
return err
}
2017-08-11 06:21:44 +00:00
includeUninitialized := cmdutil . ShouldIncludeUninitialized ( cmd , false )
2017-08-02 20:23:07 +00:00
builder := f . NewBuilder ( ) .
2016-05-11 00:26:39 +00:00
ContinueOnError ( ) .
NamespaceParam ( cmdNamespace ) . DefaultNamespace ( ) .
2016-08-17 18:28:07 +00:00
FilenameParam ( enforceNamespace , & o . FilenameOptions ) .
2017-08-11 06:21:44 +00:00
IncludeUninitialized ( includeUninitialized ) .
2016-05-11 00:26:39 +00:00
Flatten ( )
2017-08-02 20:23:07 +00:00
2017-11-08 01:42:03 +00:00
if o . Local {
2017-07-11 14:19:27 +00:00
// if a --local flag was provided, and a resource was specified in the form
// <resource>/<name>, fail immediately as --local cannot query the api server
// for the specified resource.
if len ( o . Resources ) > 0 {
return resource . LocalResourceError
}
2017-08-02 20:23:07 +00:00
builder = builder . Local ( f . ClientForMapping )
2017-11-08 01:42:03 +00:00
} else {
builder = builder .
LabelSelectorParam ( o . Selector ) .
ResourceTypeOrNameArgs ( o . All , o . Resources ... ) .
Latest ( )
2016-05-11 00:26:39 +00:00
}
2017-07-11 14:19:27 +00:00
2016-05-11 00:26:39 +00:00
o . Infos , err = builder . Do ( ) . Infos ( )
if err != nil {
return err
}
return nil
}
func ( o * ImageOptions ) Validate ( ) error {
2016-08-29 09:17:51 +00:00
errors := [ ] error { }
2017-06-14 21:14:42 +00:00
if len ( o . Resources ) < 1 && cmdutil . IsFilenameSliceEmpty ( o . Filenames ) {
2016-08-29 09:17:51 +00:00
errors = append ( errors , fmt . Errorf ( "one or more resources must be specified as <resource> <name> or <resource>/<name>" ) )
2016-05-11 00:26:39 +00:00
}
if len ( o . ContainerImages ) < 1 {
2016-08-29 09:17:51 +00:00
errors = append ( errors , fmt . Errorf ( "at least one image update is required" ) )
2016-05-11 00:26:39 +00:00
} else if len ( o . ContainerImages ) > 1 && hasWildcardKey ( o . ContainerImages ) {
2016-08-29 09:17:51 +00:00
errors = append ( errors , fmt . Errorf ( "all containers are already specified by *, but saw more than one container_name=container_image pairs" ) )
2016-05-11 00:26:39 +00:00
}
2016-08-29 09:17:51 +00:00
return utilerrors . NewAggregate ( errors )
2016-05-11 00:26:39 +00:00
}
func ( o * ImageOptions ) Run ( ) error {
allErrs := [ ] error { }
2016-11-23 05:02:30 +00:00
2016-11-14 11:53:27 +00:00
patches := CalculatePatches ( o . Infos , o . Encoder , func ( info * resource . Info ) ( [ ] byte , error ) {
2016-05-11 00:26:39 +00:00
transformed := false
2017-11-10 18:36:12 +00:00
_ , err := o . UpdatePodSpecForObject ( info . VersionedObject , func ( spec * v1 . PodSpec ) error {
2016-05-11 00:26:39 +00:00
for name , image := range o . ContainerImages {
2016-09-20 12:19:01 +00:00
var (
containerFound bool
err error
resolved string
)
2016-05-11 00:26:39 +00:00
// Find the container to update, and update its image
for i , c := range spec . Containers {
if c . Name == name || name == "*" {
containerFound = true
2016-09-20 12:19:01 +00:00
if len ( resolved ) == 0 {
if resolved , err = o . ResolveImage ( image ) ; err != nil {
allErrs = append ( allErrs , fmt . Errorf ( "error: unable to resolve image %q for container %q: %v" , image , name , err ) )
// Do not loop again if the image resolving failed for wildcard case as we
// will report the same error again for the next container.
if name == "*" {
break
}
continue
}
}
2017-08-17 03:19:14 +00:00
if spec . Containers [ i ] . Image != resolved {
spec . Containers [ i ] . Image = resolved
// Perform updates
transformed = true
}
2016-05-11 00:26:39 +00:00
}
}
// Add a new container if not found
if ! containerFound {
allErrs = append ( allErrs , fmt . Errorf ( "error: unable to find container named %q" , name ) )
}
}
return nil
} )
2016-11-14 11:53:27 +00:00
if transformed && err == nil {
2017-11-10 18:36:12 +00:00
return runtime . Encode ( o . Encoder , info . VersionedObject )
2016-11-14 11:53:27 +00:00
}
return nil , err
2016-05-11 00:26:39 +00:00
} )
for _ , patch := range patches {
info := patch . Info
if patch . Err != nil {
allErrs = append ( allErrs , fmt . Errorf ( "error: %s/%s %v\n" , info . Mapping . Resource , info . Name , patch . Err ) )
continue
}
// no changes
if string ( patch . Patch ) == "{}" || len ( patch . Patch ) == 0 {
continue
}
2016-11-07 15:05:02 +00:00
if o . PrintObject != nil && ( o . Local || o . DryRun ) {
2017-11-10 18:36:12 +00:00
if err := o . PrintObject ( o . Cmd , o . Local , o . Mapper , patch . Info . VersionedObject , o . Out ) ; err != nil {
2017-08-17 11:56:25 +00:00
return err
}
continue
2016-05-11 00:26:39 +00:00
}
// patch the change
2017-01-16 20:13:59 +00:00
obj , err := resource . NewHelper ( info . Client , info . Mapping ) . Patch ( info . Namespace , info . Name , types . StrategicMergePatchType , patch . Patch )
2016-05-11 00:26:39 +00:00
if err != nil {
allErrs = append ( allErrs , fmt . Errorf ( "failed to patch image update to pod template: %v\n" , err ) )
continue
}
info . Refresh ( obj , true )
// record this change (for rollout history)
if o . Record || cmdutil . ContainsChangeCause ( info ) {
2017-01-23 02:15:10 +00:00
if patch , patchType , err := cmdutil . ChangeResourcePatch ( info , o . ChangeCause ) ; err == nil {
if obj , err = resource . NewHelper ( info . Client , info . Mapping ) . Patch ( info . Namespace , info . Name , patchType , patch ) ; err != nil {
2016-07-20 17:20:03 +00:00
fmt . Fprintf ( o . Err , "WARNING: changes to %s/%s can't be recorded: %v\n" , info . Mapping . Resource , info . Name , err )
2016-05-11 00:26:39 +00:00
}
}
}
info . Refresh ( obj , true )
2016-11-07 15:05:02 +00:00
if len ( o . Output ) > 0 {
2017-11-10 18:36:12 +00:00
versionedObject , err := patch . Info . Mapping . ConvertToVersion ( obj , patch . Info . Mapping . GroupVersionKind . GroupVersion ( ) )
if err != nil {
return err
}
if err := o . PrintObject ( o . Cmd , o . Local , o . Mapper , versionedObject , o . Out ) ; err != nil {
2017-08-17 11:56:25 +00:00
return err
}
continue
2016-11-07 15:05:02 +00:00
}
2017-10-31 15:58:38 +00:00
o . PrintSuccess ( o . Mapper , o . ShortOutput , o . Out , info . Mapping . Resource , info . Name , o . DryRun , "image updated" )
2016-05-11 00:26:39 +00:00
}
return utilerrors . NewAggregate ( allErrs )
}
// getResourcesAndImages retrieves resources and container name:images pair from given args
func getResourcesAndImages ( args [ ] string ) ( resources [ ] string , containerImages map [ string ] string , err error ) {
pairType := "image"
resources , imageArgs , err := cmdutil . GetResourcesAndPairs ( args , pairType )
if err != nil {
return
}
containerImages , _ , err = cmdutil . ParsePairs ( imageArgs , pairType , false )
return
}
func hasWildcardKey ( containerImages map [ string ] string ) bool {
_ , ok := containerImages [ "*" ]
return ok
}