Add option to get content digest from manifest get

The client may need the content digest to delete a manifest using the digest used by the registry.

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
This commit is contained in:
Derek McGowan 2016-06-08 17:02:29 -07:00
parent b3ecf67fa7
commit f3ae941cca
2 changed files with 45 additions and 7 deletions

View file

@ -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)

View file

@ -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)