Add API unit testing for schema2 manifest
Signed-off-by: Aaron Lehmann <aaron.lehmann@docker.com>master
							parent
							
								
									3f746a8207
								
							
						
					
					
						commit
						9284810356
					
				|  | @ -18,11 +18,13 @@ import ( | |||
| 	"strings" | ||||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/docker/distribution" | ||||
| 	"github.com/docker/distribution/configuration" | ||||
| 	"github.com/docker/distribution/context" | ||||
| 	"github.com/docker/distribution/digest" | ||||
| 	"github.com/docker/distribution/manifest" | ||||
| 	"github.com/docker/distribution/manifest/schema1" | ||||
| 	"github.com/docker/distribution/manifest/schema2" | ||||
| 	"github.com/docker/distribution/registry/api/errcode" | ||||
| 	"github.com/docker/distribution/registry/api/v2" | ||||
| 	_ "github.com/docker/distribution/registry/storage/driver/inmemory" | ||||
|  | @ -690,48 +692,40 @@ func httpDelete(url string) (*http.Response, error) { | |||
| } | ||||
| 
 | ||||
| type manifestArgs struct { | ||||
| 	imageName      string | ||||
| 	signedManifest *schema1.SignedManifest | ||||
| 	dgst           digest.Digest | ||||
| } | ||||
| 
 | ||||
| func makeManifestArgs(t *testing.T) manifestArgs { | ||||
| 	args := manifestArgs{ | ||||
| 		imageName: "foo/bar", | ||||
| 	} | ||||
| 
 | ||||
| 	return args | ||||
| 	imageName string | ||||
| 	mediaType string | ||||
| 	manifest  distribution.Manifest | ||||
| 	dgst      digest.Digest | ||||
| } | ||||
| 
 | ||||
| func TestManifestAPI(t *testing.T) { | ||||
| 	deleteEnabled := false | ||||
| 	env := newTestEnv(t, deleteEnabled) | ||||
| 	args := makeManifestArgs(t) | ||||
| 	testManifestAPI(t, env, args) | ||||
| 	testManifestAPISchema1(t, env, "foo/schema1") | ||||
| 	testManifestAPISchema2(t, env, "foo/schema2") | ||||
| 
 | ||||
| 	deleteEnabled = true | ||||
| 	env = newTestEnv(t, deleteEnabled) | ||||
| 	args = makeManifestArgs(t) | ||||
| 	testManifestAPI(t, env, args) | ||||
| 	testManifestAPISchema1(t, env, "foo/schema1") | ||||
| 	testManifestAPISchema2(t, env, "foo/schema2") | ||||
| } | ||||
| 
 | ||||
| func TestManifestDelete(t *testing.T) { | ||||
| 	deleteEnabled := true | ||||
| 	env := newTestEnv(t, deleteEnabled) | ||||
| 	args := makeManifestArgs(t) | ||||
| 	env, args = testManifestAPI(t, env, args) | ||||
| 	testManifestDelete(t, env, args) | ||||
| 	schema1Args := testManifestAPISchema1(t, env, "foo/schema1") | ||||
| 	testManifestDelete(t, env, schema1Args) | ||||
| 	schema2Args := testManifestAPISchema2(t, env, "foo/schema2") | ||||
| 	testManifestDelete(t, env, schema2Args) | ||||
| } | ||||
| 
 | ||||
| func TestManifestDeleteDisabled(t *testing.T) { | ||||
| 	deleteEnabled := false | ||||
| 	env := newTestEnv(t, deleteEnabled) | ||||
| 	args := makeManifestArgs(t) | ||||
| 	testManifestDeleteDisabled(t, env, args) | ||||
| 	testManifestDeleteDisabled(t, env, "foo/schema1") | ||||
| } | ||||
| 
 | ||||
| func testManifestDeleteDisabled(t *testing.T, env *testEnv, args manifestArgs) *testEnv { | ||||
| 	imageName := args.imageName | ||||
| func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName string) { | ||||
| 	manifestURL, err := env.builder.BuildManifestURL(imageName, digest.DigestSha256EmptyTar) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting manifest url: %v", err) | ||||
|  | @ -744,12 +738,11 @@ func testManifestDeleteDisabled(t *testing.T, env *testEnv, args manifestArgs) * | |||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "status of disabled delete of manifest", resp, http.StatusMethodNotAllowed) | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, manifestArgs) { | ||||
| 	imageName := args.imageName | ||||
| func testManifestAPISchema1(t *testing.T, env *testEnv, imageName string) manifestArgs { | ||||
| 	tag := "thetag" | ||||
| 	args := manifestArgs{imageName: imageName} | ||||
| 
 | ||||
| 	manifestURL, err := env.builder.BuildManifestURL(imageName, tag) | ||||
| 	if err != nil { | ||||
|  | @ -808,10 +801,10 @@ 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() | ||||
| 	checkResponse(t, "putting unsigned manifest", resp, http.StatusBadRequest) | ||||
| 	_, p, counts := checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeManifestInvalid) | ||||
| 	_, p, counts := checkBodyHasErrorCodes(t, "putting unsigned manifest", resp, v2.ErrorCodeManifestInvalid) | ||||
| 
 | ||||
| 	expectedCounts := map[errcode.ErrorCode]int{ | ||||
| 		v2.ErrorCodeManifestInvalid: 1, | ||||
|  | @ -827,7 +820,7 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 		t.Fatalf("error signing manifest: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	resp = putManifest(t, "putting signed manifest with errors", manifestURL, sm) | ||||
| 	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, | ||||
|  | @ -872,13 +865,13 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 	} | ||||
| 
 | ||||
| 	dgst := digest.FromBytes(signedManifest.Canonical) | ||||
| 	args.signedManifest = signedManifest | ||||
| 	args.manifest = signedManifest | ||||
| 	args.dgst = dgst | ||||
| 
 | ||||
| 	manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String()) | ||||
| 	checkErr(t, err, "building manifest url") | ||||
| 
 | ||||
| 	resp = putManifest(t, "putting signed manifest no error", manifestURL, signedManifest) | ||||
| 	resp = putManifest(t, "putting signed manifest no error", manifestURL, "", signedManifest) | ||||
| 	checkResponse(t, "putting signed manifest no error", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
|  | @ -887,7 +880,7 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 
 | ||||
| 	// --------------------
 | ||||
| 	// Push by digest -- should get same result
 | ||||
| 	resp = putManifest(t, "putting signed manifest", manifestDigestURL, signedManifest) | ||||
| 	resp = putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest) | ||||
| 	checkResponse(t, "putting signed manifest", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
|  | @ -958,7 +951,7 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| 	resp = putManifest(t, "re-putting signed manifest", manifestDigestURL, sm2) | ||||
| 	resp = putManifest(t, "re-putting signed manifest", manifestDigestURL, "", sm2) | ||||
| 	checkResponse(t, "re-putting signed manifest", resp, http.StatusCreated) | ||||
| 
 | ||||
| 	resp, err = http.Get(manifestDigestURL) | ||||
|  | @ -1020,8 +1013,7 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	// Check that we get an unknown repository error when asking for tags
 | ||||
| 	checkResponse(t, "getting unknown manifest tags", resp, http.StatusOK) | ||||
| 	checkResponse(t, "getting tags", resp, http.StatusOK) | ||||
| 	dec = json.NewDecoder(resp.Body) | ||||
| 
 | ||||
| 	var tagsResponse tagsAPIResponse | ||||
|  | @ -1052,16 +1044,359 @@ func testManifestAPI(t *testing.T, env *testEnv, args manifestArgs) (*testEnv, m | |||
| 		t.Fatalf("error signing manifest") | ||||
| 	} | ||||
| 
 | ||||
| 	resp = putManifest(t, "putting invalid signed manifest", manifestDigestURL, invalidSigned) | ||||
| 	resp = putManifest(t, "putting invalid signed manifest", manifestDigestURL, "", invalidSigned) | ||||
| 	checkResponse(t, "putting invalid signed manifest", resp, http.StatusBadRequest) | ||||
| 
 | ||||
| 	return env, args | ||||
| 	return args | ||||
| } | ||||
| 
 | ||||
| func testManifestAPISchema2(t *testing.T, env *testEnv, imageName string) manifestArgs { | ||||
| 	tag := "schema2tag" | ||||
| 	args := manifestArgs{ | ||||
| 		imageName: imageName, | ||||
| 		mediaType: schema2.MediaTypeManifest, | ||||
| 	} | ||||
| 
 | ||||
| 	manifestURL, err := env.builder.BuildManifestURL(imageName, tag) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting manifest url: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// -----------------------------
 | ||||
| 	// Attempt to fetch the manifest
 | ||||
| 	resp, err := http.Get(manifestURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting manifest: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "getting non-existent manifest", resp, http.StatusNotFound) | ||||
| 	checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, v2.ErrorCodeManifestUnknown) | ||||
| 
 | ||||
| 	tagsURL, err := env.builder.BuildTagsURL(imageName) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error building tags url: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	resp, err = http.Get(tagsURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting unknown tags: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	// Check that we get an unknown repository error when asking for tags
 | ||||
| 	checkResponse(t, "getting unknown manifest tags", resp, http.StatusNotFound) | ||||
| 	checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeNameUnknown) | ||||
| 
 | ||||
| 	// --------------------------------
 | ||||
| 	// Attempt to push manifest with missing config and missing layers
 | ||||
| 	manifest := &schema2.Manifest{ | ||||
| 		Versioned: manifest.Versioned{ | ||||
| 			SchemaVersion: 2, | ||||
| 		}, | ||||
| 		MediaType: schema2.MediaTypeManifest, | ||||
| 		Config: distribution.Descriptor{ | ||||
| 			Digest:    "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b", | ||||
| 			Size:      3253, | ||||
| 			MediaType: schema2.MediaTypeConfig, | ||||
| 		}, | ||||
| 		Layers: []distribution.Descriptor{ | ||||
| 			{ | ||||
| 				Digest:    "sha256:463434349086340864309863409683460843608348608934092322395278926a", | ||||
| 				Size:      6323, | ||||
| 				MediaType: schema2.MediaTypeLayer, | ||||
| 			}, | ||||
| 			{ | ||||
| 				Digest:    "sha256:630923423623623423352523525237238023652897356239852383652aaaaaaa", | ||||
| 				Size:      6863, | ||||
| 				MediaType: schema2.MediaTypeLayer, | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	resp = putManifest(t, "putting missing config manifest", manifestURL, schema2.MediaTypeManifest, manifest) | ||||
| 	defer resp.Body.Close() | ||||
| 	checkResponse(t, "putting missing config manifest", resp, http.StatusBadRequest) | ||||
| 	_, p, counts := checkBodyHasErrorCodes(t, "putting missing config manifest", resp, v2.ErrorCodeManifestBlobUnknown) | ||||
| 
 | ||||
| 	expectedCounts := map[errcode.ErrorCode]int{ | ||||
| 		v2.ErrorCodeManifestBlobUnknown: 3, | ||||
| 	} | ||||
| 
 | ||||
| 	if !reflect.DeepEqual(counts, expectedCounts) { | ||||
| 		t.Fatalf("unexpected number of error codes encountered: %v\n!=\n%v\n---\n%s", counts, expectedCounts, string(p)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Push a config, and reference it in the manifest
 | ||||
| 	sampleConfig := []byte(`{ | ||||
| 		"architecture": "amd64", | ||||
| 		"history": [ | ||||
| 		  { | ||||
| 		    "created": "2015-10-31T22:22:54.690851953Z", | ||||
| 		    "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /" | ||||
| 		  }, | ||||
| 		  { | ||||
| 		    "created": "2015-10-31T22:22:55.613815829Z", | ||||
| 		    "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]" | ||||
| 		  } | ||||
| 		], | ||||
| 		"rootfs": { | ||||
| 		  "diff_ids": [ | ||||
| 		    "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1", | ||||
| 		    "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" | ||||
| 		  ], | ||||
| 		  "type": "layers" | ||||
| 		} | ||||
| 	}`) | ||||
| 	sampleConfigDigest := digest.FromBytes(sampleConfig) | ||||
| 
 | ||||
| 	uploadURLBase, _ := startPushLayer(t, env.builder, imageName) | ||||
| 	pushLayer(t, env.builder, imageName, sampleConfigDigest, uploadURLBase, bytes.NewReader(sampleConfig)) | ||||
| 	manifest.Config.Digest = sampleConfigDigest | ||||
| 	manifest.Config.Size = int64(len(sampleConfig)) | ||||
| 
 | ||||
| 	// The manifest should still be invalid, because its layer doesnt exist
 | ||||
| 	resp = putManifest(t, "putting missing layer manifest", manifestURL, schema2.MediaTypeManifest, manifest) | ||||
| 	defer resp.Body.Close() | ||||
| 	checkResponse(t, "putting missing layer manifest", resp, http.StatusBadRequest) | ||||
| 	_, p, counts = checkBodyHasErrorCodes(t, "getting unknown manifest tags", resp, v2.ErrorCodeManifestBlobUnknown) | ||||
| 
 | ||||
| 	expectedCounts = map[errcode.ErrorCode]int{ | ||||
| 		v2.ErrorCodeManifestBlobUnknown: 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)) | ||||
| 	} | ||||
| 
 | ||||
| 	// Push 2 random layers
 | ||||
| 	expectedLayers := make(map[digest.Digest]io.ReadSeeker) | ||||
| 
 | ||||
| 	for i := range manifest.Layers { | ||||
| 		rs, dgstStr, err := testutil.CreateRandomTarFile() | ||||
| 
 | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("error creating random layer %d: %v", i, err) | ||||
| 		} | ||||
| 		dgst := digest.Digest(dgstStr) | ||||
| 
 | ||||
| 		expectedLayers[dgst] = rs | ||||
| 		manifest.Layers[i].Digest = dgst | ||||
| 
 | ||||
| 		uploadURLBase, _ := startPushLayer(t, env.builder, imageName) | ||||
| 		pushLayer(t, env.builder, imageName, dgst, uploadURLBase, rs) | ||||
| 	} | ||||
| 
 | ||||
| 	// -------------------
 | ||||
| 	// Push the manifest with all layers pushed.
 | ||||
| 	deserializedManifest, err := schema2.FromStruct(*manifest) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("could not create DeserializedManifest: %v", err) | ||||
| 	} | ||||
| 	_, canonical, err := deserializedManifest.Payload() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("could not get manifest payload: %v", err) | ||||
| 	} | ||||
| 	dgst := digest.FromBytes(canonical) | ||||
| 	args.dgst = dgst | ||||
| 	args.manifest = deserializedManifest | ||||
| 
 | ||||
| 	manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String()) | ||||
| 	checkErr(t, err, "building manifest url") | ||||
| 
 | ||||
| 	resp = putManifest(t, "putting manifest no error", manifestURL, schema2.MediaTypeManifest, manifest) | ||||
| 	checkResponse(t, "putting manifest no error", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 	}) | ||||
| 
 | ||||
| 	// --------------------
 | ||||
| 	// Push by digest -- should get same result
 | ||||
| 	resp = putManifest(t, "putting manifest by digest", manifestDigestURL, schema2.MediaTypeManifest, manifest) | ||||
| 	checkResponse(t, "putting manifest by digest", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 	}) | ||||
| 
 | ||||
| 	// ------------------
 | ||||
| 	// Fetch by tag name
 | ||||
| 	req, err := http.NewRequest("GET", manifestURL, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 	req.Header.Set("Accept", schema2.MediaTypeManifest) | ||||
| 	resp, err = http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error fetching manifest: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 		"ETag":                  []string{fmt.Sprintf(`"%s"`, dgst)}, | ||||
| 	}) | ||||
| 
 | ||||
| 	var fetchedManifest schema2.DeserializedManifest | ||||
| 	dec := json.NewDecoder(resp.Body) | ||||
| 
 | ||||
| 	if err := dec.Decode(&fetchedManifest); err != nil { | ||||
| 		t.Fatalf("error decoding fetched manifest: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	_, fetchedCanonical, err := fetchedManifest.Payload() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error getting manifest payload: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !bytes.Equal(fetchedCanonical, canonical) { | ||||
| 		t.Fatalf("manifests do not match") | ||||
| 	} | ||||
| 
 | ||||
| 	// ---------------
 | ||||
| 	// Fetch by digest
 | ||||
| 	req, err = http.NewRequest("GET", manifestDigestURL, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 	req.Header.Set("Accept", schema2.MediaTypeManifest) | ||||
| 	resp, err = http.DefaultClient.Do(req) | ||||
| 	checkErr(t, err, "fetching manifest by digest") | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "fetching uploaded manifest", resp, http.StatusOK) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 		"ETag":                  []string{fmt.Sprintf(`"%s"`, dgst)}, | ||||
| 	}) | ||||
| 
 | ||||
| 	var fetchedManifestByDigest schema2.DeserializedManifest | ||||
| 	dec = json.NewDecoder(resp.Body) | ||||
| 	if err := dec.Decode(&fetchedManifestByDigest); err != nil { | ||||
| 		t.Fatalf("error decoding fetched manifest: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	_, fetchedCanonical, err = fetchedManifest.Payload() | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error getting manifest payload: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if !bytes.Equal(fetchedCanonical, canonical) { | ||||
| 		t.Fatalf("manifests do not match") | ||||
| 	} | ||||
| 
 | ||||
| 	// Get by name with etag, gives 304
 | ||||
| 	etag := resp.Header.Get("Etag") | ||||
| 	req, err = http.NewRequest("GET", manifestURL, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 	req.Header.Set("If-None-Match", etag) | ||||
| 	resp, err = http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	checkResponse(t, "fetching manifest by name with etag", resp, http.StatusNotModified) | ||||
| 
 | ||||
| 	// Get by digest with etag, gives 304
 | ||||
| 	req, err = http.NewRequest("GET", manifestDigestURL, nil) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 	req.Header.Set("If-None-Match", etag) | ||||
| 	resp, err = http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("Error constructing request: %s", err) | ||||
| 	} | ||||
| 
 | ||||
| 	checkResponse(t, "fetching manifest by dgst with etag", resp, http.StatusNotModified) | ||||
| 
 | ||||
| 	// Ensure that the tag is listed.
 | ||||
| 	resp, err = http.Get(tagsURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error getting unknown tags: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "getting unknown manifest tags", resp, http.StatusOK) | ||||
| 	dec = json.NewDecoder(resp.Body) | ||||
| 
 | ||||
| 	var tagsResponse tagsAPIResponse | ||||
| 
 | ||||
| 	if err := dec.Decode(&tagsResponse); err != nil { | ||||
| 		t.Fatalf("unexpected error decoding error response: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if tagsResponse.Name != imageName { | ||||
| 		t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName) | ||||
| 	} | ||||
| 
 | ||||
| 	if len(tagsResponse.Tags) != 1 { | ||||
| 		t.Fatalf("expected some tags in response: %v", tagsResponse.Tags) | ||||
| 	} | ||||
| 
 | ||||
| 	if tagsResponse.Tags[0] != tag { | ||||
| 		t.Fatalf("tag not as expected: %q != %q", tagsResponse.Tags[0], tag) | ||||
| 	} | ||||
| 
 | ||||
| 	// ------------------
 | ||||
| 	// Fetch as a schema1 manifest
 | ||||
| 	resp, err = http.Get(manifestURL) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("unexpected error fetching manifest as schema1: %v", err) | ||||
| 	} | ||||
| 	defer resp.Body.Close() | ||||
| 
 | ||||
| 	checkResponse(t, "fetching uploaded manifest as schema1", resp, http.StatusOK) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
| 		"ETag":                  []string{fmt.Sprintf(`"%s"`, dgst)}, | ||||
| 	}) | ||||
| 
 | ||||
| 	var fetchedSchema1Manifest schema1.SignedManifest | ||||
| 	dec = json.NewDecoder(resp.Body) | ||||
| 
 | ||||
| 	if err := dec.Decode(&fetchedSchema1Manifest); err != nil { | ||||
| 		t.Fatalf("error decoding fetched schema1 manifest: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	if fetchedSchema1Manifest.Manifest.SchemaVersion != 1 { | ||||
| 		t.Fatal("wrong schema version") | ||||
| 	} | ||||
| 	if fetchedSchema1Manifest.Architecture != "amd64" { | ||||
| 		t.Fatal("wrong architecture") | ||||
| 	} | ||||
| 	if fetchedSchema1Manifest.Name != imageName { | ||||
| 		t.Fatal("wrong image name") | ||||
| 	} | ||||
| 	if fetchedSchema1Manifest.Tag != tag { | ||||
| 		t.Fatal("wrong tag") | ||||
| 	} | ||||
| 	if len(fetchedSchema1Manifest.FSLayers) != 2 { | ||||
| 		t.Fatal("wrong number of FSLayers") | ||||
| 	} | ||||
| 	for i := range manifest.Layers { | ||||
| 		if fetchedSchema1Manifest.FSLayers[i].BlobSum != manifest.Layers[len(manifest.Layers)-i-1].Digest { | ||||
| 			t.Fatalf("blob digest mismatch in schema1 manifest for layer %d", i) | ||||
| 		} | ||||
| 	} | ||||
| 	if len(fetchedSchema1Manifest.History) != 2 { | ||||
| 		t.Fatal("wrong number of History entries") | ||||
| 	} | ||||
| 
 | ||||
| 	// Don't check V1Compatibility fields becuase we're using randomly-generated
 | ||||
| 	// layers.
 | ||||
| 
 | ||||
| 	return args | ||||
| } | ||||
| 
 | ||||
| func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) { | ||||
| 	imageName := args.imageName | ||||
| 	dgst := args.dgst | ||||
| 	signedManifest := args.signedManifest | ||||
| 	manifest := args.manifest | ||||
| 	manifestDigestURL, err := env.builder.BuildManifestURL(imageName, dgst.String()) | ||||
| 	// ---------------
 | ||||
| 	// Delete by digest
 | ||||
|  | @ -1090,8 +1425,8 @@ func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) { | |||
| 
 | ||||
| 	// --------------------
 | ||||
| 	// Re-upload manifest by digest
 | ||||
| 	resp = putManifest(t, "putting signed manifest", manifestDigestURL, signedManifest) | ||||
| 	checkResponse(t, "putting signed manifest", resp, http.StatusCreated) | ||||
| 	resp = putManifest(t, "putting manifest", manifestDigestURL, args.mediaType, manifest) | ||||
| 	checkResponse(t, "putting manifest", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{manifestDigestURL}, | ||||
| 		"Docker-Content-Digest": []string{dgst.String()}, | ||||
|  | @ -1183,7 +1518,7 @@ func newTestEnvWithConfig(t *testing.T, config *configuration.Configuration) *te | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| func putManifest(t *testing.T, msg, url string, v interface{}) *http.Response { | ||||
| func putManifest(t *testing.T, msg, url, contentType string, v interface{}) *http.Response { | ||||
| 	var body []byte | ||||
| 
 | ||||
| 	if sm, ok := v.(*schema1.SignedManifest); ok { | ||||
|  | @ -1205,6 +1540,10 @@ func putManifest(t *testing.T, msg, url string, v interface{}) *http.Response { | |||
| 		t.Fatalf("error creating request for %s: %v", msg, err) | ||||
| 	} | ||||
| 
 | ||||
| 	if contentType != "" { | ||||
| 		req.Header.Set("Content-Type", contentType) | ||||
| 	} | ||||
| 
 | ||||
| 	resp, err := http.DefaultClient.Do(req) | ||||
| 	if err != nil { | ||||
| 		t.Fatalf("error doing put request while %s: %v", msg, err) | ||||
|  | @ -1532,7 +1871,7 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string) | |||
| 	location, err := env.builder.BuildManifestURL(imageName, dgst.String()) | ||||
| 	checkErr(t, err, "building location URL") | ||||
| 
 | ||||
| 	resp := putManifest(t, "putting signed manifest", manifestDigestURL, signedManifest) | ||||
| 	resp := putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest) | ||||
| 	checkResponse(t, "putting signed manifest", resp, http.StatusCreated) | ||||
| 	checkHeaders(t, resp, http.Header{ | ||||
| 		"Location":              []string{location}, | ||||
|  | @ -1570,7 +1909,7 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) { | |||
| 		t.Fatalf("error signing manifest: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	resp := putManifest(t, "putting unsigned manifest", manifestURL, sm) | ||||
| 	resp := putManifest(t, "putting unsigned manifest", manifestURL, "", sm) | ||||
| 	checkResponse(t, "putting signed manifest to cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode) | ||||
| 
 | ||||
| 	// Manifest Delete
 | ||||
|  |  | |||
|  | @ -86,7 +86,7 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http | |||
| 	// Only rewrite schema2 manifests when they are being fetched by tag.
 | ||||
| 	// If they are being fetched by digest, we can't return something not
 | ||||
| 	// matching the digest.
 | ||||
| 	if _, isSchema2 := manifest.(*schema2.DeserializedManifest); imh.Tag != "" && isSchema2 { | ||||
| 	if schema2Manifest, isSchema2 := manifest.(*schema2.DeserializedManifest); imh.Tag != "" && isSchema2 { | ||||
| 		supportsSchema2 := false | ||||
| 		if acceptHeaders, ok := r.Header["Accept"]; ok { | ||||
| 			for _, mediaType := range acceptHeaders { | ||||
|  | @ -101,7 +101,7 @@ func (imh *imageManifestHandler) GetImageManifest(w http.ResponseWriter, r *http | |||
| 			// Rewrite manifest in schema1 format
 | ||||
| 			ctxu.GetLogger(imh).Infof("rewriting manifest %s in schema1 format to support old client", imh.Digest.String()) | ||||
| 
 | ||||
| 			targetDescriptor := manifest.Target() | ||||
| 			targetDescriptor := schema2Manifest.Target() | ||||
| 			blobs := imh.Repository.Blobs(imh) | ||||
| 			configJSON, err := blobs.Get(imh, targetDescriptor.Digest) | ||||
| 			if err != nil { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue