Merge pull request #3987 from milosgajdos/remove-schema1-handlers

Remove references to schema1 pacakge from handlers
This commit is contained in:
Hayley Swimelar 2023-08-17 14:18:48 -07:00 committed by GitHub
commit ac7a334069
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 99 additions and 598 deletions

View file

@ -23,7 +23,6 @@ import (
"github.com/distribution/distribution/v3/configuration" "github.com/distribution/distribution/v3/configuration"
"github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest"
"github.com/distribution/distribution/v3/manifest/manifestlist" "github.com/distribution/distribution/v3/manifest/manifestlist"
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
"github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/manifest/schema2"
"github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/reference"
"github.com/distribution/distribution/v3/registry/api/errcode" "github.com/distribution/distribution/v3/registry/api/errcode"
@ -1245,21 +1244,18 @@ type manifestArgs struct {
} }
func TestManifestAPI(t *testing.T) { func TestManifestAPI(t *testing.T) {
schema1Repo, _ := reference.WithName("foo/schema1")
schema2Repo, _ := reference.WithName("foo/schema2") schema2Repo, _ := reference.WithName("foo/schema2")
deleteEnabled := false deleteEnabled := false
env1 := newTestEnv(t, deleteEnabled) env1 := newTestEnv(t, deleteEnabled)
defer env1.Shutdown() defer env1.Shutdown()
testManifestAPISchema1(t, env1, schema1Repo) schema2Args := testManifestAPISchema2(t, env1, schema2Repo, "schema2tag")
schema2Args := testManifestAPISchema2(t, env1, schema2Repo)
testManifestAPIManifestList(t, env1, schema2Args) testManifestAPIManifestList(t, env1, schema2Args)
deleteEnabled = true deleteEnabled = true
env2 := newTestEnv(t, deleteEnabled) env2 := newTestEnv(t, deleteEnabled)
defer env2.Shutdown() defer env2.Shutdown()
testManifestAPISchema1(t, env2, schema1Repo) schema2Args = testManifestAPISchema2(t, env2, schema2Repo, "schema2tag")
schema2Args = testManifestAPISchema2(t, env2, schema2Repo)
testManifestAPIManifestList(t, env2, schema2Args) testManifestAPIManifestList(t, env2, schema2Args)
} }
@ -1438,24 +1434,21 @@ func TestGetManifestWithStorageError(t *testing.T) {
} }
func TestManifestDelete(t *testing.T) { func TestManifestDelete(t *testing.T) {
schema1Repo, _ := reference.WithName("foo/schema1")
schema2Repo, _ := reference.WithName("foo/schema2") schema2Repo, _ := reference.WithName("foo/schema2")
deleteEnabled := true deleteEnabled := true
env := newTestEnv(t, deleteEnabled) env := newTestEnv(t, deleteEnabled)
defer env.Shutdown() defer env.Shutdown()
schema1Args := testManifestAPISchema1(t, env, schema1Repo) schema2Args := testManifestAPISchema2(t, env, schema2Repo, "schema2tag")
testManifestDelete(t, env, schema1Args)
schema2Args := testManifestAPISchema2(t, env, schema2Repo)
testManifestDelete(t, env, schema2Args) testManifestDelete(t, env, schema2Args)
} }
func TestManifestDeleteDisabled(t *testing.T) { func TestManifestDeleteDisabled(t *testing.T) {
schema1Repo, _ := reference.WithName("foo/schema1") schema2Repo, _ := reference.WithName("foo/schema2")
deleteEnabled := false deleteEnabled := false
env := newTestEnv(t, deleteEnabled) env := newTestEnv(t, deleteEnabled)
defer env.Shutdown() defer env.Shutdown()
testManifestDeleteDisabled(t, env, schema1Repo) testManifestDeleteDisabled(t, env, schema2Repo)
} }
func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName reference.Named) { func testManifestDeleteDisabled(t *testing.T, env *testEnv, imageName reference.Named) {
@ -1493,324 +1486,7 @@ func testManifestWithStorageError(t *testing.T, env *testEnv, imageName referenc
checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, expectedErrorCode) checkBodyHasErrorCodes(t, "getting non-existent manifest", resp, expectedErrorCode)
} }
func testManifestAPISchema1(t *testing.T, env *testEnv, imageName reference.Named) manifestArgs { func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Named, tag string) manifestArgs {
tag := "thetag"
args := manifestArgs{imageName: imageName}
tagRef, _ := reference.WithTag(imageName, tag)
manifestURL, err := env.builder.BuildManifestURL(tagRef)
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 unsigned manifest with missing layers
unsignedManifest := &schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
Versioned: manifest.Versioned{
SchemaVersion: 1,
},
Name: imageName.Name(),
Tag: tag,
FSLayers: []schema1.FSLayer{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
{
BlobSum: "asdf",
},
{
BlobSum: "qwer",
},
},
History: []schema1.History{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
{
V1Compatibility: "",
},
{
V1Compatibility: "",
},
},
}
resp = putManifest(t, "putting unsigned manifest", manifestURL, "", unsignedManifest)
defer resp.Body.Close()
checkResponse(t, "putting unsigned manifest", resp, http.StatusBadRequest)
_, p, counts := checkBodyHasErrorCodes(t, "putting unsigned manifest", resp, v2.ErrorCodeManifestInvalid)
expectedCounts := map[errcode.ErrorCode]int{
v2.ErrorCodeManifestInvalid: 1,
}
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) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
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) {
t.Fatalf("unexpected number of error codes encountered: %v\n!=\n%v\n---\n%s", counts, expectedCounts, string(p))
}
// TODO(stevvooe): Add a test case where we take a mostly valid registry,
// tamper with the content and ensure that we get an unverified manifest
// error.
// Push 2 random layers
expectedLayers := make(map[digest.Digest]io.ReadSeeker)
for i := range unsignedManifest.FSLayers {
rs, dgst, err := testutil.CreateRandomTarFile()
if err != nil {
t.Fatalf("error creating random layer %d: %v", i, err)
}
expectedLayers[dgst] = rs
unsignedManifest.FSLayers[i].BlobSum = dgst
uploadURLBase, _ := startPushLayer(t, env, imageName)
pushLayer(t, env.builder, imageName, dgst, uploadURLBase, rs)
}
// -------------------
// Push the signed manifest with all layers pushed.
signedManifest, err := schema1.Sign(unsignedManifest, env.pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatalf("unexpected error signing manifest: %v", err)
}
dgst := digest.FromBytes(signedManifest.Canonical)
args.manifest = signedManifest
args.dgst = dgst
digestRef, _ := reference.WithDigest(imageName, dgst)
manifestDigestURL, err := env.builder.BuildManifestURL(digestRef)
checkErr(t, err, "building manifest url")
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},
"Docker-Content-Digest": []string{dgst.String()},
})
// --------------------
// Push by digest -- should get same result
resp = putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest)
checkResponse(t, "putting signed manifest", resp, http.StatusCreated)
checkHeaders(t, resp, http.Header{
"Location": []string{manifestDigestURL},
"Docker-Content-Digest": []string{dgst.String()},
})
// ------------------
// Fetch by tag name
resp, err = http.Get(manifestURL)
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 schema1.SignedManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&fetchedManifest); err != nil {
t.Fatalf("error decoding fetched manifest: %v", err)
}
if !bytes.Equal(fetchedManifest.Canonical, signedManifest.Canonical) {
t.Fatalf("manifests do not match")
}
// ---------------
// Fetch by digest
resp, err = http.Get(manifestDigestURL)
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 schema1.SignedManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
dec = json.NewDecoder(resp.Body)
if err := dec.Decode(&fetchedManifestByDigest); err != nil {
t.Fatalf("error decoding fetched manifest: %v", err)
}
if !bytes.Equal(fetchedManifestByDigest.Canonical, signedManifest.Canonical) {
t.Fatalf("manifests do not match")
}
// check signature was roundtripped
signatures, err := fetchedManifestByDigest.Signatures() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatal(err)
}
if len(signatures) != 1 {
t.Fatalf("expected 1 signature from manifest, got: %d", len(signatures))
}
// Re-sign, push and pull the same digest
sm2, err := schema1.Sign(&fetchedManifestByDigest.Manifest, env.pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatal(err)
}
// Re-push with a few different Content-Types. The official schema1
// content type should work, as should application/json with/without a
// charset.
resp = putManifest(t, "re-putting signed manifest", manifestDigestURL, schema1.MediaTypeSignedManifest, sm2) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
checkResponse(t, "re-putting signed manifest", resp, http.StatusCreated)
resp = putManifest(t, "re-putting signed manifest", manifestDigestURL, "application/json", sm2)
checkResponse(t, "re-putting signed manifest", resp, http.StatusCreated)
resp = putManifest(t, "re-putting signed manifest", manifestDigestURL, "application/json", sm2)
checkResponse(t, "re-putting signed manifest", resp, http.StatusCreated)
resp, err = http.Get(manifestDigestURL)
checkErr(t, err, "re-fetching manifest by digest")
defer resp.Body.Close()
checkResponse(t, "re-fetching uploaded manifest", resp, http.StatusOK)
checkHeaders(t, resp, http.Header{
"Docker-Content-Digest": []string{dgst.String()},
"ETag": []string{fmt.Sprintf(`"%s"`, dgst)},
})
dec = json.NewDecoder(resp.Body)
if err := dec.Decode(&fetchedManifestByDigest); err != nil {
t.Fatalf("error decoding fetched manifest: %v", err)
}
// check only 1 signature is returned
signatures, err = fetchedManifestByDigest.Signatures() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatal(err)
}
if len(signatures) != 1 {
t.Fatalf("expected 2 signature from manifest, got: %d", len(signatures))
}
// Get by name with etag, gives 304
etag := resp.Header.Get("Etag")
req, err := http.NewRequest(http.MethodGet, 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(http.MethodGet, 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 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.Name() {
t.Fatalf("tags name should match image name: %v != %v", tagsResponse.Name, imageName.Name())
}
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)
}
// Attempt to put a manifest with mismatching FSLayer and History array cardinalities
unsignedManifest.History = append(unsignedManifest.History, schema1.History{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
V1Compatibility: "",
})
invalidSigned, err := schema1.Sign(unsignedManifest, env.pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatalf("error signing manifest")
}
resp = putManifest(t, "putting invalid signed manifest", manifestDigestURL, "", invalidSigned)
checkResponse(t, "putting invalid signed manifest", resp, http.StatusBadRequest)
return args
}
func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Named) manifestArgs {
tag := "schema2tag"
args := manifestArgs{ args := manifestArgs{
imageName: imageName, imageName: imageName,
mediaType: schema2.MediaTypeManifest, mediaType: schema2.MediaTypeManifest,
@ -2101,63 +1777,6 @@ func testManifestAPISchema2(t *testing.T, env *testEnv, imageName reference.Name
t.Fatalf("tag not as expected: %q != %q", 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()
manifestBytes, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("error reading response body: %v", err)
}
checkResponse(t, "fetching uploaded manifest as schema1", resp, http.StatusOK)
m, desc, err := distribution.UnmarshalManifest(schema1.MediaTypeManifest, manifestBytes) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatalf("unexpected error unmarshalling manifest: %v", err)
}
fetchedSchema1Manifest, ok := m.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if !ok {
t.Fatalf("expecting schema1 manifest")
}
checkHeaders(t, resp, http.Header{
"Docker-Content-Digest": []string{desc.Digest.String()},
"ETag": []string{fmt.Sprintf(`"%s"`, desc.Digest)},
})
if fetchedSchema1Manifest.Manifest.SchemaVersion != 1 {
t.Fatal("wrong schema version")
}
if fetchedSchema1Manifest.Architecture != "amd64" {
t.Fatal("wrong architecture")
}
if fetchedSchema1Manifest.Name != imageName.Name() {
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 because we're using randomly-generated
// layers.
return args return args
} }
@ -2246,7 +1865,7 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
t.Fatalf("Error constructing request: %s", err) t.Fatalf("Error constructing request: %s", err)
} }
// multiple headers in mixed list format to ensure we parse correctly server-side // multiple headers in mixed list format to ensure we parse correctly server-side
req.Header.Set("Accept", fmt.Sprintf(` %s ; q=0.8 , %s ; q=0.5 `, manifestlist.MediaTypeManifestList, schema1.MediaTypeSignedManifest)) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. req.Header.Set("Accept", fmt.Sprintf(` %s ; q=0.8 `, manifestlist.MediaTypeManifestList))
req.Header.Add("Accept", schema2.MediaTypeManifest) req.Header.Add("Accept", schema2.MediaTypeManifest)
resp, err = http.DefaultClient.Do(req) resp, err = http.DefaultClient.Do(req)
if err != nil { if err != nil {
@ -2334,64 +1953,6 @@ func testManifestAPIManifestList(t *testing.T, env *testEnv, args manifestArgs)
} }
checkResponse(t, "fetching manifest by dgst with etag", resp, http.StatusNotModified) checkResponse(t, "fetching manifest by dgst with etag", resp, http.StatusNotModified)
// ------------------
// Fetch as a schema1 manifest
resp, err = http.Get(manifestURL)
if err != nil {
t.Fatalf("unexpected error fetching manifest list as schema1: %v", err)
}
defer resp.Body.Close()
manifestBytes, err := io.ReadAll(resp.Body)
if err != nil {
t.Fatalf("error reading response body: %v", err)
}
checkResponse(t, "fetching uploaded manifest list as schema1", resp, http.StatusOK)
m, desc, err := distribution.UnmarshalManifest(schema1.MediaTypeManifest, manifestBytes) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatalf("unexpected error unmarshalling manifest: %v", err)
}
fetchedSchema1Manifest, ok := m.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if !ok {
t.Fatalf("expecting schema1 manifest")
}
checkHeaders(t, resp, http.Header{
"Docker-Content-Digest": []string{desc.Digest.String()},
"ETag": []string{fmt.Sprintf(`"%s"`, desc.Digest)},
})
if fetchedSchema1Manifest.Manifest.SchemaVersion != 1 {
t.Fatal("wrong schema version")
}
if fetchedSchema1Manifest.Architecture != "amd64" {
t.Fatal("wrong architecture")
}
if fetchedSchema1Manifest.Name != imageName.Name() {
t.Fatal("wrong image name")
}
if fetchedSchema1Manifest.Tag != tag {
t.Fatal("wrong tag")
}
if len(fetchedSchema1Manifest.FSLayers) != 2 {
t.Fatal("wrong number of FSLayers")
}
layers := args.manifest.(*schema2.DeserializedManifest).Layers
for i := range layers {
if fetchedSchema1Manifest.FSLayers[i].BlobSum != layers[len(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 because we're using randomly-generated
// layers.
} }
func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) { func testManifestDelete(t *testing.T, env *testEnv, args manifestArgs) {
@ -2556,7 +2117,6 @@ func newTestEnvMirror(t *testing.T, deleteEnabled bool) *testEnv {
MaxEntries: 5, MaxEntries: 5,
}, },
} }
config.Compatibility.Schema1.Enabled = true //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
return newTestEnvWithConfig(t, &config) return newTestEnvWithConfig(t, &config)
} }
@ -2575,7 +2135,6 @@ func newTestEnv(t *testing.T, deleteEnabled bool) *testEnv {
}, },
} }
config.Compatibility.Schema1.Enabled = true //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
config.HTTP.Headers = headerConfig config.HTTP.Headers = headerConfig
return newTestEnvWithConfig(t, &config) return newTestEnvWithConfig(t, &config)
@ -2615,12 +2174,6 @@ func putManifest(t *testing.T, msg, url, contentType string, v interface{}) *htt
var body []byte var body []byte
switch m := v.(type) { switch m := v.(type) {
case *schema1.SignedManifest: //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
_, pl, err := m.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
if err != nil {
t.Fatalf("error getting payload: %v", err)
}
body = pl
case *manifestlist.DeserializedManifestList: case *manifestlist.DeserializedManifestList:
_, pl, err := m.Payload() _, pl, err := m.Payload()
if err != nil { if err != nil {
@ -2968,61 +2521,89 @@ func createRepository(env *testEnv, t *testing.T, imageName string, tag string)
t.Fatalf("unable to parse reference: %v", err) t.Fatalf("unable to parse reference: %v", err)
} }
unsignedManifest := &schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. tagRef, _ := reference.WithTag(imageNameRef, tag)
manifestURL, err := env.builder.BuildManifestURL(tagRef)
if err != nil {
t.Fatalf("unexpected error getting manifest url: %v", err)
}
manifest := &schema2.Manifest{
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 2,
MediaType: schema2.MediaTypeManifest,
}, },
Name: imageName, Config: distribution.Descriptor{
Tag: tag, Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
FSLayers: []schema1.FSLayer{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. Size: 3253,
{ MediaType: schema2.MediaTypeImageConfig,
BlobSum: "asdf",
},
}, },
History: []schema1.History{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. Layers: []distribution.Descriptor{
{ {
V1Compatibility: "", Digest: "sha256:463434349086340864309863409683460843608348608934092322395278926a",
Size: 6323,
MediaType: schema2.MediaTypeLayer,
}, },
}, },
} }
// Push 2 random layers // Push a config, and reference it in the manifest
expectedLayers := make(map[digest.Digest]io.ReadSeeker) sampleConfig := []byte(`{
"architecture": "amd64",
"history": [
{
"created": "2015-10-31T22:22:54.690851953Z",
"created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
},
],
"rootfs": {
"diff_ids": [
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
],
"type": "layers"
}
}`)
sampleConfigDigest := digest.FromBytes(sampleConfig)
for i := range unsignedManifest.FSLayers { uploadURLBase, _ := startPushLayer(t, env, imageNameRef)
pushLayer(t, env.builder, imageNameRef, sampleConfigDigest, uploadURLBase, bytes.NewReader(sampleConfig))
manifest.Config.Digest = sampleConfigDigest
manifest.Config.Size = int64(len(sampleConfig))
// Push random layers
for i := range manifest.Layers {
rs, dgst, err := testutil.CreateRandomTarFile() rs, dgst, err := testutil.CreateRandomTarFile()
if err != nil { if err != nil {
t.Fatalf("error creating random layer %d: %v", i, err) t.Fatalf("error creating random layer %d: %v", i, err)
} }
manifest.Layers[i].Digest = dgst
expectedLayers[dgst] = rs
unsignedManifest.FSLayers[i].BlobSum = dgst
uploadURLBase, _ := startPushLayer(t, env, imageNameRef) uploadURLBase, _ := startPushLayer(t, env, imageNameRef)
pushLayer(t, env.builder, imageNameRef, dgst, uploadURLBase, rs) pushLayer(t, env.builder, imageNameRef, dgst, uploadURLBase, rs)
} }
signedManifest, err := schema1.Sign(unsignedManifest, env.pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. // -------------------
// Push the manifest with all layers pushed.
deserializedManifest, err := schema2.FromStruct(*manifest)
if err != nil { if err != nil {
t.Fatalf("unexpected error signing manifest: %v", err) t.Fatalf("could not create DeserializedManifest: %v", err)
} }
_, canonical, err := deserializedManifest.Payload()
dgst := digest.FromBytes(signedManifest.Canonical) if err != nil {
t.Fatalf("could not get manifest payload: %v", err)
// Create this repository by tag to ensure the tag mapping is made in the registry }
tagRef, _ := reference.WithTag(imageNameRef, tag) dgst := digest.FromBytes(canonical)
manifestDigestURL, err := env.builder.BuildManifestURL(tagRef) digestRef, _ := reference.WithDigest(imageNameRef, dgst)
manifestDigestURL, err := env.builder.BuildManifestURL(digestRef)
checkErr(t, err, "building manifest url") checkErr(t, err, "building manifest url")
digestRef, _ := reference.WithDigest(imageNameRef, dgst) resp := putManifest(t, "putting manifest no error", manifestURL, schema2.MediaTypeManifest, manifest)
location, err := env.builder.BuildManifestURL(digestRef) checkResponse(t, "putting manifest no error", resp, http.StatusCreated)
checkErr(t, err, "building location URL")
resp := putManifest(t, "putting signed manifest", manifestDigestURL, "", signedManifest)
checkResponse(t, "putting signed manifest", resp, http.StatusCreated)
checkHeaders(t, resp, http.Header{ checkHeaders(t, resp, http.Header{
"Location": []string{location}, "Location": []string{manifestDigestURL},
"Docker-Content-Digest": []string{dgst.String()}, "Docker-Content-Digest": []string{dgst.String()},
}) })
return dgst return dgst
} }
@ -3042,27 +2623,36 @@ func TestRegistryAsCacheMutationAPIs(t *testing.T) {
} }
// Manifest upload // Manifest upload
m := &schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. manifest := &schema2.Manifest{
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 2,
MediaType: schema2.MediaTypeManifest,
},
Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 3253,
MediaType: schema2.MediaTypeImageConfig,
},
Layers: []distribution.Descriptor{
{
Digest: "sha256:463434349086340864309863409683460843608348608934092322395278926a",
Size: 6323,
MediaType: schema2.MediaTypeLayer,
},
{
Digest: "sha256:630923423623623423352523525237238023652897356239852383652aaaaaaa",
Size: 6863,
MediaType: schema2.MediaTypeLayer,
},
}, },
Name: imageName.Name(),
Tag: tag,
FSLayers: []schema1.FSLayer{}, //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
History: []schema1.History{}, //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
} }
sm, err := schema1.Sign(m, env.pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. resp := putManifest(t, "putting missing config manifest", manifestURL, schema2.MediaTypeManifest, manifest)
if err != nil { checkResponse(t, "putting missing config manifest", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
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)
// Manifest Delete // Manifest Delete
resp, _ = httpDelete(manifestURL) resp, _ = httpDelete(manifestURL)
checkResponse(t, "deleting signed manifest from cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode) checkResponse(t, "deleting config manifest from cache", resp, errcode.ErrorCodeUnsupported.Descriptor().HTTPStatusCode)
// Blob upload initialization // Blob upload initialization
layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName) layerUploadURL, err := env.builder.BuildBlobUploadURL(imageName)
@ -3094,7 +2684,6 @@ func TestProxyManifestGetByTag(t *testing.T) {
}}, }},
}, },
} }
truthConfig.Compatibility.Schema1.Enabled = true //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
truthConfig.HTTP.Headers = headerConfig truthConfig.HTTP.Headers = headerConfig
imageName, _ := reference.WithName("foo/bar") imageName, _ := reference.WithName("foo/bar")
@ -3113,7 +2702,6 @@ func TestProxyManifestGetByTag(t *testing.T) {
RemoteURL: truthEnv.server.URL, RemoteURL: truthEnv.server.URL,
}, },
} }
proxyConfig.Compatibility.Schema1.Enabled = true //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
proxyConfig.HTTP.Headers = headerConfig proxyConfig.HTTP.Headers = headerConfig
proxyEnv := newTestEnvWithConfig(t, &proxyConfig) proxyEnv := newTestEnvWithConfig(t, &proxyConfig)

View file

@ -40,7 +40,6 @@ import (
"github.com/distribution/distribution/v3/version" "github.com/distribution/distribution/v3/version"
events "github.com/docker/go-events" events "github.com/docker/go-events"
"github.com/docker/go-metrics" "github.com/docker/go-metrics"
"github.com/docker/libtrust"
"github.com/gomodule/redigo/redis" "github.com/gomodule/redigo/redis"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -79,11 +78,6 @@ type App struct {
redis *redis.Pool redis *redis.Pool
// trustKey is a deprecated key used to sign manifests converted to
// schema1 for backward compatibility. It should not be used for any
// other purposes.
trustKey libtrust.PrivateKey
// isCache is true if this registry is configured as a pull through cache // isCache is true if this registry is configured as a pull through cache
isCache bool isCache bool
@ -164,25 +158,6 @@ func NewApp(ctx context.Context, config *configuration.Configuration) *App {
app.configureLogHook(config) app.configureLogHook(config)
options := registrymiddleware.GetRegistryOptions() options := registrymiddleware.GetRegistryOptions()
if config.Compatibility.Schema1.TrustKey != "" {
app.trustKey, err = libtrust.LoadKeyFile(config.Compatibility.Schema1.TrustKey)
if err != nil {
panic(fmt.Sprintf(`could not load schema1 "signingkey" parameter: %v`, err))
}
} else {
// Generate an ephemeral key to be used for signing converted manifests
// for clients that don't support schema2.
app.trustKey, err = libtrust.GenerateECP256PrivateKey()
if err != nil {
panic(err)
}
}
options = append(options, storage.Schema1SigningKey(app.trustKey))
if config.Compatibility.Schema1.Enabled {
options = append(options, storage.EnableSchema1)
}
if config.HTTP.Host != "" { if config.HTTP.Host != "" {
u, err := url.Parse(config.HTTP.Host) u, err := url.Parse(config.HTTP.Host)

View file

@ -11,7 +11,6 @@ import (
dcontext "github.com/distribution/distribution/v3/context" dcontext "github.com/distribution/distribution/v3/context"
"github.com/distribution/distribution/v3/manifest/manifestlist" "github.com/distribution/distribution/v3/manifest/manifestlist"
"github.com/distribution/distribution/v3/manifest/ocischema" "github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/distribution/distribution/v3/manifest/schema1" //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
"github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/manifest/schema2"
"github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/reference"
"github.com/distribution/distribution/v3/registry/api/errcode" "github.com/distribution/distribution/v3/registry/api/errcode"
@ -23,8 +22,6 @@ import (
v1 "github.com/opencontainers/image-spec/specs-go/v1" v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
// These constants determine which architecture and OS to choose from a
// manifest list when downconverting it to a schema1 manifest.
const ( const (
defaultArch = "amd64" defaultArch = "amd64"
defaultOS = "linux" defaultOS = "linux"
@ -35,12 +32,11 @@ const (
type storageType int type storageType int
const ( const (
manifestSchema1 storageType = iota // 0 manifestSchema2 storageType = iota // 0
manifestSchema2 // 1 manifestlistSchema // 1
manifestlistSchema // 2 ociSchema // 2
ociSchema // 3 ociImageIndexSchema // 3
ociImageIndexSchema // 4 numStorageTypes // 4
numStorageTypes // 5
) )
// manifestDispatcher takes the request context and builds the // manifestDispatcher takes the request context and builds the
@ -151,12 +147,9 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
return return
} }
// determine the type of the returned manifest // determine the type of the returned manifest
manifestType := manifestSchema1 manifestType := manifestSchema2
schema2Manifest, isSchema2 := manifest.(*schema2.DeserializedManifest)
manifestList, isManifestList := manifest.(*manifestlist.DeserializedManifestList) manifestList, isManifestList := manifest.(*manifestlist.DeserializedManifestList)
if isSchema2 { if _, isOCImanifest := manifest.(*ocischema.DeserializedManifest); isOCImanifest {
manifestType = manifestSchema2
} else if _, isOCImanifest := manifest.(*ocischema.DeserializedManifest); isOCImanifest {
manifestType = ociSchema manifestType = ociSchema
} else if isManifestList { } else if isManifestList {
if manifestList.MediaType == manifestlist.MediaTypeManifestList { if manifestList.MediaType == manifestlist.MediaTypeManifestList {
@ -174,18 +167,8 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI index found, but accept header does not support OCI indexes")) imh.Errors = append(imh.Errors, v2.ErrorCodeManifestUnknown.WithMessage("OCI index found, but accept header does not support OCI indexes"))
return return
} }
// 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 imh.Tag != "" && manifestType == manifestSchema2 && !supports[manifestSchema2] {
// Rewrite manifest in schema1 format
dcontext.GetLogger(imh).Infof("rewriting manifest %s in schema1 format to support old client", imh.Digest.String())
manifest, err = imh.convertSchema2Manifest(schema2Manifest) if imh.Tag != "" && manifestType == manifestlistSchema && !supports[manifestlistSchema] {
if err != nil {
return
}
} else if imh.Tag != "" && manifestType == manifestlistSchema && !supports[manifestlistSchema] {
// Rewrite manifest in schema1 format // Rewrite manifest in schema1 format
dcontext.GetLogger(imh).Infof("rewriting manifest list %s in schema1 format to support old client", imh.Digest.String()) dcontext.GetLogger(imh).Infof("rewriting manifest list %s in schema1 format to support old client", imh.Digest.String())
@ -214,12 +197,9 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
return return
} }
// If necessary, convert the image manifest if _, isSchema2 := manifest.(*schema2.DeserializedManifest); isSchema2 && !supports[manifestSchema2] {
if schema2Manifest, isSchema2 := manifest.(*schema2.DeserializedManifest); isSchema2 && !supports[manifestSchema2] { imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithMessage("Schema 2 manifest not supported by client"))
manifest, err = imh.convertSchema2Manifest(schema2Manifest) return
if err != nil {
return
}
} else { } else {
imh.Digest = manifestDigest imh.Digest = manifestDigest
} }
@ -237,46 +217,6 @@ func (imh *manifestHandler) GetManifest(w http.ResponseWriter, r *http.Request)
w.Write(p) w.Write(p)
} }
func (imh *manifestHandler) convertSchema2Manifest(schema2Manifest *schema2.DeserializedManifest) (distribution.Manifest, error) {
targetDescriptor := schema2Manifest.Target()
blobs := imh.Repository.Blobs(imh)
configJSON, err := blobs.Get(imh, targetDescriptor.Digest)
if err != nil {
if err == distribution.ErrBlobUnknown {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
} else {
imh.Errors = append(imh.Errors, errcode.ErrorCodeUnknown.WithDetail(err))
}
return nil, err
}
ref := imh.Repository.Named()
if imh.Tag != "" {
ref, err = reference.WithTag(ref, imh.Tag)
if err != nil {
imh.Errors = append(imh.Errors, v2.ErrorCodeTagInvalid.WithDetail(err))
return nil, err
}
}
builder := schema1.NewConfigManifestBuilder(imh.Repository.Blobs(imh), imh.Context.App.trustKey, ref, configJSON) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
for _, d := range schema2Manifest.Layers {
if err := builder.AppendReference(d); err != nil {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
return nil, err
}
}
manifest, err := builder.Build(imh)
if err != nil {
imh.Errors = append(imh.Errors, v2.ErrorCodeManifestInvalid.WithDetail(err))
return nil, err
}
imh.Digest = digest.FromBytes(manifest.(*schema1.SignedManifest).Canonical) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
return manifest, nil
}
func etagMatch(r *http.Request, etag string) bool { func etagMatch(r *http.Request, etag string) bool {
for _, headerVal := range r.Header["If-None-Match"] { for _, headerVal := range r.Header["If-None-Match"] {
if headerVal == etag || headerVal == fmt.Sprintf(`"%s"`, etag) { // allow quoted or unquoted if headerVal == etag || headerVal == fmt.Sprintf(`"%s"`, etag) { // allow quoted or unquoted
@ -421,8 +361,6 @@ func (imh *manifestHandler) applyResourcePolicy(manifest distribution.Manifest)
var class string var class string
switch m := manifest.(type) { switch m := manifest.(type) {
case *schema1.SignedManifest: //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
class = imageClass
case *schema2.DeserializedManifest: case *schema2.DeserializedManifest:
switch m.Config.MediaType { switch m.Config.MediaType {
case schema2.MediaTypeImageConfig: case schema2.MediaTypeImageConfig: