Merge pull request #1094 from aaronlehmann/correct-payload-serialization-order

Correct unmarshal order for SignedManifest
pull/1103/head
Richard Scothern 2015-10-15 11:44:12 -07:00
commit d87cf10852
4 changed files with 77 additions and 41 deletions

View File

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

View File

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

View File

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

View File

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