diff --git a/docs/client/repository.go b/docs/client/repository.go index 1f777adda..5da2239f1 100644 --- a/docs/client/repository.go +++ b/docs/client/repository.go @@ -427,9 +427,10 @@ func (o withTagOption) Apply(m distribution.ManifestService) error { } // Put puts a manifest. A tag can be specified using an options parameter which uses some shared state to hold the -// tag name in order to build the correct upload URL. This state is written and read under a lock. +// tag name in order to build the correct upload URL. func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options ...distribution.ManifestServiceOption) (digest.Digest, error) { ref := ms.name + var tagged bool for _, option := range options { if opt, ok := option.(withTagOption); ok { @@ -438,6 +439,7 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options . if err != nil { return "", err } + tagged = true } else { err := option.Apply(ms) if err != nil { @@ -445,13 +447,24 @@ func (ms *manifests) Put(ctx context.Context, m distribution.Manifest, options . } } } - - manifestURL, err := ms.ub.BuildManifestURL(ref) + mediaType, p, err := m.Payload() if err != nil { return "", err } - mediaType, p, err := m.Payload() + if !tagged { + // generate a canonical digest and Put by digest + _, d, err := distribution.UnmarshalManifest(mediaType, p) + if err != nil { + return "", err + } + ref, err = reference.WithDigest(ref, d.Digest) + if err != nil { + return "", err + } + } + + manifestURL, err := ms.ub.BuildManifestURL(ref) if err != nil { return "", err } diff --git a/docs/client/repository_test.go b/docs/client/repository_test.go index b7b782c70..df26b6313 100644 --- a/docs/client/repository_test.go +++ b/docs/client/repository_test.go @@ -815,6 +815,7 @@ func TestManifestPut(t *testing.T) { if err != nil { t.Fatal(err) } + var m testutil.RequestResponseMap m = append(m, testutil.RequestResponseMapping{ Request: testutil.Request{ @@ -831,6 +832,22 @@ func TestManifestPut(t *testing.T) { }, }) + putDgst := digest.FromBytes(m1.Canonical) + m = append(m, testutil.RequestResponseMapping{ + Request: testutil.Request{ + Method: "PUT", + Route: "/v2/" + repo.Name() + "/manifests/" + putDgst.String(), + Body: payload, + }, + Response: testutil.Response{ + StatusCode: http.StatusAccepted, + Headers: http.Header(map[string][]string{ + "Content-Length": {"0"}, + "Docker-Content-Digest": {putDgst.String()}, + }), + }, + }) + e, c := testServer(m) defer c() @@ -848,6 +865,10 @@ func TestManifestPut(t *testing.T) { t.Fatal(err) } + if _, err := ms.Put(ctx, m1); err != nil { + t.Fatal(err) + } + // TODO(dmcgowan): Check for invalid input error }