forked from TrueCloudLab/distribution
Merge pull request #1094 from aaronlehmann/correct-payload-serialization-order
Correct unmarshal order for SignedManifest
This commit is contained in:
commit
d87cf10852
4 changed files with 77 additions and 41 deletions
|
@ -62,15 +62,20 @@ type SignedManifest struct {
|
||||||
|
|
||||||
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
|
||||||
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
func (sm *SignedManifest) UnmarshalJSON(b []byte) error {
|
||||||
|
sm.Raw = make([]byte, len(b), len(b))
|
||||||
|
copy(sm.Raw, b)
|
||||||
|
|
||||||
|
p, err := sm.Payload()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var manifest Manifest
|
var manifest Manifest
|
||||||
if err := json.Unmarshal(b, &manifest); err != nil {
|
if err := json.Unmarshal(p, &manifest); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
sm.Manifest = manifest
|
sm.Manifest = manifest
|
||||||
sm.Raw = make([]byte, len(b), len(b))
|
|
||||||
copy(sm.Raw, b)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ package client
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
@ -14,8 +13,6 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/distribution/uuid"
|
|
||||||
|
|
||||||
"github.com/docker/distribution"
|
"github.com/docker/distribution"
|
||||||
"github.com/docker/distribution/context"
|
"github.com/docker/distribution/context"
|
||||||
"github.com/docker/distribution/digest"
|
"github.com/docker/distribution/digest"
|
||||||
|
@ -23,6 +20,8 @@ import (
|
||||||
"github.com/docker/distribution/manifest/schema1"
|
"github.com/docker/distribution/manifest/schema1"
|
||||||
"github.com/docker/distribution/registry/api/errcode"
|
"github.com/docker/distribution/registry/api/errcode"
|
||||||
"github.com/docker/distribution/testutil"
|
"github.com/docker/distribution/testutil"
|
||||||
|
"github.com/docker/distribution/uuid"
|
||||||
|
"github.com/docker/libtrust"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testServer(rrm testutil.RequestResponseMap) (string, func()) {
|
func testServer(rrm testutil.RequestResponseMap) (string, func()) {
|
||||||
|
@ -420,7 +419,7 @@ func TestBlobUploadMonolithic(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.SignedManifest, digest.Digest) {
|
func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.SignedManifest, digest.Digest, []byte) {
|
||||||
blobs := make([]schema1.FSLayer, blobCount)
|
blobs := make([]schema1.FSLayer, blobCount)
|
||||||
history := make([]schema1.History, blobCount)
|
history := make([]schema1.History, blobCount)
|
||||||
|
|
||||||
|
@ -431,30 +430,38 @@ func newRandomSchemaV1Manifest(name, tag string, blobCount int) (*schema1.Signed
|
||||||
history[i] = schema1.History{V1Compatibility: fmt.Sprintf("{\"Hex\": \"%x\"}", blob)}
|
history[i] = schema1.History{V1Compatibility: fmt.Sprintf("{\"Hex\": \"%x\"}", blob)}
|
||||||
}
|
}
|
||||||
|
|
||||||
m := &schema1.SignedManifest{
|
m := schema1.Manifest{
|
||||||
Manifest: schema1.Manifest{
|
Name: name,
|
||||||
Name: name,
|
Tag: tag,
|
||||||
Tag: tag,
|
Architecture: "x86",
|
||||||
Architecture: "x86",
|
FSLayers: blobs,
|
||||||
FSLayers: blobs,
|
History: history,
|
||||||
History: history,
|
Versioned: manifest.Versioned{
|
||||||
Versioned: manifest.Versioned{
|
SchemaVersion: 1,
|
||||||
SchemaVersion: 1,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
manifestBytes, err := json.Marshal(m)
|
|
||||||
if err != nil {
|
pk, err := libtrust.GenerateECP256PrivateKey()
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
dgst, err := digest.FromBytes(manifestBytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Raw = manifestBytes
|
sm, err := schema1.Sign(&m, pk)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return m, dgst
|
p, err := sm.Payload()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dgst, err := digest.FromBytes(p)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return sm, dgst, p
|
||||||
}
|
}
|
||||||
|
|
||||||
func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
|
func addTestManifestWithEtag(repo, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
|
||||||
|
@ -551,7 +558,7 @@ func checkEqualManifest(m1, m2 *schema1.SignedManifest) error {
|
||||||
func TestManifestFetch(t *testing.T) {
|
func TestManifestFetch(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
repo := "test.example.com/repo"
|
repo := "test.example.com/repo"
|
||||||
m1, dgst := newRandomSchemaV1Manifest(repo, "latest", 6)
|
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||||
var m testutil.RequestResponseMap
|
var m testutil.RequestResponseMap
|
||||||
addTestManifest(repo, dgst.String(), m1.Raw, &m)
|
addTestManifest(repo, dgst.String(), m1.Raw, &m)
|
||||||
|
|
||||||
|
@ -586,9 +593,9 @@ func TestManifestFetch(t *testing.T) {
|
||||||
|
|
||||||
func TestManifestFetchWithEtag(t *testing.T) {
|
func TestManifestFetchWithEtag(t *testing.T) {
|
||||||
repo := "test.example.com/repo/by/tag"
|
repo := "test.example.com/repo/by/tag"
|
||||||
m1, d1 := newRandomSchemaV1Manifest(repo, "latest", 6)
|
_, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||||
var m testutil.RequestResponseMap
|
var m testutil.RequestResponseMap
|
||||||
addTestManifestWithEtag(repo, "latest", m1.Raw, &m, d1.String())
|
addTestManifestWithEtag(repo, "latest", p1, &m, d1.String())
|
||||||
|
|
||||||
e, c := testServer(m)
|
e, c := testServer(m)
|
||||||
defer c()
|
defer c()
|
||||||
|
@ -611,8 +618,8 @@ func TestManifestFetchWithEtag(t *testing.T) {
|
||||||
|
|
||||||
func TestManifestDelete(t *testing.T) {
|
func TestManifestDelete(t *testing.T) {
|
||||||
repo := "test.example.com/repo/delete"
|
repo := "test.example.com/repo/delete"
|
||||||
_, dgst1 := newRandomSchemaV1Manifest(repo, "latest", 6)
|
_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||||
_, dgst2 := newRandomSchemaV1Manifest(repo, "latest", 6)
|
_, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||||
var m testutil.RequestResponseMap
|
var m testutil.RequestResponseMap
|
||||||
m = append(m, testutil.RequestResponseMapping{
|
m = append(m, testutil.RequestResponseMapping{
|
||||||
Request: testutil.Request{
|
Request: testutil.Request{
|
||||||
|
@ -651,7 +658,7 @@ func TestManifestDelete(t *testing.T) {
|
||||||
|
|
||||||
func TestManifestPut(t *testing.T) {
|
func TestManifestPut(t *testing.T) {
|
||||||
repo := "test.example.com/repo/delete"
|
repo := "test.example.com/repo/delete"
|
||||||
m1, dgst := newRandomSchemaV1Manifest(repo, "other", 6)
|
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6)
|
||||||
var m testutil.RequestResponseMap
|
var m testutil.RequestResponseMap
|
||||||
m = append(m, testutil.RequestResponseMapping{
|
m = append(m, testutil.RequestResponseMapping{
|
||||||
Request: testutil.Request{
|
Request: testutil.Request{
|
||||||
|
@ -744,7 +751,7 @@ func TestManifestTags(t *testing.T) {
|
||||||
|
|
||||||
func TestManifestUnauthorized(t *testing.T) {
|
func TestManifestUnauthorized(t *testing.T) {
|
||||||
repo := "test.example.com/repo"
|
repo := "test.example.com/repo"
|
||||||
_, dgst := newRandomSchemaV1Manifest(repo, "latest", 6)
|
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
|
||||||
var m testutil.RequestResponseMap
|
var m testutil.RequestResponseMap
|
||||||
|
|
||||||
m = append(m, testutil.RequestResponseMapping{
|
m = append(m, testutil.RequestResponseMapping{
|
||||||
|
|
|
@ -760,14 +760,32 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m
|
||||||
|
|
||||||
resp = putManifest(t, "putting unsigned manifest", manifestURL, unsignedManifest)
|
resp = putManifest(t, "putting unsigned manifest", manifestURL, unsignedManifest)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
checkResponse(t, "posting unsigned manifest", resp, http.StatusBadRequest)
|
checkResponse(t, "putting unsigned manifest", resp, http.StatusBadRequest)
|
||||||
_, p, counts := checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp,
|
_, p, counts := checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeManifestInvalid)
|
||||||
v2.ErrorCodeManifestUnverified, v2.ErrorCodeBlobUnknown, v2.ErrorCodeDigestInvalid)
|
|
||||||
|
|
||||||
expectedCounts := map[errcode.ErrorCode]int{
|
expectedCounts := map[errcode.ErrorCode]int{
|
||||||
v2.ErrorCodeManifestUnverified: 1,
|
v2.ErrorCodeManifestInvalid: 1,
|
||||||
v2.ErrorCodeBlobUnknown: 2,
|
}
|
||||||
v2.ErrorCodeDigestInvalid: 2,
|
|
||||||
|
if !reflect.DeepEqual(counts, expectedCounts) {
|
||||||
|
t.Fatalf("unexpected number of error codes encountered: %v\n!=\n%v\n---\n%s", counts, expectedCounts, string(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
// sign the manifest and still get some interesting errors.
|
||||||
|
sm, err := schema1.Sign(unsignedManifest, env.pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp = putManifest(t, "putting signed manifest with errors", manifestURL, sm)
|
||||||
|
defer resp.Body.Close()
|
||||||
|
checkResponse(t, "putting signed manifest with errors", resp, http.StatusBadRequest)
|
||||||
|
_, p, counts = checkBodyHasErrorCodes(t, "putting signed manifest with errors", resp,
|
||||||
|
v2.ErrorCodeManifestBlobUnknown, v2.ErrorCodeDigestInvalid)
|
||||||
|
|
||||||
|
expectedCounts = map[errcode.ErrorCode]int{
|
||||||
|
v2.ErrorCodeManifestBlobUnknown: 2,
|
||||||
|
v2.ErrorCodeDigestInvalid: 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(counts, expectedCounts) {
|
if !reflect.DeepEqual(counts, expectedCounts) {
|
||||||
|
@ -1426,7 +1444,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manifest upload
|
// Manifest upload
|
||||||
unsignedManifest := &schema1.Manifest{
|
m := &schema1.Manifest{
|
||||||
Versioned: manifest.Versioned{
|
Versioned: manifest.Versioned{
|
||||||
SchemaVersion: 1,
|
SchemaVersion: 1,
|
||||||
},
|
},
|
||||||
|
@ -1434,7 +1452,13 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
FSLayers: []schema1.FSLayer{},
|
FSLayers: []schema1.FSLayer{},
|
||||||
}
|
}
|
||||||
resp := putManifest(t, "putting unsigned manifest", manifestURL, unsignedManifest)
|
|
||||||
|
sm, err := schema1.Sign(m, env.pk)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("error signing manifest: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := putManifest(t, "putting unsigned manifest", manifestURL, sm)
|
||||||
checkResponse(t, "putting signed manifest to cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
checkResponse(t, "putting signed manifest to cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
|
||||||
|
|
||||||
// Manifest Delete
|
// Manifest Delete
|
||||||
|
|
|
@ -163,7 +163,7 @@ func (imh *imageManifestHandler) PutImageManifest(w http.ResponseWriter, r *http
|
||||||
for _, verificationError := range err {
|
for _, verificationError := range err {
|
||||||
switch verificationError := verificationError.(type) {
|
switch verificationError := verificationError.(type) {
|
||||||
case distribution.ErrManifestBlobUnknown:
|
case distribution.ErrManifestBlobUnknown:
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeBlobUnknown.WithDetail(verificationError.Digest))
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestBlobUnknown.WithDetail(verificationError.Digest))
|
||||||
case distribution.ErrManifestUnverified:
|
case distribution.ErrManifestUnverified:
|
||||||
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnverified)
|
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnverified)
|
||||||
default:
|
default:
|
||||||
|
|
Loading…
Reference in a new issue