Remove schema1 references from registry client

We've replaced all the schema1 references with OCI schema manifest.
Note, there are some TODO items that must be addressed at some point in
the future once the schema1 package is removed completely from the
codebase.

Signed-off-by: Milos Gajdos <milosthegajdos@gmail.com>
This commit is contained in:
Milos Gajdos 2023-08-20 09:07:59 +01:00
parent 597e8a6b34
commit 11c341a369
No known key found for this signature in database
GPG key ID: 01300E5E6D417439

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,22 @@ 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.Config, m2.Config) {
return fmt.Errorf("name does not match %q != %q", m1.Name, m2.Name) return fmt.Errorf("config do not match %v != %v", m1.Config, m2.Config)
}
if m1.Tag != m2.Tag {
return fmt.Errorf("tag does not match %q != %q", m1.Tag, m2.Tag)
}
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)
}
} }
// TODO(rest of the fields)
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 +1094,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 +1113,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 +1126,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 +1169,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 +1213,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 +1231,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 +1266,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 +1288,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 +1317,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 +1396,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 +1477,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 +1588,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{