// pushLayer pushes the layer content returning the url on success. func pushLayer(t *testing.T, ub *v2.URLBuilder, name string, dgst digest.Digest, uploadURLBase string, body io.Reader) string { digester := digest.NewCanonicalDigester() resp, err := doPushLayer(t, ub, name, dgst, uploadURLBase, io.TeeReader(body, &digester)) if err != nil { t.Fatalf("unexpected error doing push layer request: %v", err) } defer resp.Body.Close() checkResponse(t, "putting monolithic chunk", resp, http.StatusCreated) if err != nil { t.Fatalf("error generating sha256 digest of body") } sha256Dgst := digester.Digest() expectedLayerURL, err := ub.BuildBlobURL(name, sha256Dgst) if err != nil { t.Fatalf("error building expected layer url: %v", err) } checkHeaders(t, resp, http.Header{ "Location": []string{expectedLayerURL}, "Content-Length": []string{"0"}, "Docker-Content-Digest": []string{sha256Dgst.String()}, }) return resp.Header.Get("Location") }
func doPushChunk(t *testing.T, uploadURLBase string, body io.Reader) (*http.Response, digest.Digest, error) { u, err := url.Parse(uploadURLBase) if err != nil { t.Fatalf("unexpected error parsing pushLayer url: %v", err) } u.RawQuery = url.Values{ "_state": u.Query()["_state"], }.Encode() uploadURL := u.String() digester := digest.NewCanonicalDigester() req, err := http.NewRequest("PATCH", uploadURL, io.TeeReader(body, digester)) if err != nil { t.Fatalf("unexpected error creating new request: %v", err) } req.Header.Set("Content-Type", "application/octet-stream") resp, err := http.DefaultClient.Do(req) return resp, digester.Digest(), err }
// validateLayer checks the layer data against the digest, returning an error // if it does not match. The canonical digest is returned. func (lw *layerWriter) validateLayer(dgst digest.Digest) (digest.Digest, error) { var ( verified, fullHash bool canonical digest.Digest ) if lw.resumableDigester != nil { // Restore the hasher state to the end of the upload. if err := lw.resumeHashAt(lw.size); err != nil { return "", err } canonical = lw.resumableDigester.Digest() if canonical.Algorithm() == dgst.Algorithm() { // Common case: client and server prefer the same canonical digest // algorithm - currently SHA256. verified = dgst == canonical } else { // The client wants to use a different digest algorithm. They'll just // have to be patient and wait for us to download and re-hash the // uploaded content using that digest algorithm. fullHash = true } } else { // Not using resumable digests, so we need to hash the entire layer. fullHash = true } if fullHash { digester := digest.NewCanonicalDigester() digestVerifier, err := digest.NewDigestVerifier(dgst) if err != nil { return "", err } // Read the file from the backend driver and validate it. fr, err := newFileReader(lw.layerStore.repository.ctx, lw.bufferedFileWriter.driver, lw.path) if err != nil { return "", err } tr := io.TeeReader(fr, digester) if _, err = io.Copy(digestVerifier, tr); err != nil { return "", err } canonical = digester.Digest() verified = digestVerifier.Verified() } if !verified { context.GetLoggerWithField(lw.layerStore.repository.ctx, "canonical", dgst). Errorf("canonical digest does match provided digest") return "", distribution.ErrLayerInvalidDigest{ Digest: dgst, Reason: fmt.Errorf("content does not match digest"), } } return canonical, nil }