Example #1
0
func (o *ImportImageOptions) importTag(stream *imageapi.ImageStream) (*imageapi.ImageStreamImport, error) {
	from := o.From
	tag := o.Tag
	// follow any referential tags to the destination
	finalTag, existing, ok, multiple := imageapi.FollowTagReference(stream, tag)
	if !ok && multiple {
		return nil, fmt.Errorf("tag %q on the image stream is a reference to %q, which does not exist", tag, finalTag)
	}

	// update ImageStream appropriately
	if ok {
		// disallow re-importing anything other than DockerImage
		if existing.From != nil && existing.From.Kind != "DockerImage" {
			return nil, fmt.Errorf("tag %q points to existing %s %q, it cannot be re-imported", tag, existing.From.Kind, existing.From.Name)
		}
		// disallow changing an existing tag
		if existing.From == nil {
			return nil, fmt.Errorf("tag %q already exists - you must use the 'tag' command if you want to change the source to %q", tag, from)
		}
		if len(from) != 0 && from != existing.From.Name {
			if multiple {
				return nil, fmt.Errorf("the tag %q points to the tag %q which points to %q - use the 'tag' command if you want to change the source to %q", tag, finalTag, existing.From.Name, from)
			}
			return nil, fmt.Errorf("the tag %q points to %q - use the 'tag' command if you want to change the source to %q", tag, existing.From.Name, from)
		}

		// set the target item to import
		from = existing.From.Name
		if multiple {
			tag = finalTag
		}

		// clear the legacy annotation
		delete(existing.Annotations, imageapi.DockerImageRepositoryCheckAnnotation)
		// reset the generation
		zero := int64(0)
		existing.Generation = &zero

	} else {
		// create a new tag
		if len(from) == 0 {
			from = stream.Spec.DockerImageRepository
		}
		// if the from is still empty this means there's no such tag defined
		// nor we can't create any from .spec.dockerImageRepository
		if len(from) == 0 {
			return nil, fmt.Errorf("the tag %q does not exist on the image stream - choose an existing tag to import or use the 'tag' command to create a new tag", tag)
		}
		existing = &imageapi.TagReference{
			From: &kapi.ObjectReference{
				Kind: "DockerImage",
				Name: from,
			},
		}
	}
	stream.Spec.Tags[tag] = *existing

	// and create accompanying ImageStreamImport
	return o.newImageStreamImportTags(stream, map[string]string{tag: from}), nil
}
Example #2
0
// RunImportImage contains all the necessary functionality for the OpenShift cli import-image command.
func RunImportImage(f *clientcmd.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
	if len(args) == 0 || len(args[0]) == 0 {
		return cmdutil.UsageError(cmd, "you must specify the name of an image stream")
	}

	target := args[0]
	namespace, _, err := f.DefaultNamespace()
	if err != nil {
		return err
	}

	osClient, _, err := f.Clients()
	if err != nil {
		return err
	}

	insecure := cmdutil.GetFlagBool(cmd, "insecure")
	from := cmdutil.GetFlagString(cmd, "from")
	confirm := cmdutil.GetFlagBool(cmd, "confirm")
	all := cmdutil.GetFlagBool(cmd, "all")

	targetRef, err := imageapi.ParseDockerImageReference(target)
	switch {
	case err != nil:
		return fmt.Errorf("the image name must be a valid Docker image pull spec or reference to an image stream (e.g. myregistry/myteam/image:tag)")
	case len(targetRef.ID) > 0:
		return fmt.Errorf("to import images by ID, use the 'tag' command")
	case len(targetRef.Tag) != 0 && all:
		// error out
		return fmt.Errorf("cannot specify a tag %q as well as --all", target)
	case len(targetRef.Tag) == 0 && !all:
		// apply the default tag
		targetRef.Tag = imageapi.DefaultImageTag
	}
	name := targetRef.Name
	tag := targetRef.Tag

	imageStreamClient := osClient.ImageStreams(namespace)
	stream, err := imageStreamClient.Get(name)
	if err != nil {
		if !errors.IsNotFound(err) {
			return err
		}

		// the stream is new
		if !confirm {
			return fmt.Errorf("no image stream named %q exists, pass --confirm to create and import", name)
		}
		if len(from) == 0 {
			from = target
		}
		if all {
			stream = &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: name},
				Spec:       imageapi.ImageStreamSpec{DockerImageRepository: from},
			}
		} else {
			stream = &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: name},
				Spec: imageapi.ImageStreamSpec{
					Tags: map[string]imageapi.TagReference{
						tag: {
							From: &kapi.ObjectReference{
								Kind: "DockerImage",
								Name: from,
							},
						},
					},
				},
			}
		}

	} else {
		// the stream already exists
		if len(stream.Spec.DockerImageRepository) == 0 && len(stream.Spec.Tags) == 0 {
			return fmt.Errorf("image stream has not defined anything to import")
		}

		if all {
			// importing a whole repository
			if len(from) == 0 {
				from = target
			}
			if from != stream.Spec.DockerImageRepository {
				if !confirm {
					if len(stream.Spec.DockerImageRepository) == 0 {
						return fmt.Errorf("the image stream does not currently import an entire Docker repository, pass --confirm to update")
					}
					return fmt.Errorf("the image stream has a different import spec %q, pass --confirm to update", stream.Spec.DockerImageRepository)
				}
				stream.Spec.DockerImageRepository = from
			}

		} else {
			// importing a single tag

			// follow any referential tags to the destination
			finalTag, existing, ok, multiple := imageapi.FollowTagReference(stream, tag)
			if !ok && multiple {
				return fmt.Errorf("tag %q on the image stream is a reference to %q, which does not exist", tag, finalTag)
			}

			if ok {
				// disallow changing an existing tag
				if existing.From == nil || existing.From.Kind != "DockerImage" {
					return fmt.Errorf("tag %q already exists - you must use the 'tag' command if you want to change the source to %q", tag, from)
				}
				if len(from) != 0 && from != existing.From.Name {
					if multiple {
						return fmt.Errorf("the tag %q points to the tag %q which points to %q - use the 'tag' command if you want to change the source to %q", tag, finalTag, existing.From.Name, from)
					}
					return fmt.Errorf("the tag %q points to %q - use the 'tag' command if you want to change the source to %q", tag, existing.From.Name, from)
				}

				// set the target item to import
				from = existing.From.Name
				if multiple {
					tag = finalTag
				}

				// clear the legacy annotation
				delete(existing.Annotations, imageapi.DockerImageRepositoryCheckAnnotation)
				// reset the generation
				zero := int64(0)
				existing.Generation = &zero

			} else {
				// create a new tag
				if len(from) == 0 {
					from = target
				}
				existing = &imageapi.TagReference{
					From: &kapi.ObjectReference{
						Kind: "DockerImage",
						Name: from,
					},
				}
			}
			stream.Spec.Tags[tag] = *existing
		}
	}

	if len(from) == 0 {
		// catch programmer error
		return fmt.Errorf("unexpected error, from is empty")
	}

	// Attempt the new, direct import path
	isi := &imageapi.ImageStreamImport{
		ObjectMeta: kapi.ObjectMeta{
			Name:            stream.Name,
			Namespace:       namespace,
			ResourceVersion: stream.ResourceVersion,
		},
		Spec: imageapi.ImageStreamImportSpec{Import: true},
	}
	if all {
		isi.Spec.Repository = &imageapi.RepositoryImportSpec{
			From: kapi.ObjectReference{
				Kind: "DockerImage",
				Name: from,
			},
			ImportPolicy: imageapi.TagImportPolicy{Insecure: insecure},
		}
	} else {
		isi.Spec.Images = append(isi.Spec.Images, imageapi.ImageImportSpec{
			From: kapi.ObjectReference{
				Kind: "DockerImage",
				Name: from,
			},
			To:           &kapi.LocalObjectReference{Name: tag},
			ImportPolicy: imageapi.TagImportPolicy{Insecure: insecure},
		})
	}

	// TODO: add dry-run
	_, err = imageStreamClient.Import(isi)
	switch {
	case err == client.ErrImageStreamImportUnsupported:
	case err != nil:
		return err
	default:
		fmt.Fprint(cmd.Out(), "The import completed successfully.\n\n")

		// optimization, use the image stream returned by the call
		d := describe.ImageStreamDescriber{Interface: osClient}
		info, err := d.Describe(namespace, stream.Name)
		if err != nil {
			return err
		}

		fmt.Fprintln(out, info)
		return nil
	}

	// Legacy path, remove when support for older importers is removed
	delete(stream.Annotations, imageapi.DockerImageRepositoryCheckAnnotation)
	if insecure {
		if stream.Annotations == nil {
			stream.Annotations = make(map[string]string)
		}
		stream.Annotations[imageapi.InsecureRepositoryAnnotation] = "true"
	}

	if stream.CreationTimestamp.IsZero() {
		stream, err = imageStreamClient.Create(stream)
	} else {
		stream, err = imageStreamClient.Update(stream)
	}
	if err != nil {
		return err
	}

	resourceVersion := stream.ResourceVersion

	fmt.Fprintln(cmd.Out(), "Importing (ctrl+c to stop waiting) ...")

	updatedStream, err := waitForImport(imageStreamClient, stream.Name, resourceVersion)
	if err != nil {
		if _, ok := err.(importError); ok {
			return err
		}
		return fmt.Errorf("unable to determine if the import completed successfully - please run 'oc describe -n %s imagestream/%s' to see if the tags were updated as expected: %v", stream.Namespace, stream.Name, err)
	}

	fmt.Fprint(cmd.Out(), "The import completed successfully.\n\n")

	d := describe.ImageStreamDescriber{Interface: osClient}
	info, err := d.Describe(updatedStream.Namespace, updatedStream.Name)
	if err != nil {
		return err
	}

	fmt.Fprintln(out, info)
	return nil
}
Example #3
0
func formatImageStreamTags(out *tabwriter.Writer, stream *imageapi.ImageStream) {
	if len(stream.Status.Tags) == 0 && len(stream.Spec.Tags) == 0 {
		fmt.Fprintf(out, "Tags:\t<none>\n")
		return
	}

	now := timeNowFn()

	images := make(map[string]string)
	for tag, tags := range stream.Status.Tags {
		for _, item := range tags.Items {
			switch {
			case len(item.Image) > 0:
				if _, ok := images[item.Image]; !ok {
					images[item.Image] = tag
				}
			case len(item.DockerImageReference) > 0:
				if _, ok := images[item.DockerImageReference]; !ok {
					images[item.Image] = item.DockerImageReference
				}
			}
		}
	}

	sortedTags := []string{}
	for k := range stream.Status.Tags {
		sortedTags = append(sortedTags, k)
	}
	var localReferences sets.String
	var referentialTags map[string]sets.String
	for k := range stream.Spec.Tags {
		if target, _, ok, multiple := imageapi.FollowTagReference(stream, k); ok && multiple {
			if referentialTags == nil {
				referentialTags = make(map[string]sets.String)
			}
			if localReferences == nil {
				localReferences = sets.NewString()
			}
			localReferences.Insert(k)
			v := referentialTags[target]
			if v == nil {
				v = sets.NewString()
				referentialTags[target] = v
			}
			v.Insert(k)
		}
		if _, ok := stream.Status.Tags[k]; !ok {
			sortedTags = append(sortedTags, k)
		}
	}
	fmt.Fprintf(out, "Unique Images:\t%d\nTags:\t%d\n\n", len(images), len(sortedTags))

	first := true
	imageapi.PrioritizeTags(sortedTags)
	for _, tag := range sortedTags {
		if localReferences.Has(tag) {
			continue
		}
		if first {
			first = false
		} else {
			fmt.Fprintf(out, "\n")
		}
		taglist, _ := stream.Status.Tags[tag]
		tagRef, hasSpecTag := stream.Spec.Tags[tag]
		scheduled := false
		insecure := false
		importing := false

		var name string
		if hasSpecTag && tagRef.From != nil {
			if len(tagRef.From.Namespace) > 0 && tagRef.From.Namespace != stream.Namespace {
				name = fmt.Sprintf("%s/%s", tagRef.From.Namespace, tagRef.From.Name)
			} else {
				name = tagRef.From.Name
			}
			scheduled, insecure = tagRef.ImportPolicy.Scheduled, tagRef.ImportPolicy.Insecure
			gen := imageapi.LatestObservedTagGeneration(stream, tag)
			importing = !tagRef.Reference && tagRef.Generation != nil && *tagRef.Generation != gen
		}

		//   updates whenever tag :5.2 is changed

		// :latest (30 minutes ago) -> 102.205.358.453/foo/bar@sha256:abcde734
		//   error: last import failed 20 minutes ago
		//   updates automatically from index.docker.io/mysql/bar
		//     will use insecure HTTPS connections or HTTP
		//
		//   MySQL 5.5
		//   ---------
		//   Describes a system for updating based on practical changes to a database system
		//   with some other data involved
		//
		//   20 minutes ago  <import failed>
		//	  	Failed to locate the server in time
		//   30 minutes ago  102.205.358.453/foo/bar@sha256:abcdef
		//   1 hour ago      102.205.358.453/foo/bar@sha256:bfedfc

		//var shortErrors []string
		/*
			var internalReference *imageapi.DockerImageReference
			if value := stream.Status.DockerImageRepository; len(value) > 0 {
				ref, err := imageapi.ParseDockerImageReference(value)
				if err != nil {
					internalReference = &ref
				}
			}
		*/

		if referentialTags[tag].Len() > 0 {
			references := referentialTags[tag].List()
			imageapi.PrioritizeTags(references)
			fmt.Fprintf(out, "%s (%s)\n", tag, strings.Join(references, ", "))
		} else {
			fmt.Fprintf(out, "%s\n", tag)
		}

		switch {
		case !hasSpecTag || tagRef.From == nil:
			fmt.Fprintf(out, "  pushed image\n")
		case tagRef.From.Kind == "ImageStreamTag":
			switch {
			case tagRef.Reference:
				fmt.Fprintf(out, "  reference to %s\n", name)
			case scheduled:
				fmt.Fprintf(out, "  updates automatically from %s\n", name)
			default:
				fmt.Fprintf(out, "  tagged from %s\n", name)
			}
		case tagRef.From.Kind == "DockerImage":
			switch {
			case tagRef.Reference:
				fmt.Fprintf(out, "  reference to registry %s\n", name)
			case scheduled:
				fmt.Fprintf(out, "  updates automatically from registry %s\n", name)
			default:
				fmt.Fprintf(out, "  tagged from %s\n", name)
			}
		case tagRef.From.Kind == "ImageStreamImage":
			switch {
			case tagRef.Reference:
				fmt.Fprintf(out, "  reference to image %s\n", name)
			default:
				fmt.Fprintf(out, "  tagged from %s\n", name)
			}
		default:
			switch {
			case tagRef.Reference:
				fmt.Fprintf(out, "  reference to %s %s\n", tagRef.From.Kind, name)
			default:
				fmt.Fprintf(out, "  updates from %s %s\n", tagRef.From.Kind, name)
			}
		}
		if insecure {
			fmt.Fprintf(out, "    will use insecure HTTPS or HTTP connections\n")
		}

		fmt.Fprintln(out)

		extraOutput := false
		if d := tagRef.Annotations["description"]; len(d) > 0 {
			fmt.Fprintf(out, "  %s\n", d)
			extraOutput = true
		}
		if t := tagRef.Annotations["tags"]; len(t) > 0 {
			fmt.Fprintf(out, "  Tags: %s\n", strings.Join(strings.Split(t, ","), ", "))
			extraOutput = true
		}
		if t := tagRef.Annotations["supports"]; len(t) > 0 {
			fmt.Fprintf(out, "  Supports: %s\n", strings.Join(strings.Split(t, ","), ", "))
			extraOutput = true
		}
		if t := tagRef.Annotations["sampleRepo"]; len(t) > 0 {
			fmt.Fprintf(out, "  Example Repo: %s\n", t)
			extraOutput = true
		}
		if extraOutput {
			fmt.Fprintln(out)
		}

		if importing {
			fmt.Fprintf(out, "  ~ importing latest image ...\n")
		}

		for i := range taglist.Conditions {
			condition := &taglist.Conditions[i]
			switch condition.Type {
			case imageapi.ImportSuccess:
				if condition.Status == api.ConditionFalse {
					d := now.Sub(condition.LastTransitionTime.Time)
					fmt.Fprintf(out, "  ! error: Import failed (%s): %s\n      %s ago\n", condition.Reason, condition.Message, units.HumanDuration(d))
				}
			}
		}

		if len(taglist.Items) == 0 {
			continue
		}

		for i, event := range taglist.Items {
			d := now.Sub(event.Created.Time)

			if i == 0 {
				fmt.Fprintf(out, "  * %s\n", event.DockerImageReference)
			} else {
				fmt.Fprintf(out, "    %s\n", event.DockerImageReference)
			}

			ref, err := imageapi.ParseDockerImageReference(event.DockerImageReference)
			id := event.Image
			if len(id) > 0 && err == nil && ref.ID != id {
				fmt.Fprintf(out, "      %s ago\t%s\n", units.HumanDuration(d), id)
			} else {
				fmt.Fprintf(out, "      %s ago\n", units.HumanDuration(d))
			}
		}
	}
}
Example #4
0
func (o *ImportImageOptions) createImageImport() (*imageapi.ImageStream, *imageapi.ImageStreamImport, error) {
	stream, err := o.isClient.Get(o.Name)
	from := o.From
	tag := o.Tag
	if err != nil {
		if !errors.IsNotFound(err) {
			return nil, nil, err
		}

		// the stream is new
		if !o.Confirm {
			return nil, nil, fmt.Errorf("no image stream named %q exists, pass --confirm to create and import", o.Name)
		}
		if len(from) == 0 {
			from = o.Target
		}
		if o.All {
			stream = &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: o.Name},
				Spec:       imageapi.ImageStreamSpec{DockerImageRepository: from},
			}
		} else {
			stream = &imageapi.ImageStream{
				ObjectMeta: kapi.ObjectMeta{Name: o.Name},
				Spec: imageapi.ImageStreamSpec{
					Tags: map[string]imageapi.TagReference{
						tag: {
							From: &kapi.ObjectReference{
								Kind: "DockerImage",
								Name: from,
							},
						},
					},
				},
			}
		}

	} else {
		// the stream already exists
		if len(stream.Spec.DockerImageRepository) == 0 && len(stream.Spec.Tags) == 0 {
			return nil, nil, fmt.Errorf("image stream has not defined anything to import")
		}

		if o.All {
			// importing a whole repository
			// TODO soltysh: write tests to cover all the possible usecases!!!
			if len(from) == 0 {
				if len(stream.Spec.DockerImageRepository) == 0 {
					// FIXME soltysh:
					return nil, nil, fmt.Errorf("flag --all is applicable only to images with spec.dockerImageRepository defined")
				}
				from = stream.Spec.DockerImageRepository
			}
			if from != stream.Spec.DockerImageRepository {
				if !o.Confirm {
					if len(stream.Spec.DockerImageRepository) == 0 {
						return nil, nil, fmt.Errorf("the image stream does not currently import an entire Docker repository, pass --confirm to update")
					}
					return nil, nil, fmt.Errorf("the image stream has a different import spec %q, pass --confirm to update", stream.Spec.DockerImageRepository)
				}
				stream.Spec.DockerImageRepository = from
			}

		} else {
			// importing a single tag

			// follow any referential tags to the destination
			finalTag, existing, ok, multiple := imageapi.FollowTagReference(stream, tag)
			if !ok && multiple {
				return nil, nil, fmt.Errorf("tag %q on the image stream is a reference to %q, which does not exist", tag, finalTag)
			}

			if ok {
				// disallow changing an existing tag
				if existing.From == nil || existing.From.Kind != "DockerImage" {
					return nil, nil, fmt.Errorf("tag %q already exists - you must use the 'tag' command if you want to change the source to %q", tag, from)
				}
				if len(from) != 0 && from != existing.From.Name {
					if multiple {
						return nil, nil, fmt.Errorf("the tag %q points to the tag %q which points to %q - use the 'tag' command if you want to change the source to %q", tag, finalTag, existing.From.Name, from)
					}
					return nil, nil, fmt.Errorf("the tag %q points to %q - use the 'tag' command if you want to change the source to %q", tag, existing.From.Name, from)
				}

				// set the target item to import
				from = existing.From.Name
				if multiple {
					tag = finalTag
				}

				// clear the legacy annotation
				delete(existing.Annotations, imageapi.DockerImageRepositoryCheckAnnotation)
				// reset the generation
				zero := int64(0)
				existing.Generation = &zero

			} else {
				// create a new tag
				if len(from) == 0 {
					from = stream.Spec.DockerImageRepository
				}
				existing = &imageapi.TagReference{
					From: &kapi.ObjectReference{
						Kind: "DockerImage",
						Name: from,
					},
				}
			}
			stream.Spec.Tags[tag] = *existing
		}
	}

	if len(from) == 0 {
		// catch programmer error
		return nil, nil, fmt.Errorf("unexpected error, from is empty")
	}

	// Attempt the new, direct import path
	isi := &imageapi.ImageStreamImport{
		ObjectMeta: kapi.ObjectMeta{
			Name:            stream.Name,
			Namespace:       o.Namespace,
			ResourceVersion: stream.ResourceVersion,
		},
		Spec: imageapi.ImageStreamImportSpec{Import: true},
	}
	if o.All {
		isi.Spec.Repository = &imageapi.RepositoryImportSpec{
			From: kapi.ObjectReference{
				Kind: "DockerImage",
				Name: from,
			},
			ImportPolicy: imageapi.TagImportPolicy{Insecure: o.Insecure},
		}
	} else {
		isi.Spec.Images = append(isi.Spec.Images, imageapi.ImageImportSpec{
			From: kapi.ObjectReference{
				Kind: "DockerImage",
				Name: from,
			},
			To:           &kapi.LocalObjectReference{Name: tag},
			ImportPolicy: imageapi.TagImportPolicy{Insecure: o.Insecure},
		})
	}

	return stream, isi, nil
}