diff --git a/pkg/kubectl/cmd/set/set_image.go b/pkg/kubectl/cmd/set/set_image.go index be22e0102a4..9fe5b8a2a38 100644 --- a/pkg/kubectl/cmd/set/set_image.go +++ b/pkg/kubectl/cmd/set/set_image.go @@ -35,21 +35,22 @@ import ( type ImageOptions struct { resource.FilenameOptions - Mapper meta.RESTMapper - Typer runtime.ObjectTyper - Infos []*resource.Info - Encoder runtime.Encoder - 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 + Mapper meta.RESTMapper + Typer runtime.ObjectTyper + Infos []*resource.Info + Encoder runtime.Encoder + 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) PrintObject func(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error UpdatePodSpecForObject func(obj runtime.Object, fn func(*api.PodSpec) error) (bool, error) @@ -120,6 +121,7 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st o.PrintObject = f.PrintObject o.DryRun = cmdutil.GetDryRunFlag(cmd) o.Output = cmdutil.GetFlagString(cmd, "output") + o.ResolveImage = f.ResolveImage o.Cmd = cmd cmdNamespace, enforceNamespace, err := f.DefaultNamespace() @@ -171,12 +173,27 @@ func (o *ImageOptions) Run() error { transformed := false _, err := o.UpdatePodSpecForObject(info.Object, func(spec *api.PodSpec) error { for name, image := range o.ContainerImages { - containerFound := false + var ( + containerFound bool + err error + resolved string + ) // Find the container to update, and update its image for i, c := range spec.Containers { if c.Name == name || name == "*" { - spec.Containers[i].Image = image containerFound = true + 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 + } + } + spec.Containers[i].Image = resolved // Perform updates transformed = true } diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index a08a37a1fe1..34d8295b6cb 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -293,6 +293,10 @@ func (f *FakeFactory) Resumer(info *resource.Info) (bool, error) { return false, nil } +func (f *FakeFactory) ResolveImage(name string) (string, error) { + return name, nil +} + func (f *FakeFactory) Validator(validate bool, cacheDir string) (validation.Schema, error) { return f.tf.Validator, f.tf.Err } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index b160100ad24..c8f70144bf0 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -135,6 +135,10 @@ type Factory interface { Pauser(info *resource.Info) (bool, error) // Resumer resumes a paused object inside the info ie. it will be reconciled by its controller. Resumer(info *resource.Info) (bool, error) + // ResolveImage resolves the image names. For kubernetes this function is just + // passthrough but it allows to perform more sophisticated image name resolving for + // third-party vendors. + ResolveImage(imageName string) (string, error) // Returns a schema that can validate objects stored on disk. Validator(validate bool, cacheDir string) (validation.Schema, error) // SwaggerSchema returns the schema declaration for the provided group version kind. @@ -654,6 +658,10 @@ func (f *factory) Pauser(info *resource.Info) (bool, error) { } } +func (f *factory) ResolveImage(name string) (string, error) { + return name, nil +} + func (f *factory) Resumer(info *resource.Info) (bool, error) { switch obj := info.Object.(type) { case *extensions.Deployment: