Merge pull request #1775 from dmcgowan/get-content-digest
Add option to get content digest from manifest getmaster
						commit
						3b1b58e9c5
					
				|  | @ -394,11 +394,26 @@ func (o etagOption) Apply(ms distribution.ManifestService) error { | ||||||
| 	return fmt.Errorf("etag options is a client-only option") | 	return fmt.Errorf("etag options is a client-only option") | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // ReturnContentDigest allows a client to set a the content digest on
 | ||||||
|  | // a successful request from the 'Docker-Content-Digest' header. This
 | ||||||
|  | // returned digest is represents the digest which the registry uses
 | ||||||
|  | // to refer to the content and can be used to delete the content.
 | ||||||
|  | func ReturnContentDigest(dgst *digest.Digest) distribution.ManifestServiceOption { | ||||||
|  | 	return contentDigestOption{dgst} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type contentDigestOption struct{ digest *digest.Digest } | ||||||
|  | 
 | ||||||
|  | func (o contentDigestOption) Apply(ms distribution.ManifestService) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) { | func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...distribution.ManifestServiceOption) (distribution.Manifest, error) { | ||||||
| 	var ( | 	var ( | ||||||
| 		digestOrTag string | 		digestOrTag string | ||||||
| 		ref         reference.Named | 		ref         reference.Named | ||||||
| 		err         error | 		err         error | ||||||
|  | 		contentDgst *digest.Digest | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	for _, option := range options { | 	for _, option := range options { | ||||||
|  | @ -408,6 +423,8 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, err | 				return nil, err | ||||||
| 			} | 			} | ||||||
|  | 		} else if opt, ok := option.(contentDigestOption); ok { | ||||||
|  | 			contentDgst = opt.digest | ||||||
| 		} else { | 		} else { | ||||||
| 			err := option.Apply(ms) | 			err := option.Apply(ms) | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
|  | @ -450,6 +467,12 @@ func (ms *manifests) Get(ctx context.Context, dgst digest.Digest, options ...dis | ||||||
| 	if resp.StatusCode == http.StatusNotModified { | 	if resp.StatusCode == http.StatusNotModified { | ||||||
| 		return nil, distribution.ErrManifestNotModified | 		return nil, distribution.ErrManifestNotModified | ||||||
| 	} else if SuccessStatus(resp.StatusCode) { | 	} else if SuccessStatus(resp.StatusCode) { | ||||||
|  | 		if contentDgst != nil { | ||||||
|  | 			dgst, err := digest.ParseDigest(resp.Header.Get("Docker-Content-Digest")) | ||||||
|  | 			if err == nil { | ||||||
|  | 				*contentDgst = dgst | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		mt := resp.Header.Get("Content-Type") | 		mt := resp.Header.Get("Content-Type") | ||||||
| 		body, err := ioutil.ReadAll(resp.Body) | 		body, err := ioutil.ReadAll(resp.Body) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -605,6 +605,14 @@ func addTestManifestWithEtag(repo reference.Named, reference string, content []b | ||||||
| 	*m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag}) | 	*m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func contentDigestString(mediatype string, content []byte) string { | ||||||
|  | 	if mediatype == schema1.MediaTypeSignedManifest { | ||||||
|  | 		m, _, _ := distribution.UnmarshalManifest(mediatype, content) | ||||||
|  | 		content = m.(*schema1.SignedManifest).Canonical | ||||||
|  | 	} | ||||||
|  | 	return digest.Canonical.FromBytes(content).String() | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) { | func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) { | ||||||
| 	*m = append(*m, testutil.RequestResponseMapping{ | 	*m = append(*m, testutil.RequestResponseMapping{ | ||||||
| 		Request: testutil.Request{ | 		Request: testutil.Request{ | ||||||
|  | @ -615,9 +623,10 @@ func addTestManifest(repo reference.Named, reference string, mediatype string, c | ||||||
| 			StatusCode: http.StatusOK, | 			StatusCode: http.StatusOK, | ||||||
| 			Body:       content, | 			Body:       content, | ||||||
| 			Headers: http.Header(map[string][]string{ | 			Headers: http.Header(map[string][]string{ | ||||||
| 				"Content-Length": {fmt.Sprint(len(content))}, | 				"Content-Length":        {fmt.Sprint(len(content))}, | ||||||
| 				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | 				"Last-Modified":         {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | ||||||
| 				"Content-Type":   {mediatype}, | 				"Content-Type":          {mediatype}, | ||||||
|  | 				"Docker-Content-Digest": {contentDigestString(mediatype, content)}, | ||||||
| 			}), | 			}), | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
|  | @ -629,9 +638,10 @@ func addTestManifest(repo reference.Named, reference string, mediatype string, c | ||||||
| 		Response: testutil.Response{ | 		Response: testutil.Response{ | ||||||
| 			StatusCode: http.StatusOK, | 			StatusCode: http.StatusOK, | ||||||
| 			Headers: http.Header(map[string][]string{ | 			Headers: http.Header(map[string][]string{ | ||||||
| 				"Content-Length": {fmt.Sprint(len(content))}, | 				"Content-Length":        {fmt.Sprint(len(content))}, | ||||||
| 				"Last-Modified":  {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | 				"Last-Modified":         {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, | ||||||
| 				"Content-Type":   {mediatype}, | 				"Content-Type":          {mediatype}, | ||||||
|  | 				"Docker-Content-Digest": {digest.Canonical.FromBytes(content).String()}, | ||||||
| 			}), | 			}), | ||||||
| 		}, | 		}, | ||||||
| 	}) | 	}) | ||||||
|  | @ -710,7 +720,8 @@ func TestV1ManifestFetch(t *testing.T) { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	manifest, err = ms.Get(ctx, dgst, distribution.WithTag("latest")) | 	var contentDigest digest.Digest | ||||||
|  | 	manifest, err = ms.Get(ctx, dgst, distribution.WithTag("latest"), ReturnContentDigest(&contentDigest)) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
|  | @ -723,6 +734,10 @@ func TestV1ManifestFetch(t *testing.T) { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if contentDigest != dgst { | ||||||
|  | 		t.Fatalf("Unexpected returned content digest %v, expected %v", contentDigest, dgst) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	manifest, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype")) | 	manifest, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype")) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		t.Fatal(err) | 		t.Fatal(err) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue