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")
}