Merge pull request #4007 from milosgajdos/remove-schema1-client

This commit is contained in:
Milos Gajdos 2023-08-21 13:46:29 +01:00 committed by GitHub
commit 1a4638a06c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -19,14 +19,14 @@ import (
"github.com/distribution/distribution/v3" "github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/context" "github.com/distribution/distribution/v3/context"
"github.com/distribution/distribution/v3/manifest" "github.com/distribution/distribution/v3/manifest"
"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/ocischema"
"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"
v2 "github.com/distribution/distribution/v3/registry/api/v2" v2 "github.com/distribution/distribution/v3/registry/api/v2"
"github.com/distribution/distribution/v3/testutil" "github.com/distribution/distribution/v3/testutil"
"github.com/distribution/distribution/v3/uuid" "github.com/distribution/distribution/v3/uuid"
"github.com/docker/libtrust"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
) )
func testServer(rrm testutil.RequestResponseMap) (string, func()) { func testServer(rrm testutil.RequestResponseMap) (string, func()) {
@ -917,39 +917,38 @@ func TestBlobMount(t *testing.T) {
} }
} }
func newRandomSchemaV1Manifest(name reference.Named, tag string, blobCount int) (*schema1.SignedManifest, digest.Digest, []byte) { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. func newRandomOCIManifest(t *testing.T, blobCount int) (*ocischema.Manifest, digest.Digest, []byte) {
blobs := make([]schema1.FSLayer, blobCount) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. layers := make([]distribution.Descriptor, blobCount)
history := make([]schema1.History, blobCount) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
for i := 0; i < blobCount; i++ { for i := 0; i < blobCount; i++ {
dgst, blob := newRandomBlob((i % 5) * 16) dgst, blob := newRandomBlob((i % 5) * 16)
layers[i] = distribution.Descriptor{
blobs[i] = schema1.FSLayer{BlobSum: dgst} //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. MediaType: v1.MediaTypeImageLayer,
history[i] = schema1.History{V1Compatibility: fmt.Sprintf("{\"Hex\": \"%x\"}", blob)} //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. Digest: dgst,
Size: int64(len(blob)),
}
} }
m := schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. m := ocischema.Manifest{
Name: name.String(),
Tag: tag,
Architecture: "x86",
FSLayers: blobs,
History: history,
Versioned: manifest.Versioned{ Versioned: manifest.Versioned{
SchemaVersion: 1, SchemaVersion: 2,
MediaType: v1.MediaTypeImageManifest,
}, },
Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 123,
MediaType: v1.MediaTypeImageConfig,
},
Layers: layers,
} }
pk, err := libtrust.GenerateECP256PrivateKey() sm, err := ocischema.FromStruct(m)
if err != nil { if err != nil {
panic(err) t.Fatal(err)
} }
sm, err := schema1.Sign(&m, pk) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. _, payload, _ := sm.Payload()
if err != nil {
panic(err)
}
return sm, digest.FromBytes(sm.Canonical), sm.Canonical return &m, digest.FromBytes(payload), payload
} }
func addTestManifestWithEtag(repo reference.Named, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) { func addTestManifestWithEtag(repo reference.Named, reference string, content []byte, m *testutil.RequestResponseMap, dgst string) {
@ -970,7 +969,7 @@ func addTestManifestWithEtag(repo reference.Named, reference string, content []b
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {"0"}, "Content-Length": {"0"},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, "Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
"Content-Type": {schema1.MediaTypeSignedManifest}, //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. "Content-Type": {v1.MediaTypeImageManifest},
}), }),
} }
} else { } else {
@ -980,21 +979,13 @@ func addTestManifestWithEtag(repo reference.Named, reference string, content []b
Headers: http.Header(map[string][]string{ Headers: http.Header(map[string][]string{
"Content-Length": {fmt.Sprint(len(content))}, "Content-Length": {fmt.Sprint(len(content))},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, "Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
"Content-Type": {schema1.MediaTypeSignedManifest}, //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. "Content-Type": {v1.MediaTypeImageManifest},
}), }),
} }
} }
*m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag}) *m = append(*m, testutil.RequestResponseMapping{Request: getReqWithEtag, Response: getRespWithEtag})
} }
func contentDigestString(mediatype string, content []byte) string {
if mediatype == schema1.MediaTypeSignedManifest { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
m, _, _ := distribution.UnmarshalManifest(mediatype, content)
content = m.(*schema1.SignedManifest).Canonical //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
}
return digest.Canonical.FromBytes(content).String()
}
func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) { func addTestManifest(repo reference.Named, reference string, mediatype string, content []byte, m *testutil.RequestResponseMap) {
*m = append(*m, testutil.RequestResponseMapping{ *m = append(*m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
@ -1008,7 +999,7 @@ func addTestManifest(repo reference.Named, reference string, mediatype string, c
"Content-Length": {fmt.Sprint(len(content))}, "Content-Length": {fmt.Sprint(len(content))},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)}, "Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
"Content-Type": {mediatype}, "Content-Type": {mediatype},
"Docker-Content-Digest": {contentDigestString(mediatype, content)}, "Docker-Content-Digest": {digest.FromBytes(content).String()},
}), }),
}, },
}) })
@ -1061,43 +1052,27 @@ func addTestManifestWithoutDigestHeader(repo reference.Named, reference string,
}) })
} }
func checkEqualManifest(m1, m2 *schema1.SignedManifest) error { //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. func checkEqualManifest(m1, m2 *ocischema.DeserializedManifest) error {
if m1.Name != m2.Name { if !reflect.DeepEqual(m1.Versioned, m2.Versioned) {
return fmt.Errorf("name does not match %q != %q", m1.Name, m2.Name) return fmt.Errorf("versions do not match: %v != %v", m1.Versioned, m2.Versioned)
} }
if m1.Tag != m2.Tag { if !reflect.DeepEqual(m1.Config, m2.Config) {
return fmt.Errorf("tag does not match %q != %q", m1.Tag, m2.Tag) return fmt.Errorf("config do not match: %v != %v", m1.Config, m2.Config)
}
if len(m1.FSLayers) != len(m2.FSLayers) {
return fmt.Errorf("fs blob length does not match %d != %d", len(m1.FSLayers), len(m2.FSLayers))
}
for i := range m1.FSLayers {
if m1.FSLayers[i].BlobSum != m2.FSLayers[i].BlobSum {
return fmt.Errorf("blobsum does not match %q != %q", m1.FSLayers[i].BlobSum, m2.FSLayers[i].BlobSum)
}
}
if len(m1.History) != len(m2.History) {
return fmt.Errorf("history length does not match %d != %d", len(m1.History), len(m2.History))
}
for i := range m1.History {
if m1.History[i].V1Compatibility != m2.History[i].V1Compatibility {
return fmt.Errorf("blobsum does not match %q != %q", m1.History[i].V1Compatibility, m2.History[i].V1Compatibility)
} }
if !reflect.DeepEqual(m1.Layers, m2.Layers) {
return fmt.Errorf("layers do not match: %v != %v", m1.Layers, m2.Layers)
} }
return nil return nil
} }
func TestV1ManifestFetch(t *testing.T) { func TestOCIManifestFetch(t *testing.T) {
ctx := context.Background() ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo") repo, _ := reference.WithName("test.example.com/repo")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) m1, dgst, pl := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
_, pl, err := m1.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. addTestManifest(repo, dgst.String(), v1.MediaTypeImageManifest, pl, &m)
if err != nil { addTestManifest(repo, "latest", v1.MediaTypeImageManifest, pl, &m)
t.Fatal(err)
}
addTestManifest(repo, dgst.String(), schema1.MediaTypeSignedManifest, pl, &m) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
addTestManifest(repo, "latest", schema1.MediaTypeSignedManifest, pl, &m) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
addTestManifest(repo, "badcontenttype", "text/html", pl, &m) addTestManifest(repo, "badcontenttype", "text/html", pl, &m)
e, c := testServer(m) e, c := testServer(m)
@ -1124,12 +1099,17 @@ func TestV1ManifestFetch(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
v1manifest, ok := manifest.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. ociManifest, ok := manifest.(*ocischema.DeserializedManifest)
if !ok { if !ok {
t.Fatalf("Unexpected manifest type from Get: %T", manifest) t.Fatalf("Unexpected manifest type from Get: %T", manifest)
} }
if err := checkEqualManifest(v1manifest, m1); err != nil { dm1, err := ocischema.FromStruct(*m1)
if err != nil {
t.Fatal(err)
}
if err := checkEqualManifest(ociManifest, dm1); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1138,12 +1118,12 @@ func TestV1ManifestFetch(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
v1manifest, ok = manifest.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. ociManifest, ok = manifest.(*ocischema.DeserializedManifest)
if !ok { if !ok {
t.Fatalf("Unexpected manifest type from Get: %T", manifest) t.Fatalf("Unexpected manifest type from Get: %T", manifest)
} }
if err = checkEqualManifest(v1manifest, m1); err != nil { if err = checkEqualManifest(ociManifest, dm1); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -1151,23 +1131,19 @@ func TestV1ManifestFetch(t *testing.T) {
t.Fatalf("Unexpected returned content digest %v, expected %v", contentDigest, dgst) t.Fatalf("Unexpected returned content digest %v, expected %v", contentDigest, dgst)
} }
manifest, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype")) // TODO(milosgajdos): once the schema1 manifest package is removed we need to
if err != nil { // return some predefined error from distribution.UnmarshalManifest() for the cases
t.Fatal(err) // where empty mediaType/ctHeader is provided; currently this is handled by schema1 unmarshaler.
} // Ideally we'd like to returns something like UnsupportedManifest error and assert it in this test.
v1manifest, ok = manifest.(*schema1.SignedManifest) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. _, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype"))
if !ok { if err == nil {
t.Fatalf("Unexpected manifest type from Get: %T", manifest) t.Fatal("expected to fail")
}
if err = checkEqualManifest(v1manifest, m1); err != nil {
t.Fatal(err)
} }
} }
func TestManifestFetchWithEtag(t *testing.T) { func TestManifestFetchWithEtag(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/by/tag") repo, _ := reference.WithName("test.example.com/repo/by/tag")
_, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6) _, d1, p1 := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
addTestManifestWithEtag(repo, "latest", p1, &m, d1.String()) addTestManifestWithEtag(repo, "latest", p1, &m, d1.String())
@ -1198,7 +1174,7 @@ func TestManifestFetchWithEtag(t *testing.T) {
func TestManifestFetchWithAccept(t *testing.T) { func TestManifestFetchWithAccept(t *testing.T) {
ctx := context.Background() ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo") repo, _ := reference.WithName("test.example.com/repo")
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst, _ := newRandomOCIManifest(t, 6)
headers := make(chan []string, 1) headers := make(chan []string, 1)
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
headers <- req.Header["Accept"] headers <- req.Header["Accept"]
@ -1242,6 +1218,10 @@ func TestManifestFetchWithAccept(t *testing.T) {
}, },
} }
for _, testCase := range testCases { for _, testCase := range testCases {
// NOTE(milosgajdos): we are not checking error values here because this test
// is not storing any manifests, so this will inevitably error out.
// This test is about checking if the Accept headers are returned as expected.
// nolint:errcheck
ms.Get(ctx, dgst, distribution.WithManifestMediaTypes(testCase.mediaTypes)) ms.Get(ctx, dgst, distribution.WithManifestMediaTypes(testCase.mediaTypes))
actual := <-headers actual := <-headers
if testCase.sort { if testCase.sort {
@ -1256,8 +1236,8 @@ func TestManifestFetchWithAccept(t *testing.T) {
func TestManifestDelete(t *testing.T) { func TestManifestDelete(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/delete") repo, _ := reference.WithName("test.example.com/repo/delete")
_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst1, _ := newRandomOCIManifest(t, 6)
_, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst2, _ := newRandomOCIManifest(t, 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{
@ -1291,23 +1271,17 @@ func TestManifestDelete(t *testing.T) {
if err := ms.Delete(ctx, dgst2); err == nil { if err := ms.Delete(ctx, dgst2); err == nil {
t.Fatal("Expected error deleting unknown manifest") t.Fatal("Expected error deleting unknown manifest")
} }
// TODO(dmcgowan): Check for specific unknown error
} }
func TestManifestPut(t *testing.T) { func TestManifestPut(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/delete") repo, _ := reference.WithName("test.example.com/repo/delete")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 6) m1, dgst, payload := newRandomOCIManifest(t, 6)
_, payload, err := m1.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.Fatal(err)
}
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: http.MethodPut, Method: http.MethodPut,
Route: "/v2/" + repo.Name() + "/manifests/other", Route: "/v2/" + repo.Name() + "/manifests/sometag",
Body: payload, Body: payload,
}, },
Response: testutil.Response{ Response: testutil.Response{
@ -1319,7 +1293,7 @@ func TestManifestPut(t *testing.T) {
}, },
}) })
putDgst := digest.FromBytes(m1.Canonical) putDgst := digest.FromBytes(payload)
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{ Request: testutil.Request{
Method: http.MethodPut, Method: http.MethodPut,
@ -1348,15 +1322,18 @@ func TestManifestPut(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if _, err := ms.Put(ctx, m1, distribution.WithTag(m1.Tag)); err != nil { dm, err := ocischema.FromStruct(*m1)
if err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := ms.Put(ctx, m1); err != nil { if _, err := ms.Put(ctx, dm, distribution.WithTag("sometag")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
// TODO(dmcgowan): Check for invalid input error if _, err := ms.Put(ctx, dm); err != nil {
t.Fatal(err)
}
} }
func TestManifestTags(t *testing.T) { func TestManifestTags(t *testing.T) {
@ -1424,7 +1401,7 @@ func TestManifestTags(t *testing.T) {
func TestTagDelete(t *testing.T) { func TestTagDelete(t *testing.T) {
tag := "latest" tag := "latest"
repo, _ := reference.WithName("test.example.com/repo/delete") repo, _ := reference.WithName("test.example.com/repo/delete")
newRandomSchemaV1Manifest(repo, tag, 1) newRandomOCIManifest(t, 1)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{
@ -1505,12 +1482,8 @@ func TestObtainsManifestForTagWithoutHeaders(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo") repo, _ := reference.WithName("test.example.com/repo")
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst, pl := newRandomOCIManifest(t, 6)
_, pl, err := m1.Payload() //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. addTestManifestWithoutDigestHeader(repo, "1.0.0", v1.MediaTypeImageManifest, pl, &m)
if err != nil {
t.Fatal(err)
}
addTestManifestWithoutDigestHeader(repo, "1.0.0", schema1.MediaTypeSignedManifest, pl, &m) //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility.
e, c := testServer(m) e, c := testServer(m)
defer c() defer c()
@ -1620,7 +1593,7 @@ func TestManifestTagsPaginated(t *testing.T) {
func TestManifestUnauthorized(t *testing.T) { func TestManifestUnauthorized(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo") repo, _ := reference.WithName("test.example.com/repo")
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6) _, dgst, _ := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{ m = append(m, testutil.RequestResponseMapping{