Пример #1
0
// Resolve converts an image reference into a resolved image or returns an error. Only images located in the internal
// registry or those with a digest can be resolved - all other scenarios will return an error.
func (c *imageResolutionCache) resolveImageReference(ref imageapi.DockerImageReference) (*rules.ImagePolicyAttributes, error) {
	// images by ID can be checked for policy
	if len(ref.ID) > 0 {
		now := now()
		if value, ok := c.cache.Get(ref.ID); ok {
			cached := value.(imageCacheEntry)
			if now.Before(cached.expires) {
				return &rules.ImagePolicyAttributes{Name: ref, Image: cached.image}, nil
			}
		}
		image, err := c.images.Get(ref.ID)
		if err != nil {
			return nil, err
		}
		c.cache.Add(ref.ID, imageCacheEntry{expires: now.Add(c.expiration), image: image})
		return &rules.ImagePolicyAttributes{Name: ref, Image: image}, nil
	}

	if !c.integrated.Matches(ref.Registry) {
		return nil, fmt.Errorf("only images imported into the registry are allowed (%s)", ref.Exact())
	}

	tag := ref.Tag
	if len(tag) == 0 {
		tag = imageapi.DefaultImageTag
	}

	return c.resolveImageStreamTag(ref.Namespace, ref.Name, tag)
}
func autoConvert_v1_DockerImageReference_To_api_DockerImageReference(in *DockerImageReference, out *api.DockerImageReference, s conversion.Scope) error {
	out.Registry = in.Registry
	out.Namespace = in.Namespace
	out.Name = in.Name
	out.Tag = in.Tag
	out.ID = in.ID
	return nil
}
Пример #3
0
func autoConvert_v1_DockerImageReference_To_api_DockerImageReference(in *DockerImageReference, out *image_api.DockerImageReference, s conversion.Scope) error {
	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
		defaulting.(func(*DockerImageReference))(in)
	}
	out.Registry = in.Registry
	out.Namespace = in.Namespace
	out.Name = in.Name
	out.Tag = in.Tag
	out.ID = in.ID
	return nil
}
Пример #4
0
// importTag import single tag from given ImageStream. Returns retrieved image (for later reuse),
// a flag saying if we should retry imports and an error if one occurs.
func (c *ImportController) importTag(stream *api.ImageStream, tag string, ref api.DockerImageReference, dockerImage *dockerregistry.Image, client dockerregistry.Client, insecure bool) (*dockerregistry.Image, bool, error) {
	glog.V(5).Infof("Importing tag %s from %s/%s...", tag, stream.Namespace, stream.Name)
	if dockerImage == nil {
		// TODO insecure applies to the stream's spec.dockerImageRepository, not necessarily to an external one!
		conn, err := client.Connect(ref.Registry, insecure)
		if err != nil {
			// retry-able error no. 3
			return nil, true, err
		}
		if len(ref.ID) > 0 {
			dockerImage, err = conn.ImageByID(ref.Namespace, ref.Name, ref.ID)
		} else {
			dockerImage, err = conn.ImageByTag(ref.Namespace, ref.Name, ref.Tag)
		}
		switch {
		case dockerregistry.IsRepositoryNotFound(err), dockerregistry.IsRegistryNotFound(err), dockerregistry.IsImageNotFound(err), dockerregistry.IsTagNotFound(err):
			return nil, false, err
		case err != nil:
			// retry-able error no. 4
			return nil, true, err
		}
	}
	var image api.DockerImage
	if err := kapi.Scheme.Convert(&dockerImage.Image, &image); err != nil {
		return nil, false, fmt.Errorf("could not convert image: %#v", err)
	}

	// prefer to pull by ID always
	if dockerImage.PullByID {
		// if the registry indicates the image is pullable by ID, clear the tag
		ref.Tag = ""
		ref.ID = dockerImage.ID
	}

	mapping := &api.ImageStreamMapping{
		ObjectMeta: kapi.ObjectMeta{
			Name:      stream.Name,
			Namespace: stream.Namespace,
		},
		Tag: tag,
		Image: api.Image{
			ObjectMeta: kapi.ObjectMeta{
				Name: dockerImage.ID,
			},
			DockerImageReference: ref.String(),
			DockerImageMetadata:  image,
		},
	}
	if err := c.mappings.ImageStreamMappings(stream.Namespace).Create(mapping); err != nil {
		// retry-able no. 5
		return nil, true, err
	}
	return dockerImage, false, nil
}
Пример #5
0
// Get retrieves the manifest with digest `dgst`.
func (r *repository) Get(dgst digest.Digest) (*schema1.SignedManifest, error) {
	if _, err := r.getImageStreamImage(dgst); err != nil {
		context.GetLogger(r.ctx).Errorf("Error retrieving ImageStreamImage %s/%s@%s: %v", r.namespace, r.name, dgst.String(), err)
		return nil, err
	}

	image, err := r.getImage(dgst)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("Error retrieving image %s: %v", dgst.String(), err)
		return nil, err
	}

	ref := imageapi.DockerImageReference{Namespace: r.namespace, Name: r.name, Registry: r.registryAddr}
	return r.manifestFromImageWithCachedLayers(image, ref.DockerClientDefaults().Exact())
}
Пример #6
0
// dockerImageRepository determines the docker image stream for stream.
// If stream.DockerImageRepository is set, that value is returned. Otherwise,
// if a default registry exists, the value returned is of the form
// <default registry>/<namespace>/<stream name>.
func (s Strategy) dockerImageRepository(stream *api.ImageStream) string {
	registry, ok := s.defaultRegistry.DefaultRegistry()
	if !ok {
		return stream.Spec.DockerImageRepository
	}

	if len(stream.Namespace) == 0 {
		stream.Namespace = kapi.NamespaceDefault
	}
	ref := api.DockerImageReference{
		Registry:  registry,
		Namespace: stream.Namespace,
		Name:      stream.Name,
	}
	return ref.String()
}
Пример #7
0
// pullthroughGetByTag attempts to load the given image manifest from the remote server defined by ref, using cacheName to store any cached layers.
func (r *repository) pullthroughGetByTag(image *imageapi.Image, ref imageapi.DockerImageReference, cacheName string, options ...distribution.ManifestServiceOption) (*schema1.SignedManifest, error) {
	defaultRef := ref.DockerClientDefaults()

	retriever := r.importContext()

	repo, err := retriever.Repository(r.ctx, defaultRef.RegistryURL(), defaultRef.RepositoryName(), false)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("Error getting remote repository for image %q: %v", image.DockerImageReference, err)
		return nil, err
	}

	// get a manifest context
	manifests, err := repo.Manifests(r.ctx)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("Error getting manifests for image %q: %v", image.DockerImageReference, err)
		return nil, err
	}

	// fetch this by image
	if len(ref.ID) > 0 {
		dgst, err := digest.ParseDigest(ref.ID)
		if err != nil {
			context.GetLogger(r.ctx).Errorf("Error getting manifests for image %q: %v", image.DockerImageReference, err)
			return nil, err
		}
		manifest, err := manifests.Get(dgst)
		if err != nil {
			context.GetLogger(r.ctx).Errorf("Error getting manifest from remote server for image %q: %v", image.DockerImageReference, err)
			return nil, err
		}
		r.rememberLayers(manifest, cacheName)
		return manifest, nil
	}

	// fetch this by tag
	manifest, err := manifests.GetByTag(ref.Tag, options...)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("Error getting manifest from remote server for image %q: %v", image.DockerImageReference, err)
		return nil, err
	}

	r.rememberLayers(manifest, cacheName)
	return manifest, nil
}
Пример #8
0
// Get retrieves the manifest with digest `dgst`.
func (r *repository) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
	if err := r.checkPendingErrors(ctx); err != nil {
		return nil, err
	}

	if _, err := r.getImageStreamImage(dgst); err != nil {
		context.GetLogger(r.ctx).Errorf("error retrieving ImageStreamImage %s/%s@%s: %v", r.namespace, r.name, dgst.String(), err)
		return nil, err
	}

	image, err := r.getImage(dgst)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("error retrieving image %s: %v", dgst.String(), err)
		return nil, err
	}

	ref := imageapi.DockerImageReference{Namespace: r.namespace, Name: r.name, Registry: r.registryAddr}
	manifest, err := r.manifestFromImageWithCachedLayers(image, ref.DockerClientDefaults().Exact())

	return manifest, err
}
Пример #9
0
// Get retrieves the manifest with digest `dgst`.
func (r *repository) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) {
	if err := r.checkPendingErrors(ctx); err != nil {
		return nil, err
	}

	if _, err := r.getImageStreamImage(dgst); err != nil {
		context.GetLogger(r.ctx).Errorf("error retrieving ImageStreamImage %s/%s@%s: %v", r.namespace, r.name, dgst.String(), err)
		return nil, err
	}

	image, err := r.getImage(dgst)
	if err != nil {
		context.GetLogger(r.ctx).Errorf("error retrieving image %s: %v", dgst.String(), err)
		return nil, err
	}

	ref := imageapi.DockerImageReference{Namespace: r.namespace, Name: r.name, Registry: r.registryAddr}
	if managed := image.Annotations[imageapi.ManagedByOpenShiftAnnotation]; managed == "true" {
		// Repository without a registry part is refers to repository containing locally managed images.
		// Such an entry is retrieved, checked and set by blobDescriptorService operating only on local blobs.
		ref.Registry = ""
	} else {
		// Repository with a registry points to remote repository. This is used by pullthrough middleware.
		ref = ref.DockerClientDefaults().AsRepository()
	}

	manifest, err := r.manifestFromImageWithCachedLayers(image, ref.Exact())

	return manifest, err
}
Пример #10
0
// proxyStat attempts to locate the digest in the provided remote repository or returns an error. If the digest is found,
// r.digestToStore saves the store.
func (r *pullthroughBlobStore) proxyStat(ctx context.Context, retriever importer.RepositoryRetriever, ref imageapi.DockerImageReference, dgst digest.Digest) (distribution.Descriptor, error) {
	context.GetLogger(ctx).Infof("Trying to stat %q from %q", dgst, ref.Exact())
	repo, err := retriever.Repository(ctx, ref.RegistryURL(), ref.RepositoryName(), r.pullFromInsecureRegistries)
	if err != nil {
		context.GetLogger(ctx).Errorf("Error getting remote repository for image %q: %v", ref.Exact(), err)
		return distribution.Descriptor{}, err
	}
	pullthroughBlobStore := repo.Blobs(ctx)
	desc, err := pullthroughBlobStore.Stat(ctx, dgst)
	if err != nil {
		if err != distribution.ErrBlobUnknown {
			context.GetLogger(ctx).Errorf("Error getting pullthroughBlobStore for image %q: %v", ref.Exact(), err)
		}
		return distribution.Descriptor{}, err
	}

	r.digestToStore[dgst.String()] = pullthroughBlobStore
	return desc, nil
}
Пример #11
0
func DockerImageRepositoryNodeName(o imageapi.DockerImageReference) osgraph.UniqueName {
	return osgraph.UniqueName(fmt.Sprintf("%s|%s", DockerRepositoryNodeKind, o.String()))
}
Пример #12
0
// importImages updates the passed ImageStreamImport object and sets Status for each image based on whether the import
// succeeded or failed. Cache is updated with any loaded images. Limiter is optional and controls how fast images are updated.
func (i *ImageStreamImporter) importImages(ctx gocontext.Context, retriever RepositoryRetriever, isi *api.ImageStreamImport, limiter flowcontrol.RateLimiter) {
	tags := make(map[manifestKey][]int)
	ids := make(map[manifestKey][]int)
	repositories := make(map[repositoryKey]*importRepository)
	cache := i.digestToRepositoryCache[ctx]

	isi.Status.Images = make([]api.ImageImportStatus, len(isi.Spec.Images))
	for i := range isi.Spec.Images {
		spec := &isi.Spec.Images[i]
		from := spec.From
		if from.Kind != "DockerImage" {
			continue
		}
		// TODO: This should be removed in 1.6
		// See for more info: https://github.com/openshift/origin/pull/11774#issuecomment-258905994
		var (
			err error
			ref api.DockerImageReference
		)
		if from.Name != "*" {
			ref, err = api.ParseDockerImageReference(from.Name)
			if err != nil {
				isi.Status.Images[i].Status = invalidStatus("", field.Invalid(field.NewPath("from", "name"), from.Name, fmt.Sprintf("invalid name: %v", err)))
				continue
			}
		} else {
			ref = api.DockerImageReference{Name: from.Name}
		}
		defaultRef := ref.DockerClientDefaults()
		repoName := defaultRef.RepositoryName()
		registryURL := defaultRef.RegistryURL()

		key := repositoryKey{url: *registryURL, name: repoName}
		repo, ok := repositories[key]
		if !ok {
			repo = &importRepository{
				Ref:      ref,
				Registry: &key.url,
				Name:     key.name,
				Insecure: spec.ImportPolicy.Insecure,
			}
			repositories[key] = repo
		}

		if len(defaultRef.ID) > 0 {
			id := manifestKey{repositoryKey: key}
			id.value = defaultRef.ID
			ids[id] = append(ids[id], i)
			if len(ids[id]) == 1 {
				repo.Digests = append(repo.Digests, importDigest{
					Name:  defaultRef.ID,
					Image: cache[id],
				})
			}
		} else {
			tag := manifestKey{repositoryKey: key}
			tag.value = defaultRef.Tag
			tags[tag] = append(tags[tag], i)
			if len(tags[tag]) == 1 {
				repo.Tags = append(repo.Tags, importTag{
					Name:  defaultRef.Tag,
					Image: cache[tag],
				})
			}
		}
	}

	// for each repository we found, import all tags and digests
	for key, repo := range repositories {
		i.importRepositoryFromDocker(ctx, retriever, repo, limiter)
		for _, tag := range repo.Tags {
			j := manifestKey{repositoryKey: key}
			j.value = tag.Name
			if tag.Image != nil {
				cache[j] = tag.Image
			}
			for _, index := range tags[j] {
				if tag.Err != nil {
					setImageImportStatus(isi, index, tag.Name, tag.Err)
					continue
				}
				copied := *tag.Image
				image := &isi.Status.Images[index]
				ref := repo.Ref
				ref.Tag, ref.ID = tag.Name, copied.Name
				copied.DockerImageReference = ref.MostSpecific().Exact()
				image.Tag = tag.Name
				image.Image = &copied
				image.Status.Status = unversioned.StatusSuccess
			}
		}
		for _, digest := range repo.Digests {
			j := manifestKey{repositoryKey: key}
			j.value = digest.Name
			if digest.Image != nil {
				cache[j] = digest.Image
			}
			for _, index := range ids[j] {
				if digest.Err != nil {
					setImageImportStatus(isi, index, "", digest.Err)
					continue
				}
				image := &isi.Status.Images[index]
				copied := *digest.Image
				ref := repo.Ref
				ref.Tag, ref.ID = "", copied.Name
				copied.DockerImageReference = ref.MostSpecific().Exact()
				image.Image = &copied
				image.Status.Status = unversioned.StatusSuccess
			}
		}
	}
}
Пример #13
0
// Search searches all images in local docker server for images that match terms
func (r DockerClientSearcher) Search(precise bool, terms ...string) (ComponentMatches, []error) {
	componentMatches := ComponentMatches{}
	errs := []error{}
	for _, term := range terms {
		var (
			ref imageapi.DockerImageReference
			err error
		)
		switch term {
		case "__dockerimage_fail":
			errs = append(errs, fmt.Errorf("unable to find the specified docker image: %s", term))
			continue
		case "scratch":
			componentMatches = append(componentMatches, &ComponentMatch{
				Value: term,
				Score: 0.0,
				// we don't want to create an imagestream for "scratch", so treat
				// it as a local only image.
				LocalOnly: true,
				Virtual:   true,
			})
			return componentMatches, errs
		case "*":
			ref = imageapi.DockerImageReference{Name: term}
		default:
			ref, err = imageapi.ParseDockerImageReference(term)
			if err != nil {
				continue
			}
		}

		termMatches := ScoredComponentMatches{}

		// first look for the image in the remote docker registry
		if r.RegistrySearcher != nil {
			glog.V(4).Infof("checking remote registry for %q", ref.String())
			matches, err := r.RegistrySearcher.Search(precise, term)
			errs = append(errs, err...)

			for i := range matches {
				matches[i].LocalOnly = false
				glog.V(5).Infof("Found remote match %v", matches[i].Value)
			}
			termMatches = append(termMatches, matches...)
		}

		if r.Client == nil || reflect.ValueOf(r.Client).IsNil() {
			componentMatches = append(componentMatches, termMatches...)
			continue
		}

		// if we didn't find it exactly in a remote registry,
		// try to find it as a local-only image.
		if len(termMatches.Exact()) == 0 {
			glog.V(4).Infof("checking local Docker daemon for %q", ref.String())
			images, err := r.Client.ListImages(docker.ListImagesOptions{})
			if err != nil {
				errs = append(errs, err)
				continue
			}

			if len(ref.Tag) == 0 {
				ref.Tag = imageapi.DefaultImageTag
				term = fmt.Sprintf("%s:%s", term, imageapi.DefaultImageTag)
			}
			for _, image := range images {
				if tags := matchTag(image, term, ref.Registry, ref.Namespace, ref.Name, ref.Tag); len(tags) > 0 {
					for i := range tags {
						tags[i].LocalOnly = true
						glog.V(5).Infof("Found local docker image match %q with score %f", tags[i].Value, tags[i].Score)
					}
					termMatches = append(termMatches, tags...)
				}
			}
		}
		sort.Sort(termMatches)

		for i, match := range termMatches {
			if match.Image != nil {
				continue
			}

			image, err := r.Client.InspectImage(match.Value)
			if err != nil {
				if err != docker.ErrNoSuchImage {
					errs = append(errs, err)
				}
				continue
			}
			dockerImage := &imageapi.DockerImage{}
			if err := kapi.Scheme.Convert(image, dockerImage, nil); err != nil {
				errs = append(errs, err)
				continue
			}
			updated := &ComponentMatch{
				Value:       match.Value,
				Argument:    fmt.Sprintf("--docker-image=%q", match.Value),
				Name:        match.Value,
				Description: descriptionFor(dockerImage, match.Value, ref.Registry, ""),
				Score:       match.Score,
				Image:       dockerImage,
				ImageTag:    ref.Tag,
				Insecure:    r.Insecure,
				Meta:        map[string]string{"registry": ref.Registry},
				LocalOnly:   match.LocalOnly,
			}
			termMatches[i] = updated
		}

		componentMatches = append(componentMatches, termMatches...)
	}

	return componentMatches, errs
}
Пример #14
0
// Search searches in the Docker registry for images that match terms
func (r DockerRegistrySearcher) Search(precise bool, terms ...string) (ComponentMatches, []error) {
	componentMatches := ComponentMatches{}
	var errs []error
	for _, term := range terms {
		var (
			ref imageapi.DockerImageReference
			err error
		)
		if term != "*" {
			ref, err = imageapi.ParseDockerImageReference(term)
			if err != nil {
				continue
			}
		} else {
			ref = imageapi.DockerImageReference{Name: term}
		}

		glog.V(4).Infof("checking Docker registry for %q, allow-insecure=%v", ref.String(), r.AllowInsecure)
		connection, err := r.Client.Connect(ref.Registry, r.AllowInsecure)
		if err != nil {
			if dockerregistry.IsRegistryNotFound(err) {
				errs = append(errs, err)
				continue
			}
			errs = append(errs, fmt.Errorf("can't connect to %q: %v", ref.Registry, err))
			continue
		}

		image, err := connection.ImageByTag(ref.Namespace, ref.Name, ref.Tag)
		if err != nil {
			if dockerregistry.IsNotFound(err) {
				if dockerregistry.IsTagNotFound(err) {
					glog.V(4).Infof("tag not found: %v", err)
				}
				continue
			}
			errs = append(errs, fmt.Errorf("can't connect to %q: %v", ref.Registry, err))
			continue
		}

		if len(ref.Tag) == 0 {
			ref.Tag = imageapi.DefaultImageTag
		}
		if len(ref.Registry) == 0 {
			ref.Registry = "Docker Hub"
		}
		glog.V(4).Infof("found image: %#v", image)

		dockerImage := &imageapi.DockerImage{}
		if err = kapi.Scheme.Convert(&image.Image, dockerImage, nil); err != nil {
			errs = append(errs, err)
			continue
		}

		match := &ComponentMatch{
			Value:       term,
			Argument:    fmt.Sprintf("--docker-image=%q", term),
			Name:        term,
			Description: descriptionFor(dockerImage, term, ref.Registry, ref.Tag),
			Score:       0,
			Image:       dockerImage,
			ImageTag:    ref.Tag,
			Insecure:    r.AllowInsecure,
			Meta:        map[string]string{"registry": ref.Registry},
		}
		glog.V(2).Infof("Adding %s as component match for %q with score %v", match.Description, term, match.Score)
		componentMatches = append(componentMatches, match)
	}

	return componentMatches, errs
}
Пример #15
0
// Search will attempt to find imagestreams with names that match the passed in value
func (r ImageStreamSearcher) Search(precise bool, terms ...string) (ComponentMatches, []error) {
	componentMatches := ComponentMatches{}
	var errs []error
	for _, term := range terms {
		var (
			ref imageapi.DockerImageReference
			err error
		)
		switch term {
		case "__imagestream_fail":
			errs = append(errs, fmt.Errorf("unable to find the specified image: %s", term))
			continue
		case "*":
			ref = imageapi.DockerImageReference{Name: term}
		default:
			ref, err = imageapi.ParseDockerImageReference(term)
			if err != nil || len(ref.Registry) != 0 {
				glog.V(2).Infof("image streams must be of the form [<namespace>/]<name>[:<tag>|@<digest>], term %q did not qualify", term)
				continue
			}
		}

		namespaces := r.Namespaces
		if len(ref.Namespace) != 0 {
			namespaces = []string{ref.Namespace}
		}
		followTag := false
		searchTag := ref.Tag
		if len(searchTag) == 0 {
			searchTag = imageapi.DefaultImageTag
			followTag = true
		}
		for _, namespace := range namespaces {
			glog.V(4).Infof("checking ImageStreams %s/%s with ref %q", namespace, ref.Name, searchTag)
			exact := false
			streams, err := r.Client.ImageStreams(namespace).List(kapi.ListOptions{})
			if err != nil {
				if errors.IsNotFound(err) || errors.IsForbidden(err) {
					continue
				}
				errs = append(errs, err)
				continue
			}
			original := ref
			ref.Namespace = namespace
			for i := range streams.Items {
				stream := &streams.Items[i]
				score, scored := imageStreamScorer(*stream, ref.Name)
				if !scored {
					glog.V(2).Infof("unscored %s: %v", stream.Name, score)
					continue
				}

				// indicate the server knows how to directly import image stream tags
				var meta map[string]string
				if stream.Generation > 0 {
					meta = map[string]string{"direct-tag": "1"}
				}

				imageref := original
				imageref.Name = stream.Name
				imageref.Registry = ""
				matchName := fmt.Sprintf("%s/%s", stream.Namespace, stream.Name)

				addMatch := func(tag string, matchScore float32, image *imageapi.DockerImage, notFound bool) {
					name := matchName
					var description, argument string
					if len(tag) > 0 {
						name = fmt.Sprintf("%s:%s", name, tag)
						argument = fmt.Sprintf("--image-stream=%q", name)
						description = fmt.Sprintf("Image stream %q (tag %q) in project %q", stream.Name, tag, stream.Namespace)
					} else {
						argument = fmt.Sprintf("--image-stream=%q --allow-missing-imagestream-tags", name)
						description = fmt.Sprintf("Image stream %q in project %q", stream.Name, stream.Namespace)
					}

					match := &ComponentMatch{
						Value:       term,
						Argument:    argument,
						Name:        name,
						Description: description,
						Score:       matchScore,
						ImageStream: stream,
						Image:       image,
						ImageTag:    tag,
						Meta:        meta,
						NoTagsFound: notFound,
					}
					glog.V(2).Infof("Adding %s as component match for %q with score %v", match.Description, term, matchScore)
					componentMatches = append(componentMatches, match)
				}

				// When an image stream contains a tag that references another local tag, and the user has not
				// provided a tag themselves (i.e. they asked for mysql and we defaulted to mysql:latest), walk
				// the chain of references to the end. This ensures that applications can default to using a "stable"
				// branch by giving the control over version to the image stream author.
				finalTag := searchTag
				if specTag, ok := stream.Spec.Tags[searchTag]; ok && followTag {
					if specTag.From != nil && specTag.From.Kind == "ImageStreamTag" && !strings.Contains(specTag.From.Name, ":") {
						if imageapi.LatestTaggedImage(stream, specTag.From.Name) != nil {
							finalTag = specTag.From.Name
						}
					}
				}

				latest := imageapi.LatestTaggedImage(stream, finalTag)
				if latest == nil || len(latest.Image) == 0 {

					glog.V(2).Infof("no image recorded for %s/%s:%s", stream.Namespace, stream.Name, finalTag)
					if r.AllowMissingTags {
						addMatch(finalTag, score, nil, false)
						continue
					}
					// Find tags that do exist and return those as partial matches
					foundOtherTags := false
					for tag := range stream.Status.Tags {
						latest := imageapi.LatestTaggedImage(stream, tag)
						if latest == nil || len(latest.Image) == 0 {
							continue
						}
						foundOtherTags = true

						// If the user specified a tag in their search string (followTag == false),
						// then score this match lower.
						tagScore := score
						if !followTag {
							tagScore += 0.5
						}
						addMatch(tag, tagScore, nil, false)
					}
					if !foundOtherTags {
						addMatch("", 0.5+score, nil, true)
					}
					continue
				}

				imageStreamImage, err := r.ImageStreamImages.ImageStreamImages(namespace).Get(stream.Name, latest.Image)
				if err != nil {
					if errors.IsNotFound(err) {
						// continue searching
						glog.V(2).Infof("tag %q is set, but image %q has been removed", finalTag, latest.Image)
						continue
					}
					errs = append(errs, err)
					continue
				}

				addMatch(finalTag, score, &imageStreamImage.Image.DockerImageMetadata, false)
				if score == 0.0 {
					exact = true
				}
			}

			// If we found one or more exact matches in this namespace, do not continue looking at
			// other namespaces
			if exact && precise {
				break
			}
		}
	}
	return componentMatches, errs
}
Пример #16
0
// Next processes the given image stream, looking for streams that have DockerImageRepository
// set but have not yet been marked as "ready". If transient errors occur, err is returned but
// the image stream is not modified (so it will be tried again later). If a permanent
// failure occurs the image is marked with an annotation. The tags of the original spec image
// are left as is (those are updated through status).
func (c *ImportController) Next(stream *api.ImageStream) error {
	if !needsImport(stream) {
		return nil
	}
	name := stream.Spec.DockerImageRepository

	ref, err := api.ParseDockerImageReference(name)
	if err != nil {
		err = fmt.Errorf("invalid docker image repository, cannot import data: %v", err)
		util.HandleError(err)
		return c.done(stream, err.Error(), retryCount)
	}

	insecure := stream.Annotations != nil && stream.Annotations[api.InsecureRepositoryAnnotation] == "true"

	client := c.client
	if client == nil {
		client = dockerregistry.NewClient()
	}
	conn, err := client.Connect(ref.Registry, insecure, false)
	if err != nil {
		return err
	}
	tags, err := conn.ImageTags(ref.Namespace, ref.Name)
	switch {
	case dockerregistry.IsRepositoryNotFound(err), dockerregistry.IsRegistryNotFound(err):
		return c.done(stream, err.Error(), retryCount)
	case err != nil:
		return err
	}

	imageToTag := make(map[string][]string)
	for tag, image := range tags {
		if specTag, ok := stream.Spec.Tags[tag]; ok && specTag.From != nil {
			// spec tag is set to track another tag - do not import
			continue
		}

		imageToTag[image] = append(imageToTag[image], tag)
	}

	// no tags to import
	if len(imageToTag) == 0 {
		return c.done(stream, "", retryCount)
	}

	for id, tags := range imageToTag {
		dockerImage, err := conn.ImageByID(ref.Namespace, ref.Name, id)
		switch {
		case dockerregistry.IsRepositoryNotFound(err), dockerregistry.IsRegistryNotFound(err):
			return c.done(stream, err.Error(), retryCount)
		case dockerregistry.IsImageNotFound(err):
			continue
		case err != nil:
			return err
		}
		var image api.DockerImage
		if err := kapi.Scheme.Convert(&dockerImage.Image, &image); err != nil {
			err = fmt.Errorf("could not convert image: %#v", err)
			util.HandleError(err)
			return c.done(stream, err.Error(), retryCount)
		}

		idTagPresent := false
		if len(tags) > 1 && hasTag(tags, id) {
			// only set to true if we have at least 1 tag that isn't the image id
			idTagPresent = true
		}
		for _, tag := range tags {
			if idTagPresent && id == tag {
				continue
			}

			pullRef := api.DockerImageReference{
				Registry:  ref.Registry,
				Namespace: ref.Namespace,
				Name:      ref.Name,
				Tag:       tag,
			}
			// prefer to pull by ID always
			if dockerImage.PullByID {
				// if the registry indicates the image is pullable by ID, clear the tag
				pullRef.Tag = ""
				pullRef.ID = dockerImage.ID
			} else if idTagPresent {
				// if there is a tag for the image by its id (tag=tag), we can pull by id
				pullRef.Tag = id
			}

			mapping := &api.ImageStreamMapping{
				ObjectMeta: kapi.ObjectMeta{
					Name:      stream.Name,
					Namespace: stream.Namespace,
				},
				Tag: tag,
				Image: api.Image{
					ObjectMeta: kapi.ObjectMeta{
						Name: dockerImage.ID,
					},
					DockerImageReference: pullRef.String(),
					DockerImageMetadata:  image,
				},
			}
			if err := c.mappings.ImageStreamMappings(stream.Namespace).Create(mapping); err != nil {
				if errors.IsNotFound(err) {
					return c.done(stream, err.Error(), retryCount)
				}
				return err
			}
		}
	}

	// we've completed our updates
	return c.done(stream, "", retryCount)
}
Пример #17
0
// importFromRepository imports the repository named on the ImageStreamImport, if any, importing up to maximumTags, and reporting
// status on each image that is attempted to be imported. If the repository cannot be found or tags cannot be retrieved, the repository
// status field is set.
func (i *ImageStreamImporter) importFromRepository(ctx gocontext.Context, retriever RepositoryRetriever, isi *api.ImageStreamImport, maximumTags int, limiter flowcontrol.RateLimiter) {
	if isi.Spec.Repository == nil {
		return
	}
	cache := i.digestToRepositoryCache[ctx]
	isi.Status.Repository = &api.RepositoryImportStatus{}
	status := isi.Status.Repository

	spec := isi.Spec.Repository
	from := spec.From
	if from.Kind != "DockerImage" {
		return
	}
	// TODO: This should be removed in 1.6
	// See for more info: https://github.com/openshift/origin/pull/11774#issuecomment-258905994
	var (
		err error
		ref api.DockerImageReference
	)
	if from.Name != "*" {
		ref, err = api.ParseDockerImageReference(from.Name)
		if err != nil {
			status.Status = invalidStatus("", field.Invalid(field.NewPath("from", "name"), from.Name, fmt.Sprintf("invalid name: %v", err)))
			return
		}
	} else {
		ref = api.DockerImageReference{Name: from.Name}
	}

	defaultRef := ref.DockerClientDefaults()
	repoName := defaultRef.RepositoryName()
	registryURL := defaultRef.RegistryURL()

	key := repositoryKey{url: *registryURL, name: repoName}
	repo := &importRepository{
		Ref:         ref,
		Registry:    &key.url,
		Name:        key.name,
		Insecure:    spec.ImportPolicy.Insecure,
		MaximumTags: maximumTags,
	}
	i.importRepositoryFromDocker(ctx, retriever, repo, limiter)

	if repo.Err != nil {
		status.Status = imageImportStatus(repo.Err, "", "repository")
		return
	}

	additional := []string{}
	tagKey := manifestKey{repositoryKey: key}
	for _, s := range repo.AdditionalTags {
		tagKey.value = s
		if image, ok := cache[tagKey]; ok {
			repo.Tags = append(repo.Tags, importTag{
				Name:  s,
				Image: image,
			})
		} else {
			additional = append(additional, s)
		}
	}
	status.AdditionalTags = additional

	failures := 0
	status.Status.Status = unversioned.StatusSuccess
	status.Images = make([]api.ImageImportStatus, len(repo.Tags))
	for i, tag := range repo.Tags {
		status.Images[i].Tag = tag.Name
		if tag.Err != nil {
			failures++
			status.Images[i].Status = imageImportStatus(tag.Err, "", "repository")
			continue
		}
		status.Images[i].Status.Status = unversioned.StatusSuccess

		copied := *tag.Image
		ref.Tag, ref.ID = tag.Name, copied.Name
		copied.DockerImageReference = ref.MostSpecific().Exact()
		status.Images[i].Image = &copied
	}
	if failures > 0 {
		status.Status.Status = unversioned.StatusFailure
		status.Status.Reason = unversioned.StatusReason("ImportFailed")
		switch failures {
		case 1:
			status.Status.Message = "one of the images from this repository failed to import"
		default:
			status.Status.Message = fmt.Sprintf("%d of the images from this repository failed to import", failures)
		}
	}
}