diff --git a/pkg/oc/cli/cmd/set/set.go b/pkg/oc/cli/cmd/set/set.go index cc351fdd02a9..99b6028218c6 100644 --- a/pkg/oc/cli/cmd/set/set.go +++ b/pkg/oc/cli/cmd/set/set.go @@ -96,9 +96,6 @@ func NewCmdImage(fullName string, f *clientcmd.Factory, out, err io.Writer) *cob cmd.Long = setImageLong cmd.Example = fmt.Sprintf(setImageExample, fullName) - flags := cmd.Flags() - f.ImageResolutionOptions().Bind(flags) - return cmd } diff --git a/pkg/oc/cli/util/clientcmd/factory_client_access.go b/pkg/oc/cli/util/clientcmd/factory_client_access.go index b520bc49a705..20bd798e9906 100644 --- a/pkg/oc/cli/util/clientcmd/factory_client_access.go +++ b/pkg/oc/cli/util/clientcmd/factory_client_access.go @@ -25,13 +25,14 @@ import ( kapi "k8s.io/kubernetes/pkg/apis/core" kclientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/kubectl/cmd/set" kcmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/util/transport" appsapiv1 "github.com/openshift/api/apps/v1" appsapi "github.com/openshift/origin/pkg/apps/apis/apps" - imageclient "github.com/openshift/origin/pkg/image/generated/internalclientset" + imageapi "github.com/openshift/origin/pkg/image/apis/image" deploymentcmd "github.com/openshift/origin/pkg/oc/cli/deploymentconfigs" routegen "github.com/openshift/origin/pkg/route/generator" ) @@ -40,21 +41,19 @@ type ring0Factory struct { *OpenshiftCLIClientBuilder clientConfig kclientcmd.ClientConfig - imageResolutionOptions FlagBinder kubeClientAccessFactory kcmdutil.ClientAccessFactory } type ClientAccessFactory interface { kcmdutil.ClientAccessFactory CLIClientBuilder - - ImageResolutionOptions() FlagBinder } func NewClientAccessFactory(optionalClientConfig kclientcmd.ClientConfig) ClientAccessFactory { // if we call this factory construction method, we want the openshift style config loading kclientcmd.UseOpenShiftKubeConfigValues = true kclientcmd.ErrEmptyConfig = kclientcmd.NewErrConfigurationMissing() + set.ParseDockerImageReferenceToStringFunc = ParseDockerImageReferenceToStringFunc flags := pflag.NewFlagSet("", pflag.ContinueOnError) clientConfig := optionalClientConfig @@ -62,8 +61,7 @@ func NewClientAccessFactory(optionalClientConfig kclientcmd.ClientConfig) Client clientConfig = kcmdutil.DefaultClientConfig(flags) } factory := &ring0Factory{ - clientConfig: clientConfig, - imageResolutionOptions: &imageResolutionOptions{}, + clientConfig: clientConfig, } factory.kubeClientAccessFactory = kcmdutil.NewClientAccessFactoryFromDiscovery( flags, @@ -276,49 +274,8 @@ func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) { } } -// ImageResolutionOptions provides the "--source" flag to commands that deal with images -// and need to provide extra capabilities for working with ImageStreamTags and -// ImageStreamImages. -type imageResolutionOptions struct { - bound bool - Source string -} - -func (o *imageResolutionOptions) Bound() bool { - return o.bound -} - -func (o *imageResolutionOptions) Bind(f *pflag.FlagSet) { - if o.Bound() { - return - } - f.StringVarP(&o.Source, "source", "", "docker", "The image source type; valid types are 'imagestreamtag', 'istag', 'imagestreamimage', 'isimage', and 'docker'") - o.bound = true -} - -func (f *ring0Factory) ImageResolutionOptions() FlagBinder { - return f.imageResolutionOptions -} - func (f *ring0Factory) ResolveImage(image string) (string, error) { - options := f.imageResolutionOptions.(*imageResolutionOptions) - if isDockerImageSource(options.Source) { - return f.kubeClientAccessFactory.ResolveImage(image) - } - config, err := f.kubeClientAccessFactory.ClientConfig() - if err != nil { - return "", err - } - imageClient, err := imageclient.NewForConfig(config) - if err != nil { - return "", err - } - namespace, _, err := f.DefaultNamespace() - if err != nil { - return "", err - } - - return resolveImagePullSpec(imageClient.Image(), options.Source, image, namespace) + return f.kubeClientAccessFactory.ResolveImage(image) } func (f *ring0Factory) Resumer(info *resource.Info) ([]byte, error) { @@ -416,3 +373,11 @@ func getProtocols(spec kapi.PodSpec) map[string]string { } return result } + +func ParseDockerImageReferenceToStringFunc(spec string) (string, error) { + ret, err := imageapi.ParseDockerImageReference(spec) + if err != nil { + return "", err + } + return ret.String(), nil +} diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/patch_setimage.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/patch_setimage.go new file mode 100644 index 000000000000..76f0cdfcf411 --- /dev/null +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/patch_setimage.go @@ -0,0 +1,107 @@ +package set + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + + imageclient "github.com/openshift/client-go/image/clientset/versioned" + imagetypedclient "github.com/openshift/client-go/image/clientset/versioned/typed/image/v1" +) + +// this relies on internal APIs that we don't access to in kubectl +var ParseDockerImageReferenceToStringFunc func(spec string) (string, error) + +type imageResolverFunc func(in string) (string, error) + +func resolveImageFactory(f cmdutil.Factory, cmd *cobra.Command) imageResolverFunc { + source, err := cmd.Flags().GetString("source") + if err != nil { + return f.ResolveImage + } + + return func(image string) (string, error) { + if isDockerImageSource(source) { + return f.ResolveImage(image) + } + config, err := f.ClientConfig() + if err != nil { + return "", err + } + imageClient, err := imageclient.NewForConfig(config) + if err != nil { + return "", err + } + namespace, _, err := f.DefaultNamespace() + if err != nil { + return "", err + } + + return resolveImagePullSpec(imageClient.ImageV1(), source, image, namespace) + } +} + +// resolveImagePullSpec resolves the provided source which can be "docker", "istag" or +// "isimage" and returns the full Docker pull spec. +func resolveImagePullSpec(imageClient imagetypedclient.ImageV1Interface, source, name, defaultNamespace string) (string, error) { + // for Docker source, just passtrough the image name + if isDockerImageSource(source) { + return name, nil + } + // parse the namespace from the provided image + namespace, image := splitNamespaceAndImage(name) + if len(namespace) == 0 { + namespace = defaultNamespace + } + + dockerImageReference := "" + + if isImageStreamTag(source) { + if resolved, err := imageClient.ImageStreamTags(namespace).Get(image, metav1.GetOptions{}); err != nil { + return "", fmt.Errorf("failed to get image stream tag %q: %v", image, err) + } else { + dockerImageReference = resolved.Image.DockerImageReference + } + } + + if isImageStreamImage(source) { + if resolved, err := imageClient.ImageStreamImages(namespace).Get(image, metav1.GetOptions{}); err != nil { + return "", fmt.Errorf("failed to get image stream image %q: %v", image, err) + } else { + dockerImageReference = resolved.Image.DockerImageReference + } + } + + if len(dockerImageReference) == 0 { + return "", fmt.Errorf("unable to resolve %s %q", source, name) + } + + return ParseDockerImageReferenceToStringFunc(dockerImageReference) +} + +func isDockerImageSource(source string) bool { + return source == "docker" +} + +func isImageStreamTag(source string) bool { + return source == "istag" || source == "imagestreamtag" +} + +func isImageStreamImage(source string) bool { + return source == "isimage" || source == "imagestreamimage" +} + +func splitNamespaceAndImage(name string) (string, string) { + namespace := "" + imageName := "" + if parts := strings.Split(name, "/"); len(parts) == 2 { + namespace, imageName = parts[0], parts[1] + } else if len(parts) == 1 { + imageName = parts[0] + } + return namespace, imageName +} diff --git a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/set_image.go b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/set_image.go index 7d9ac6a8e0f1..aae2a38efc99 100644 --- a/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/set_image.go +++ b/vendor/k8s.io/kubernetes/pkg/kubectl/cmd/set/set_image.go @@ -53,6 +53,8 @@ type ImageOptions struct { UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) Resources []string ContainerImages map[string]string + + Source string } var ( @@ -98,6 +100,9 @@ func NewCmdImage(f cmdutil.Factory, out, err io.Writer) *cobra.Command { }, } + options.Source = "docker" + cmd.Flags().StringVar(&options.Source, "source", options.Source, "The image source type; valid types are 'imagestreamtag', 'istag', 'imagestreamimage', 'isimage', and 'docker'") + cmdutil.AddPrinterFlags(cmd) usage := "identifying the resource to get from a server." cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) @@ -117,7 +122,7 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st o.ChangeCause = f.Command(cmd, false) o.DryRun = cmdutil.GetDryRunFlag(cmd) o.Output = cmdutil.GetFlagString(cmd, "output") - o.ResolveImage = f.ResolveImage + o.ResolveImage = resolveImageFactory(f, cmd) o.Cmd = cmd cmdNamespace, enforceNamespace, err := f.DefaultNamespace()