// matchImageIDOnly checks that the given image specifier is a digest-only // reference, and that it matches the given image. func matchImageIDOnly(inspected dockertypes.ImageInspect, image string) bool { // If the image ref is literally equal to the inspected image's ID, // just return true here (this might be the case for Docker 1.9, // where we won't have a digest for the ID) if inspected.ID == image { return true } // Otherwise, we should try actual parsing to be more correct ref, err := dockerref.Parse(image) if err != nil { glog.V(4).Infof("couldn't parse image reference %q: %v", image, err) return false } digest, isDigested := ref.(dockerref.Digested) if !isDigested { glog.V(4).Infof("the image reference %q was not a digest reference") return false } id, err := dockerdigest.ParseDigest(inspected.ID) if err != nil { glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err) return false } if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() { return true } glog.V(4).Infof("The reference %s does not directly refer to the given image's ID (%q)", image, inspected.ID) return false }
// String converts a DockerImageReference to a Docker pull spec. func (r DockerImageReference) String() string { registry := r.Registry if len(registry) > 0 { registry += "/" } if len(r.Namespace) == 0 { r.Namespace = DockerDefaultNamespace } r.Namespace += "/" var ref string if len(r.Tag) > 0 { ref = ":" + r.Tag } else if len(r.ID) > 0 { if _, err := digest.ParseDigest(r.ID); err == nil { // if it parses as a digest, it's v2 pull by id ref = "@" + r.ID } else { // if it doesn't parse as a digest, it's presumably a v1 registry by-id tag ref = ":" + r.ID } } return fmt.Sprintf("%s%s%s%s", registry, r.Namespace, r.Name, ref) }
// imageManifestDispatcher takes the request context and builds the // appropriate handler for handling image manifest requests. func imageManifestDispatcher(ctx *Context, r *http.Request) http.Handler { imageManifestHandler := &imageManifestHandler{ Context: ctx, } reference := getReference(ctx) dgst, err := digest.ParseDigest(reference) if err != nil { // We just have a tag imageManifestHandler.Tag = reference } else { imageManifestHandler.Digest = dgst } mhandler := handlers.MethodHandler{ "GET": http.HandlerFunc(imageManifestHandler.GetImageManifest), "HEAD": http.HandlerFunc(imageManifestHandler.GetImageManifest), } if !ctx.readOnly { mhandler["PUT"] = http.HandlerFunc(imageManifestHandler.PutImageManifest) mhandler["DELETE"] = http.HandlerFunc(imageManifestHandler.DeleteImageManifest) } return mhandler }
// ResolveImageID returns latest TagEvent for specified imageID and an error if // there's more than one image matching the ID or when one does not exist. func ResolveImageID(stream *ImageStream, imageID string) (*TagEvent, error) { var event *TagEvent set := sets.NewString() for _, history := range stream.Status.Tags { for i := range history.Items { tagging := &history.Items[i] if d, err := digest.ParseDigest(tagging.Image); err == nil { if strings.HasPrefix(d.Hex(), imageID) || strings.HasPrefix(tagging.Image, imageID) { event = tagging set.Insert(tagging.Image) } continue } if strings.HasPrefix(tagging.Image, imageID) { event = tagging set.Insert(tagging.Image) } } } switch len(set) { case 1: return &TagEvent{ Created: unversioned.Now(), DockerImageReference: event.DockerImageReference, Image: event.Image, }, nil case 0: return nil, errors.NewNotFound(Resource("imagestreamimage"), imageID) default: return nil, errors.NewConflict(Resource("imagestreamimage"), imageID, fmt.Errorf("multiple images match the prefix %q: %s", imageID, strings.Join(set.List(), ", "))) } }
// Parse parses s and returns a syntactically valid Reference. // If an error was encountered it is returned, along with a nil Reference. // NOTE: Parse will not handle short digests. func Parse(s string) (Reference, error) { matches := ReferenceRegexp.FindStringSubmatch(s) if matches == nil { if s == "" { return nil, ErrNameEmpty } // TODO(dmcgowan): Provide more specific and helpful error return nil, ErrReferenceInvalidFormat } if len(matches[1]) > NameTotalLengthMax { return nil, ErrNameTooLong } ref := reference{ name: matches[1], tag: matches[2], } if matches[3] != "" { var err error ref.digest, err = digest.ParseDigest(matches[3]) if err != nil { return nil, err } } r := getBestReferenceType(ref) if r == nil { return nil, ErrNameEmpty } return r, nil }
// manifestFromImage converts an Image to a SignedManifest. func (r *repository) manifestFromImage(image *imageapi.Image) (*manifest.SignedManifest, error) { dgst, err := digest.ParseDigest(image.Name) if err != nil { return nil, err } // Fetch the signatures for the manifest signatures, err := r.Signatures().Get(dgst) if err != nil { return nil, err } jsig, err := libtrust.NewJSONSignature([]byte(image.DockerImageManifest), signatures...) if err != nil { return nil, err } // Extract the pretty JWS raw, err := jsig.PrettySignature("signatures") if err != nil { return nil, err } var sm manifest.SignedManifest if err := json.Unmarshal(raw, &sm); err != nil { return nil, err } return &sm, err }
// GetByTag retrieves the named manifest with the provided tag func (r *repository) GetByTag(tag string, options ...distribution.ManifestServiceOption) (*schema1.SignedManifest, error) { for _, opt := range options { if err := opt(r); err != nil { return nil, err } } imageStreamTag, err := r.getImageStreamTag(tag) if err != nil { context.GetLogger(r.ctx).Errorf("Error getting ImageStreamTag %q: %v", tag, err) return nil, err } image := &imageStreamTag.Image dgst, err := digest.ParseDigest(imageStreamTag.Image.Name) if err != nil { context.GetLogger(r.ctx).Errorf("Error parsing digest %q: %v", imageStreamTag.Image.Name, err) return nil, err } image, err = r.getImage(dgst) if err != nil { context.GetLogger(r.ctx).Errorf("Error getting image %q: %v", dgst.String(), err) return nil, err } return r.manifestFromImage(image) }
// GetImageID returns an image ID corresponding to the image referred to by // refOrID. func (daemon *Daemon) GetImageID(refOrID string) (image.ID, error) { // Treat as an ID if id, err := digest.ParseDigest(refOrID); err == nil { return image.ID(id), nil } // Treat it as a possible tag or digest reference if ref, err := reference.ParseNamed(refOrID); err == nil { ref = registry.NormalizeLocalReference(ref) if id, err := daemon.tagStore.Get(ref); err == nil { return id, nil } if tagged, ok := ref.(reference.Tagged); ok { if id, err := daemon.imageStore.Search(tagged.Tag()); err == nil { for _, namedRef := range daemon.tagStore.References(id) { if namedRef.Name() == ref.Name() { return id, nil } } } } } // Search based on ID if id, err := daemon.imageStore.Search(refOrID); err == nil { return id, nil } return "", ErrImageDoesNotExist{refOrID} }
func (b *buildCache) getImageID(ctx context.Context, ref string) (digest.Digest, error) { inspect, _, err := b.client.ImageInspectWithRaw(ctx, ref) if err != nil { return "", err } return digest.ParseDigest(inspect.ID) }
func (t tagService) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { imageStream, err := t.repo.getImageStream() if err != nil { context.GetLogger(ctx).Errorf("error retrieving ImageStream %s/%s: %v", t.repo.namespace, t.repo.name, err) return distribution.Descriptor{}, distribution.ErrRepositoryUnknown{Name: t.repo.Named().Name()} } te := imageapi.LatestTaggedImage(imageStream, tag) if te == nil { return distribution.Descriptor{}, distribution.ErrTagUnknown{Tag: tag} } dgst, err := digest.ParseDigest(te.Image) if err != nil { return distribution.Descriptor{}, err } if !t.repo.pullthrough { image, err := t.repo.getImage(dgst) if err != nil { return distribution.Descriptor{}, err } if !isImageManaged(image) { return distribution.Descriptor{}, distribution.ErrTagUnknown{Tag: tag} } } return distribution.Descriptor{Digest: dgst}, nil }
// loadManifest loads a manifest from a byte array and verifies its content, // returning the local digest, the manifest itself, whether or not it was // verified. If ref is a digest, rather than a tag, this will be treated as // the local digest. An error will be returned if the signature verification // fails, local digest verification fails and, if provided, the remote digest // verification fails. The boolean return will only be false without error on // the failure of signatures trust check. func (s *TagStore) loadManifest(manifestBytes []byte, ref string, remoteDigest digest.Digest) (digest.Digest, *registry.ManifestData, bool, error) { payload, keys, err := unpackSignedManifest(manifestBytes) if err != nil { return "", nil, false, fmt.Errorf("error unpacking manifest: %v", err) } // TODO(stevvooe): It would be a lot better here to build up a stack of // verifiers, then push the bytes one time for signatures and digests, but // the manifests are typically small, so this optimization is not worth // hacking this code without further refactoring. var localDigest digest.Digest // Verify the local digest, if present in ref. ParseDigest will validate // that the ref is a digest and verify against that if present. Otherwize // (on error), we simply compute the localDigest and proceed. if dgst, err := digest.ParseDigest(ref); err == nil { // verify the manifest against local ref if err := verifyDigest(dgst, payload); err != nil { return "", nil, false, fmt.Errorf("verifying local digest: %v", err) } localDigest = dgst } else { // We don't have a local digest, since we are working from a tag. // Compute the digest of the payload and return that. logrus.Debugf("provided manifest reference %q is not a digest: %v", ref, err) localDigest, err = digest.FromBytes(payload) if err != nil { // near impossible logrus.Errorf("error calculating local digest during tag pull: %v", err) return "", nil, false, err } } // verify against the remote digest, if available if remoteDigest != "" { if err := verifyDigest(remoteDigest, payload); err != nil { return "", nil, false, fmt.Errorf("verifying remote digest: %v", err) } } var manifest registry.ManifestData if err := json.Unmarshal(payload, &manifest); err != nil { return "", nil, false, fmt.Errorf("error unmarshalling manifest: %s", err) } // validate the contents of the manifest if err := validateManifest(&manifest); err != nil { return "", nil, false, err } var verified bool verified, err = s.verifyTrustedKeys(manifest.Name, keys) if err != nil { return "", nil, false, fmt.Errorf("error verifying trusted keys: %v", err) } return localDigest, &manifest, verified, nil }
func (t tagService) Untag(ctx context.Context, tag string) error { imageStream, err := t.repo.getImageStream() if err != nil { context.GetLogger(ctx).Errorf("error retrieving ImageStream %s/%s: %v", t.repo.namespace, t.repo.name, err) return distribution.ErrRepositoryUnknown{Name: t.repo.Named().Name()} } te := imageapi.LatestTaggedImage(imageStream, tag) if te == nil { return distribution.ErrTagUnknown{Tag: tag} } if !t.repo.pullthrough { dgst, err := digest.ParseDigest(te.Image) if err != nil { return err } image, err := t.repo.getImage(dgst) if err != nil { return err } if !isImageManaged(image) { return distribution.ErrTagUnknown{Tag: tag} } } return t.repo.registryOSClient.ImageStreamTags(imageStream.Namespace).Delete(imageStream.Name, tag) }
// Parse parses s and returns a syntactically valid Reference. // If an error was encountered it is returned, along with a nil Reference. // NOTE: Parse will not handle short digests. func Parse(s string) (Reference, error) { matches := ReferenceRegexp.FindStringSubmatch(s) if matches == nil { if s == "" { return nil, ErrNameEmpty } if ReferenceRegexp.FindStringSubmatch(strings.ToLower(s)) != nil { return nil, ErrNameContainsUppercase } return nil, ErrReferenceInvalidFormat } if len(matches[1]) > NameTotalLengthMax { return nil, ErrNameTooLong } ref := reference{ name: matches[1], tag: matches[2], } if matches[3] != "" { var err error ref.digest, err = digest.ParseDigest(matches[3]) if err != nil { return nil, err } } r := getBestReferenceType(ref) if r == nil { return nil, ErrNameEmpty } return r, nil }
// PullImage initiates a pull operation. image is the repository name to pull, and // tag may be either empty, or indicate a specific tag to pull. func (daemon *Daemon) PullImage(ctx context.Context, image, tag string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error { // Special case: "pull -a" may send an image name with a // trailing :. This is ugly, but let's not break API // compatibility. image = strings.TrimSuffix(image, ":") ref, err := reference.ParseNamed(image) if err != nil { return err } if tag != "" { // The "tag" could actually be a digest. var dgst digest.Digest dgst, err = digest.ParseDigest(tag) if err == nil { ref, err = reference.WithDigest(ref, dgst) } else { ref, err = reference.WithTag(ref, tag) } if err != nil { return err } } return daemon.pullImageWithReference(ctx, ref, metaHeaders, authConfig, outStream) }
// ManifestMatchesImage returns true if the provided manifest matches the name of the image. func ManifestMatchesImage(image *Image, newManifest []byte) (bool, error) { dgst, err := digest.ParseDigest(image.Name) if err != nil { return false, err } v, err := digest.NewDigestVerifier(dgst) if err != nil { return false, err } var canonical []byte switch image.DockerImageManifestMediaType { case schema2.MediaTypeManifest: var m schema2.DeserializedManifest if err := json.Unmarshal(newManifest, &m); err != nil { return false, err } _, canonical, err = m.Payload() if err != nil { return false, err } case schema1.MediaTypeManifest, "": var m schema1.SignedManifest if err := json.Unmarshal(newManifest, &m); err != nil { return false, err } canonical = m.Canonical default: return false, fmt.Errorf("unsupported manifest mediatype: %s", image.DockerImageManifestMediaType) } if _, err := v.Write(canonical); err != nil { return false, err } return v.Verified(), nil }
func validateDigest(dgst string) error { if dgst == "" { return errors.New("digest can't be empty") } if _, err := digest.ParseDigest(dgst); err != nil { return err } return nil }
// ParseReference parses a reference into either a digest or tag reference func ParseReference(ref string) Reference { if strings.Contains(ref, ":") { dgst, err := digest.ParseDigest(ref) if err == nil { return digestReference{digest: dgst} } } return tagReference{tag: ref} }
// ParseIDOrReference parses string for an image ID or a reference. ID can be // without a default prefix. func ParseIDOrReference(idOrRef string) (digest.Digest, Named, error) { if err := stringid.ValidateID(idOrRef); err == nil { idOrRef = "sha256:" + idOrRef } if dgst, err := digest.ParseDigest(idOrRef); err == nil { return dgst, nil, nil } ref, err := ParseNamed(idOrRef) return "", ref, err }
// ShortDockerImageID returns a short form of the provided DockerImage ID for display func ShortDockerImageID(image *DockerImage, length int) string { id := image.ID if s, err := digest.ParseDigest(id); err == nil { id = s.Hex() } if len(id) > length { id = id[:length] } return id }
// Check if the inspected image matches what we are looking for func matchImageTagOrSHA(inspected dockertypes.ImageInspect, image string) bool { // The image string follows the grammar specified here // https://github.com/docker/distribution/blob/master/reference/reference.go#L4 named, err := dockerref.ParseNamed(image) if err != nil { glog.V(4).Infof("couldn't parse image reference %q: %v", image, err) return false } _, isTagged := named.(dockerref.Tagged) digest, isDigested := named.(dockerref.Digested) if !isTagged && !isDigested { // No Tag or SHA specified, so just return what we have return true } if isTagged { // Check the RepoTags for a match. for _, tag := range inspected.RepoTags { // An image name (without the tag/digest) can be [hostname '/'] component ['/' component]* // Because either the RepoTag or the name *may* contain the // hostname or not, we only check for the suffix match. if strings.HasSuffix(image, tag) || strings.HasSuffix(tag, image) { return true } } } if isDigested { for _, repoDigest := range inspected.RepoDigests { named, err := dockerref.ParseNamed(repoDigest) if err != nil { glog.V(4).Infof("couldn't parse image RepoDigest reference %q: %v", repoDigest, err) continue } if d, isDigested := named.(dockerref.Digested); isDigested { if digest.Digest().Algorithm().String() == d.Digest().Algorithm().String() && digest.Digest().Hex() == d.Digest().Hex() { return true } } } // process the ID as a digest id, err := dockerdigest.ParseDigest(inspected.ID) if err != nil { glog.V(4).Infof("couldn't parse image ID reference %q: %v", id, err) return false } if digest.Digest().Algorithm().String() == id.Algorithm().String() && digest.Digest().Hex() == id.Hex() { return true } } glog.V(4).Infof("Inspected image (%q) does not match %s", inspected.ID, image) return false }
func (graph *Graph) getLayerDigest(id string) (digest.Digest, error) { root := graph.imageRoot(id) cs, err := ioutil.ReadFile(filepath.Join(root, digestFileName)) if err != nil { if os.IsNotExist(err) { return "", ErrDigestNotSet } return "", err } return digest.ParseDigest(string(cs)) }
// signedManifestFromImage converts an Image to a SignedManifest. func (r *repository) signedManifestFromImage(image *imageapi.Image) (*schema1.SignedManifest, error) { if image.DockerImageManifestMediaType == schema2.MediaTypeManifest { context.GetLogger(r.ctx).Errorf("old client pulling new image %s", image.DockerImageReference) return nil, fmt.Errorf("unable to convert new image to old one") } raw := []byte(image.DockerImageManifest) // prefer signatures from the manifest if _, err := libtrust.ParsePrettySignature(raw, "signatures"); err == nil { sm := schema1.SignedManifest{Canonical: raw} if err = json.Unmarshal(raw, &sm); err == nil { return &sm, nil } } dgst, err := digest.ParseDigest(image.Name) if err != nil { return nil, err } var signBytes [][]byte if len(image.DockerImageSignatures) == 0 { // Fetch the signatures for the manifest signatures, errSign := r.getSignatures(dgst) if errSign != nil { return nil, errSign } for _, signatureDigest := range signatures { signBytes = append(signBytes, []byte(signatureDigest)) } } else { for _, sign := range image.DockerImageSignatures { signBytes = append(signBytes, sign) } } jsig, err := libtrust.NewJSONSignature(raw, signBytes...) if err != nil { return nil, err } // Extract the pretty JWS raw, err = jsig.PrettySignature("signatures") if err != nil { return nil, err } var sm schema1.SignedManifest if err = json.Unmarshal(raw, &sm); err != nil { return nil, err } return &sm, err }
func getCAHashFromToken(token string) (digest.Digest, error) { split := strings.Split(token, "-") if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" { return "", errors.New("invalid join token") } var digestInt big.Int digestInt.SetString(split[2], joinTokenBase) return digest.ParseDigest(fmt.Sprintf("sha256:%0[1]*s", 64, digestInt.Text(16))) }
func (r *mockTagService) Get(ctx context.Context, tag string) (distribution.Descriptor, error) { v, ok := r.repo.tags[tag] if !ok { return distribution.Descriptor{}, r.repo.getTagErr } dgst, err := digest.ParseDigest(v) if err != nil { panic(err) } return distribution.Descriptor{Digest: dgst}, r.repo.getTagErr }
// Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the // tag name in order to build the correct upload URL. This state is written and read under a lock. func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) { ref := ms.name for _, option := range options { if opt, ok := option.(withTagOption); ok { var err error ref, err = reference.WithTag(ref, opt.tag) if err != nil { return "", err } } else { err := option.Apply(ms) if err != nil { return "", err } } } manifestURL, err := ms.ub.BuildManifestURL(ref) if err != nil { return "", err } mediaType, p, err := m.Payload() if err != nil { return "", err } putRequest, err := http.NewRequest("PUT", manifestURL, bytes.NewReader(p)) if err != nil { return "", err } putRequest.Header.Set("Content-Type", mediaType) resp, err := ms.client.Do(putRequest) if err != nil { return "", err } defer resp.Body.Close() if SuccessStatus(resp.StatusCode) { dgstHeader := resp.Header.Get("Docker-Content-Digest") dgst, err := digest.ParseDigest(dgstHeader) if err != nil { return "", err } return dgst, nil } return "", HandleErrorResponse(resp) }
// Finally Push the (signed) manifest of the blobs we've just pushed func (r *Session) PutV2ImageManifest(ep *Endpoint, imageName, tagName string, signedManifest, rawManifest []byte, auth *RequestAuthorization) (digest.Digest, error) { routeURL, err := getV2Builder(ep).BuildManifestURL(imageName, tagName) if err != nil { return "", err } method := "PUT" logrus.Debugf("[registry] Calling %q %s", method, routeURL) req, err := http.NewRequest(method, routeURL, bytes.NewReader(signedManifest)) if err != nil { return "", err } if err := auth.Authorize(req); err != nil { return "", err } res, err := r.client.Do(req) if err != nil { return "", err } defer res.Body.Close() // All 2xx and 3xx responses can be accepted for a put. if res.StatusCode >= 400 { if res.StatusCode == 401 { return "", errLoginRequired } errBody, err := ioutil.ReadAll(res.Body) if err != nil { return "", err } logrus.Debugf("Unexpected response from server: %q %#v", errBody, res.Header) return "", httputils.NewHTTPRequestError(fmt.Sprintf("Server error: %d trying to push %s:%s manifest", res.StatusCode, imageName, tagName), res) } hdrDigest, err := digest.ParseDigest(res.Header.Get(DockerDigestHeader)) if err != nil { return "", fmt.Errorf("invalid manifest digest from registry: %s", err) } dgstVerifier, err := digest.NewDigestVerifier(hdrDigest) if err != nil { return "", fmt.Errorf("invalid manifest digest from registry: %s", err) } dgstVerifier.Write(rawManifest) if !dgstVerifier.Verified() { computedDigest, _ := digest.FromBytes(rawManifest) return "", fmt.Errorf("unable to verify manifest digest: registry has %q, computed %q", hdrDigest, computedDigest) } return hdrDigest, nil }
func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) { content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff")) if err != nil { return "", err } dgst, err := digest.ParseDigest(string(content)) if err != nil { return "", err } return DiffID(dgst), nil }
// readlink returns the linked digest at path. func (bs *blobStore) readlink(ctx context.Context, path string) (digest.Digest, error) { content, err := bs.driver.GetContent(ctx, path) if err != nil { return "", err } linked, err := digest.ParseDigest(string(content)) if err != nil { return "", err } return linked, nil }
// BlobDispatcher takes the request context and builds the appropriate handler // for handling blob requests. func BlobDispatcher(ctx *handlers.Context, r *http.Request) http.Handler { reference := ctxu.GetStringValue(ctx, "vars.digest") dgst, _ := digest.ParseDigest(reference) blobHandler := &blobHandler{ Context: ctx, Digest: dgst, } return gorillahandlers.MethodHandler{ "DELETE": http.HandlerFunc(blobHandler.Delete), } }
func (registry *Registry) ManifestDigest(repository, reference string) (digest.Digest, error) { url := registry.url("/v2/%s/manifests/%s", repository, reference) registry.Logf("registry.manifest.head url=%s repository=%s reference=%s", url, repository, reference) resp, err := registry.Client.Head(url) if resp != nil { defer resp.Body.Close() } if err != nil { return "", err } return digest.ParseDigest(resp.Header.Get("Docker-Content-Digest")) }