Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove schema1 references from registry client #4007

Merged
merged 2 commits into from
Aug 21, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
175 changes: 74 additions & 101 deletions registry/client/repository_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ import (
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/context"
"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/registry/api/errcode"
v2 "github.com/distribution/distribution/v3/registry/api/v2"
"github.com/distribution/distribution/v3/testutil"
"github.com/distribution/distribution/v3/uuid"
"github.com/docker/libtrust"
"github.com/opencontainers/go-digest"
v1 "github.com/opencontainers/image-spec/specs-go/v1"
)

func testServer(rrm testutil.RequestResponseMap) (string, func()) {
Expand Down Expand Up @@ -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.
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.
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.

func newRandomOCIManifest(t *testing.T, blobCount int) (*ocischema.Manifest, digest.Digest, []byte) {
layers := make([]distribution.Descriptor, blobCount)
for i := 0; i < blobCount; i++ {
dgst, blob := newRandomBlob((i % 5) * 16)

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.
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.
layers[i] = distribution.Descriptor{
MediaType: v1.MediaTypeImageLayer,
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.
Name: name.String(),
Tag: tag,
Architecture: "x86",
FSLayers: blobs,
History: history,
m := ocischema.Manifest{
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 {
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.
if err != nil {
panic(err)
}
_, payload, _ := sm.Payload()

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) {
Expand All @@ -970,7 +969,7 @@ func addTestManifestWithEtag(repo reference.Named, reference string, content []b
Headers: http.Header(map[string][]string{
"Content-Length": {"0"},
"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 {
Expand All @@ -980,21 +979,13 @@ func addTestManifestWithEtag(repo reference.Named, reference string, content []b
Headers: http.Header(map[string][]string{
"Content-Length": {fmt.Sprint(len(content))},
"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})
}

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) {
*m = append(*m, testutil.RequestResponseMapping{
Request: testutil.Request{
Expand All @@ -1008,7 +999,7 @@ func addTestManifest(repo reference.Named, reference string, mediatype string, c
"Content-Length": {fmt.Sprint(len(content))},
"Last-Modified": {time.Now().Add(-1 * time.Second).Format(time.ANSIC)},
"Content-Type": {mediatype},
"Docker-Content-Digest": {contentDigestString(mediatype, content)},
"Docker-Content-Digest": {digest.FromBytes(content).String()},
}),
},
})
Expand Down Expand Up @@ -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.
if m1.Name != m2.Name {
return fmt.Errorf("name does not match %q != %q", m1.Name, m2.Name)
}
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))
func checkEqualManifest(m1, m2 *ocischema.DeserializedManifest) error {
if !reflect.DeepEqual(m1.Versioned, m2.Versioned) {
return fmt.Errorf("versions do not match: %v != %v", m1.Versioned, m2.Versioned)
}
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))
if !reflect.DeepEqual(m1.Config, m2.Config) {
return fmt.Errorf("config do not match: %v != %v", m1.Config, m2.Config)
}
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
}

func TestV1ManifestFetch(t *testing.T) {
func TestOCIManifestFetch(t *testing.T) {
ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
m1, dgst, pl := newRandomOCIManifest(t, 6)
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.
if err != nil {
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, dgst.String(), v1.MediaTypeImageManifest, pl, &m)
addTestManifest(repo, "latest", v1.MediaTypeImageManifest, pl, &m)
addTestManifest(repo, "badcontenttype", "text/html", pl, &m)

e, c := testServer(m)
Expand All @@ -1124,12 +1099,17 @@ func TestV1ManifestFetch(t *testing.T) {
if err != nil {
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 {
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)
}

Expand All @@ -1138,36 +1118,32 @@ func TestV1ManifestFetch(t *testing.T) {
if err != nil {
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 {
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)
}

if contentDigest != dgst {
t.Fatalf("Unexpected returned content digest %v, expected %v", contentDigest, dgst)
}

manifest, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype"))
if err != nil {
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.
if !ok {
t.Fatalf("Unexpected manifest type from Get: %T", manifest)
}

if err = checkEqualManifest(v1manifest, m1); err != nil {
t.Fatal(err)
// TODO(milosgajdos): once the schema1 manifest package is removed we need to
// return some predefined error from distribution.UnmarshalManifest() for the cases
// 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.
_, err = ms.Get(ctx, dgst, distribution.WithTag("badcontenttype"))
if err == nil {
t.Fatal("expected to fail")
}
}

func TestManifestFetchWithEtag(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/by/tag")
_, d1, p1 := newRandomSchemaV1Manifest(repo, "latest", 6)
_, d1, p1 := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap
addTestManifestWithEtag(repo, "latest", p1, &m, d1.String())

Expand Down Expand Up @@ -1198,7 +1174,7 @@ func TestManifestFetchWithEtag(t *testing.T) {
func TestManifestFetchWithAccept(t *testing.T) {
ctx := context.Background()
repo, _ := reference.WithName("test.example.com/repo")
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
_, dgst, _ := newRandomOCIManifest(t, 6)
headers := make(chan []string, 1)
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
headers <- req.Header["Accept"]
Expand Down Expand Up @@ -1242,6 +1218,10 @@ func TestManifestFetchWithAccept(t *testing.T) {
},
}
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))
actual := <-headers
if testCase.sort {
Expand All @@ -1256,8 +1236,8 @@ func TestManifestFetchWithAccept(t *testing.T) {

func TestManifestDelete(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/delete")
_, dgst1, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
_, dgst2, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
_, dgst1, _ := newRandomOCIManifest(t, 6)
_, dgst2, _ := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{
Expand Down Expand Up @@ -1291,23 +1271,17 @@ func TestManifestDelete(t *testing.T) {
if err := ms.Delete(ctx, dgst2); err == nil {
t.Fatal("Expected error deleting unknown manifest")
}
// TODO(dmcgowan): Check for specific unknown error
}

func TestManifestPut(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo/delete")
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "other", 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)
}
m1, dgst, payload := newRandomOCIManifest(t, 6)

var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{
Method: http.MethodPut,
Route: "/v2/" + repo.Name() + "/manifests/other",
Route: "/v2/" + repo.Name() + "/manifests/sometag",
Body: payload,
},
Response: testutil.Response{
Expand All @@ -1319,7 +1293,7 @@ func TestManifestPut(t *testing.T) {
},
})

putDgst := digest.FromBytes(m1.Canonical)
putDgst := digest.FromBytes(payload)
m = append(m, testutil.RequestResponseMapping{
Request: testutil.Request{
Method: http.MethodPut,
Expand Down Expand Up @@ -1348,15 +1322,18 @@ func TestManifestPut(t *testing.T) {
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)
}

if _, err := ms.Put(ctx, m1); err != nil {
if _, err := ms.Put(ctx, dm, distribution.WithTag("sometag")); err != nil {
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) {
Expand Down Expand Up @@ -1424,7 +1401,7 @@ func TestManifestTags(t *testing.T) {
func TestTagDelete(t *testing.T) {
tag := "latest"
repo, _ := reference.WithName("test.example.com/repo/delete")
newRandomSchemaV1Manifest(repo, tag, 1)
newRandomOCIManifest(t, 1)

var m testutil.RequestResponseMap
m = append(m, testutil.RequestResponseMapping{
Expand Down Expand Up @@ -1505,12 +1482,8 @@ func TestObtainsManifestForTagWithoutHeaders(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo")

var m testutil.RequestResponseMap
m1, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 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.
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.
_, dgst, pl := newRandomOCIManifest(t, 6)
addTestManifestWithoutDigestHeader(repo, "1.0.0", v1.MediaTypeImageManifest, pl, &m)

e, c := testServer(m)
defer c()
Expand Down Expand Up @@ -1620,7 +1593,7 @@ func TestManifestTagsPaginated(t *testing.T) {

func TestManifestUnauthorized(t *testing.T) {
repo, _ := reference.WithName("test.example.com/repo")
_, dgst, _ := newRandomSchemaV1Manifest(repo, "latest", 6)
_, dgst, _ := newRandomOCIManifest(t, 6)
var m testutil.RequestResponseMap

m = append(m, testutil.RequestResponseMapping{
Expand Down