diff --git a/notifications/bridge_test.go b/notifications/bridge_test.go index 65b09a07..e147fccb 100644 --- a/notifications/bridge_test.go +++ b/notifications/bridge_test.go @@ -4,13 +4,14 @@ import ( "testing" "github.com/distribution/distribution/v3" - "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" + "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/reference" v2 "github.com/distribution/distribution/v3/registry/api/v2" "github.com/distribution/distribution/v3/uuid" events "github.com/docker/go-events" - "github.com/docker/libtrust" "github.com/opencontainers/go-digest" + v1 "github.com/opencontainers/image-spec/specs-go/v1" ) var ( @@ -26,22 +27,17 @@ var ( actor = ActorRecord{ Name: "test", } - request = RequestRecord{} - layers = []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", - }, - } - m = schema1.Manifest{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. - Name: repo, - Tag: "latest", - FSLayers: layers, + request = RequestRecord{} + tag = "latest" + ociMediaType = v1.MediaTypeImageManifest + artifactType = "application/vnd.example.sbom.v1" + cfg = distribution.Descriptor{ + MediaType: artifactType, + Size: 100, + Digest: "cfgdgst", } - sm *schema1.SignedManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. + sm *schema2.DeserializedManifest payload []byte dgst digest.Digest ) @@ -83,7 +79,7 @@ func TestEventBridgeManifestPushedWithTag(t *testing.T) { })) repoRef, _ := reference.WithName(repo) - if err := l.ManifestPushed(repoRef, sm, distribution.WithTag(m.Tag)); err != nil { + if err := l.ManifestPushed(repoRef, sm, distribution.WithTag(tag)); err != nil { t.Fatalf("unexpected error notifying manifest pull: %v", err) } } @@ -99,7 +95,7 @@ func TestEventBridgeManifestPulledWithTag(t *testing.T) { })) repoRef, _ := reference.WithName(repo) - if err := l.ManifestPulled(repoRef, sm, distribution.WithTag(m.Tag)); err != nil { + if err := l.ManifestPulled(repoRef, sm, distribution.WithTag(tag)); err != nil { t.Fatalf("unexpected error notifying manifest pull: %v", err) } } @@ -122,14 +118,14 @@ func TestEventBridgeManifestDeleted(t *testing.T) { func TestEventBridgeTagDeleted(t *testing.T) { l := createTestEnv(t, testSinkFn(func(event events.Event) error { checkDeleted(t, EventActionDelete, event) - if event.(Event).Target.Tag != m.Tag { - t.Fatalf("unexpected tag on event target: %q != %q", event.(Event).Target.Tag, m.Tag) + if event.(Event).Target.Tag != tag { + t.Fatalf("unexpected tag on event target: %q != %q", event.(Event).Target.Tag, tag) } return nil })) repoRef, _ := reference.WithName(repo) - if err := l.TagDeleted(repoRef, m.Tag); err != nil { + if err := l.TagDeleted(repoRef, tag); err != nil { t.Fatalf("unexpected error notifying tag deletion: %v", err) } } @@ -147,18 +143,21 @@ func TestEventBridgeRepoDeleted(t *testing.T) { } func createTestEnv(t *testing.T, fn testSinkFn) Listener { - pk, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - t.Fatalf("error generating private key: %v", err) + manifest := schema2.Manifest{ + Versioned: manifest.Versioned{ + MediaType: ociMediaType, + }, + Config: cfg, } - 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. + deserializedManifest, err := schema2.FromStruct(manifest) if err != nil { - t.Fatalf("error signing manifest: %v", err) + t.Fatalf("creating OCI manifest: %v", err) } - payload = sm.Canonical + _, payload, _ = deserializedManifest.Payload() dgst = digest.FromBytes(payload) + sm = deserializedManifest return NewBridge(ub, source, actor, request, fn, true) } @@ -198,15 +197,6 @@ func checkCommonManifest(t *testing.T, action string, event events.Event) { if event.(Event).Target.URL != u { t.Fatalf("incorrect url passed: \n%q != \n%q", event.(Event).Target.URL, u) } - - if len(event.(Event).Target.References) != len(layers) { - t.Fatalf("unexpected number of references %v != %v", len(event.(Event).Target.References), len(layers)) - } - for i, targetReference := range event.(Event).Target.References { - if targetReference.Digest != layers[i].BlobSum { - t.Fatalf("unexpected reference: %q != %q", targetReference.Digest, layers[i].BlobSum) - } - } } func checkCommon(t *testing.T, event events.Event) { diff --git a/notifications/event_test.go b/notifications/event_test.go index 59a86fd5..c82d0643 100644 --- a/notifications/event_test.go +++ b/notifications/event_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "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" ) // TestEventJSONFormat provides silly test to detect if the event format or @@ -21,7 +21,7 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { "timestamp": "2006-01-02T15:04:05Z", "action": "push", "target": { - "mediaType": "application/vnd.docker.distribution.manifest.v1+prettyjws", + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "digest": "sha256:0123456789abcdef0", "size": 1, "length": 1, @@ -116,7 +116,7 @@ func TestEventEnvelopeJSONFormat(t *testing.T) { manifestPush := prototype manifestPush.ID = "asdf-asdf-asdf-asdf-0" - manifestPush.Target.MediaType = schema1.MediaTypeSignedManifest //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. + manifestPush.Target.MediaType = schema2.MediaTypeManifest manifestPush.Target.Digest = "sha256:0123456789abcdef0" manifestPush.Target.Size = 1 manifestPush.Target.Length = 1 diff --git a/notifications/http_test.go b/notifications/http_test.go index fdcb6b5c..8cc07efa 100644 --- a/notifications/http_test.go +++ b/notifications/http_test.go @@ -13,7 +13,7 @@ import ( "strings" "testing" - "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" events "github.com/docker/go-events" ) @@ -121,11 +121,11 @@ func TestHTTPSink(t *testing.T) { }{ { statusCode: http.StatusOK, - event: createTestEvent("push", "library/test", schema1.MediaTypeSignedManifest), //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. + event: createTestEvent("push", "library/test", schema2.MediaTypeManifest), }, { statusCode: http.StatusOK, - event: createTestEvent("push", "library/test", schema1.MediaTypeSignedManifest), //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. + event: createTestEvent("push", "library/test", schema2.MediaTypeManifest), }, { statusCode: http.StatusOK, diff --git a/notifications/listener_test.go b/notifications/listener_test.go index 9e588318..8d0c18a4 100644 --- a/notifications/listener_test.go +++ b/notifications/listener_test.go @@ -1,31 +1,27 @@ package notifications import ( - "io" + "bytes" "reflect" "testing" "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. + dcontext "github.com/distribution/distribution/v3/context" + "github.com/distribution/distribution/v3/manifest/schema2" "github.com/distribution/distribution/v3/reference" "github.com/distribution/distribution/v3/registry/storage" "github.com/distribution/distribution/v3/registry/storage/cache/memory" "github.com/distribution/distribution/v3/registry/storage/driver/inmemory" "github.com/distribution/distribution/v3/testutil" - "github.com/docker/libtrust" "github.com/opencontainers/go-digest" ) func TestListener(t *testing.T) { - ctx := context.Background() - k, err := libtrust.GenerateECP256PrivateKey() - if err != nil { - t.Fatal(err) - } + ctx := dcontext.Background() - registry, err := storage.NewRegistry(ctx, inmemory.New(), storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), storage.EnableDelete, storage.EnableRedirect, storage.Schema1SigningKey(k), storage.EnableSchema1) + registry, err := storage.NewRegistry(ctx, inmemory.New(), + storage.BlobDescriptorCacheProvider(memory.NewInMemoryBlobDescriptorCacheProvider(memory.UnlimitedSize)), + storage.EnableDelete, storage.EnableRedirect) if err != nil { t.Fatalf("error creating registry: %v", err) } @@ -46,15 +42,15 @@ func TestListener(t *testing.T) { repository, remover = Listen(repository, remover, tl) // Now take the registry through a number of operations - checkExerciseRepository(t, repository, remover) + checkTestRepository(t, repository, remover) expectedOps := map[string]int{ "manifest:push": 1, "manifest:pull": 1, "manifest:delete": 1, - "layer:push": 2, - "layer:pull": 2, - "layer:delete": 2, + "layer:push": 3, + "layer:pull": 3, + "layer:delete": 3, "tag:delete": 1, "repo:delete": 1, } @@ -113,28 +109,47 @@ func (tl *testListener) RepoDeleted(repo reference.Named) error { return nil } -// checkExerciseRegistry takes the registry through all of its operations, +// checkTestRepository takes the registry through all of its operations, // carrying out generic checks. -func checkExerciseRepository(t *testing.T, repository distribution.Repository, remover distribution.RepositoryRemover) { +func checkTestRepository(t *testing.T, repository distribution.Repository, remover distribution.RepositoryRemover) { // TODO(stevvooe): This would be a nice testutil function. Basically, it // takes the registry through a common set of operations. This could be // used to make cross-cutting updates by changing internals that affect // update counts. Basically, it would make writing tests a lot easier. + // TODO: change this to use Builder - ctx := context.Background() + ctx := dcontext.Background() tag := "thetag" - // todo: change this to use Builder - m := 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: repository.Named().Name(), - Tag: tag, - } + config := []byte(`{"name": "foo"}`) + configDgst := digest.FromBytes(config) + configReader := bytes.NewReader(config) var blobDigests []digest.Digest + blobDigests = append(blobDigests, configDgst) + blobs := repository.Blobs(ctx) + + // push config blob + if err := testutil.PushBlob(ctx, repository, configReader, configDgst); err != nil { + t.Fatal(err) + } + + // Then fetch the config blob + if rc, err := blobs.Open(ctx, configDgst); err != nil { + t.Fatalf("error fetching config: %v", err) + } else { + defer rc.Close() + } + + m := schema2.Manifest{ + Versioned: schema2.SchemaVersion, + Config: distribution.Descriptor{ + MediaType: "foo/bar", + Digest: configDgst, + }, + } + for i := 0; i < 2; i++ { rs, dgst, err := testutil.CreateRandomTarFile() if err != nil { @@ -142,28 +157,13 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository, r } blobDigests = append(blobDigests, dgst) - wr, err := blobs.Create(ctx) - if err != nil { - t.Fatalf("error creating layer upload: %v", err) + if err := testutil.PushBlob(ctx, repository, rs, dgst); err != nil { + t.Fatal(err) } - // Use the resumes, as well! - wr, err = blobs.Resume(ctx, wr.ID()) - if err != nil { - t.Fatalf("error resuming layer upload: %v", err) - } - - io.Copy(wr, rs) - - if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil { - t.Fatalf("unexpected error finishing upload: %v", err) - } - - m.FSLayers = append(m.FSLayers, schema1.FSLayer{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. - BlobSum: dgst, - }) - m.History = append(m.History, schema1.History{ //nolint:staticcheck // Ignore SA1019: "github.com/distribution/distribution/v3/manifest/schema1" is deprecated, as it's used for backward compatibility. - V1Compatibility: "", + m.Layers = append(m.Layers, distribution.Descriptor{ + MediaType: "application/octet-stream", + Digest: dgst, }) // Then fetch the blobs @@ -174,14 +174,9 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository, r } } - pk, err := libtrust.GenerateECP256PrivateKey() + sm, err := schema2.FromStruct(m) if err != nil { - t.Fatalf("unexpected error generating key: %v", 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 { - t.Fatalf("unexpected error signing manifest: %v", err) + t.Fatal(err.Error()) } manifests, err := repository.Manifests(ctx) @@ -194,7 +189,12 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository, r t.Fatalf("unexpected error putting the manifest: %v", err) } - dgst := digest.FromBytes(sm.Canonical) + _, canonical, err := sm.Payload() + if err != nil { + t.Fatal(err.Error()) + } + + dgst := digest.FromBytes(canonical) if dgst != digestPut { t.Fatalf("mismatching digest from payload and put") } @@ -208,7 +208,7 @@ func checkExerciseRepository(t *testing.T, repository distribution.Repository, r t.Fatalf("unexpected error fetching manifest: %v", err) } - err = repository.Tags(ctx).Untag(ctx, m.Tag) + err = repository.Tags(ctx).Untag(ctx, tag) if err != nil { t.Fatalf("unexpected error deleting tag: %v", err) } diff --git a/testutil/push.go b/testutil/push.go new file mode 100644 index 00000000..17f2230e --- /dev/null +++ b/testutil/push.go @@ -0,0 +1,36 @@ +package testutil + +import ( + "context" + "fmt" + "io" + + "github.com/distribution/distribution/v3" + "github.com/opencontainers/go-digest" +) + +// PushBlob pushes a blob with the given digest to the given repository. +func PushBlob(ctx context.Context, repository distribution.Repository, blobReader io.ReadSeeker, dgst digest.Digest) error { + blobs := repository.Blobs(ctx) + + wr, err := blobs.Create(ctx) + if err != nil { + return fmt.Errorf("error creating layer upload: %v", err) + } + + // Use the resumes, as well! + wr, err = blobs.Resume(ctx, wr.ID()) + if err != nil { + return fmt.Errorf("error resuming layer upload: %v", err) + } + + if _, err := io.Copy(wr, blobReader); err != nil { + return fmt.Errorf("unexpected error uploading: %v", err) + } + + if _, err := wr.Commit(ctx, distribution.Descriptor{Digest: dgst}); err != nil { + return fmt.Errorf("unexpected error finishing upload: %v", err) + } + + return nil +}