// 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 }
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 }
// 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 }
// 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()) }
// 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() }
// 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 }
// 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 }
// 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 }
// 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 }
func DockerImageRepositoryNodeName(o imageapi.DockerImageReference) osgraph.UniqueName { return osgraph.UniqueName(fmt.Sprintf("%s|%s", DockerRepositoryNodeKind, o.String())) }
// 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 } } } }
// 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 }
// 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 }
// 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 }
// 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) }
// 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) } } }