func()

in registry/handlers/manifests.go [626:737]


func (imh *manifestHandler) PutManifest(w http.ResponseWriter, r *http.Request) {
	l := log.GetLogger(log.WithContext(imh))
	l.Debug("PutImageManifest")

	if imh.Tag != "" {
		if err := imh.validateTagPushRestriction(); err != nil {
			imh.Errors = append(imh.Errors, err)
			return
		}
	}

	var jsonBuf bytes.Buffer
	if err := copyFullPayload(imh, w, r, &jsonBuf, maxManifestBodySize, "image manifest PUT"); err != nil {
		// copyFullPayload reports the error if necessary
		imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err.Error()))
		return
	}

	mediaType := r.Header.Get("Content-Type")
	m, desc, err := distribution.UnmarshalManifest(mediaType, jsonBuf.Bytes())
	if err != nil {
		imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err.Error()))
		return
	}

	switch {
	case imh.Digest != "":
		if desc.Digest != imh.Digest {
			l.WithFields(log.Fields{
				"payload_digest":  desc.Digest,
				"provided_digest": imh.Digest,
			}).Error("payload digest does not match provided digest")
			imh.Errors = append(imh.Errors, v2.ErrorCodeDigestInvalid)
			return
		}
	case imh.Tag != "":
		imh.Digest = desc.Digest
	default:
		imh.Errors = append(imh.Errors, v2.ErrorCodeTagInvalid.WithDetail("no tag or digest specified"))
		return
	}

	isAnOCIManifest := mediaType == v1.MediaTypeImageManifest || mediaType == v1.MediaTypeImageIndex

	if isAnOCIManifest {
		l.Debug("Putting an OCI Manifest!")
	} else {
		l.Debug("Putting a Docker Manifest!")
	}

	manifestList, isManifestList := m.(*manifestlist.DeserializedManifestList)

	if isManifestList {
		logIfManifestListInvalid(imh, manifestList, http.MethodPut)
	}

	if err := imh.applyResourcePolicy(m); err != nil {
		imh.Errors = append(imh.Errors, err)
		return
	}

	manifestWriter, err := imh.newManifestWriter()
	if err != nil {
		imh.Errors = append(imh.Errors, err)
		return
	}

	if err = manifestWriter.Put(imh, m); err != nil {
		imh.appendPutError(err)
		return
	}

	// Tag this manifest
	if imh.Tag != "" {
		if err = manifestWriter.Tag(imh, m, imh.Tag, desc); err != nil {
			imh.appendPutError(err)
			return
		}
	}

	if err := imh.queueBridge.ManifestPushed(imh.Repository.Named(), m, distribution.WithTagOption{Tag: imh.Tag}); err != nil {
		l.WithError(err).Error("dispatching manifest push to listener")
	}

	// Construct a canonical url for the uploaded manifest.
	ref, err := reference.WithDigest(imh.Repository.Named(), imh.Digest)
	if err != nil {
		imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
		return
	}

	location, err := imh.urlBuilder.BuildManifestURL(ref)
	if err != nil {
		// NOTE(stevvooe): Given the behavior above, this absurdly unlikely to
		// happen. We'll log the error here but proceed as if it worked. Worst
		// case, we set an empty location header.
		l.WithError(err).Error("error building manifest url from digest")
	}

	w.Header().Set("Location", location)
	w.Header().Set("Docker-Content-Digest", imh.Digest.String())

	w.WriteHeader(http.StatusCreated)

	l.WithFields(log.Fields{
		"media_type":      desc.MediaType,
		"size_bytes":      desc.Size,
		"digest":          desc.Digest,
		"tag_name":        imh.Tag,
		"reference_count": len(m.References()),
	}).Info("manifest uploaded")
}