diff --git a/cmd/notation/internal/display/handler.go b/cmd/notation/internal/display/handler.go index 678b0e617..c482421f0 100644 --- a/cmd/notation/internal/display/handler.go +++ b/cmd/notation/internal/display/handler.go @@ -65,3 +65,9 @@ func NewVerifyHandler(printer *output.Printer) metadata.VerifyHandler { func NewBlobVerifyHandler(printer *output.Printer) metadata.BlobVerifyHandler { return text.NewBlobVerifyHandler(printer) } + +// NewListHandler creates a new metadata ListHandler for rendering signature +// metadata information in a tree format. +func NewListHandler(printer *output.Printer) metadata.ListHandler { + return tree.NewListHandler(printer) +} diff --git a/cmd/notation/internal/display/metadata/interface.go b/cmd/notation/internal/display/metadata/interface.go index 9099ff2fd..62f6f1716 100644 --- a/cmd/notation/internal/display/metadata/interface.go +++ b/cmd/notation/internal/display/metadata/interface.go @@ -76,3 +76,16 @@ type BlobVerifyHandler interface { // outcomes must not be nil or empty. OnVerifySucceeded(outcomes []*notation.VerificationOutcome, blobPath string) } + +// ListHandler is a handler for rendering metadata information of a list of +// signatures. +type ListHandler interface { + Renderer + + // OnReferenceResolved sets the artifact reference and media type for the + // handler. + OnReferenceResolved(reference string) + + // OnSignatureListed adds the signature digest to be rendered. + OnSignatureListed(signatureManifest ocispec.Descriptor) error +} diff --git a/cmd/notation/internal/display/metadata/tree/list.go b/cmd/notation/internal/display/metadata/tree/list.go new file mode 100644 index 000000000..871d0ca48 --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/list.go @@ -0,0 +1,78 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tree + +import ( + notationregistry "github.com/notaryproject/notation-go/registry" + "github.com/notaryproject/notation/cmd/notation/internal/display/output" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// ListHandler is a handler for rendering a list of signature digests in +// streaming fashion. It implements the metadata.ListHandler interface. +type ListHandler struct { + printer *output.Printer + + // sprinter is a stream printer to print the signature digest nodes in + // a streaming fashion + sprinter *streamPrinter + + // headerNode contains the headers of the output + // + // example: + // localhost:5000/net-monitor@sha256:b94d27b9934d3e08a52e52d7da7dabfac4efe37a5380ee9088f7ace2efcde9 + // └── application/vnd.cncf.notary.signature + headerNode *node + + // headerPrinted is a flag to indicate if the header has been printed + headerPrinted bool +} + +// NewListHandler creates a new ListHandler. +func NewListHandler(printer *output.Printer) *ListHandler { + return &ListHandler{ + printer: printer, + sprinter: newStreamPrinter(subTreePrefixLast, printer), + } +} + +// OnReferenceResolved sets the artifact reference and media type for the +// handler. +func (h *ListHandler) OnReferenceResolved(reference string) { + h.headerNode = newNode(reference) + h.headerNode.Add(notationregistry.ArtifactTypeNotation) +} + +// OnSignatureListed adds the signature digest to be printed. +func (h *ListHandler) OnSignatureListed(signatureManifest ocispec.Descriptor) error { + // print the header + if !h.headerPrinted { + if err := h.headerNode.Print(h.printer); err != nil { + return err + } + h.headerPrinted = true + } + return h.sprinter.PrintNode(newNode(signatureManifest.Digest.String())) +} + +// Render completes the rendering of the list of signature digests. +func (h *ListHandler) Render() error { + if err := h.sprinter.Flush(); err != nil { + return err + } + if !h.headerPrinted { + return h.printer.Printf("%s has no associated signatures\n", h.headerNode.Value) + } + return nil +} diff --git a/cmd/notation/internal/display/metadata/tree/printer.go b/cmd/notation/internal/display/metadata/tree/printer.go new file mode 100644 index 000000000..d4c8be45a --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/printer.go @@ -0,0 +1,56 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tree + +import "io" + +// streamPrinter prints the tree nodes in a streaming fashion. +type streamPrinter struct { + w io.Writer + prefix string + prevNode *node +} + +// newStreamPrinter creates a new stream printer. +// +// prefix is the prefix string that will be inherited by the nodes that are +// printed. +func newStreamPrinter(prefix string, w io.Writer) *streamPrinter { + return &streamPrinter{ + w: w, + prefix: prefix, + } +} + +// PrintNode adds a new node to be ready to print. +func (p *streamPrinter) PrintNode(node *node) error { + if p.prevNode == nil { + p.prevNode = node + return nil + } + if err := print(p.w, p.prefix, treeItemPrefix, p.prefix+subTreePrefix, p.prevNode); err != nil { + return err + } + p.prevNode = node + return nil +} + +// Flush prints the last node and completes the printing. +func (p *streamPrinter) Flush() error { + if p.prevNode != nil { + // print the last node + return print(p.w, p.prefix, treeItemPrefixLast, p.prefix+subTreePrefixLast, p.prevNode) + } + return nil +} diff --git a/cmd/notation/internal/display/metadata/tree/printer_test.go b/cmd/notation/internal/display/metadata/tree/printer_test.go new file mode 100644 index 000000000..0b78a023f --- /dev/null +++ b/cmd/notation/internal/display/metadata/tree/printer_test.go @@ -0,0 +1,123 @@ +// Copyright The Notary Project Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tree + +import ( + "bytes" + "testing" +) + +func TestStreamingPrinter(t *testing.T) { + t.Run("empty output", func(t *testing.T) { + expected := "" + buff := &bytes.Buffer{} + p := newStreamPrinter("", buff) + p.Flush() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("one node", func(t *testing.T) { + expected := "└── a\n" + buff := &bytes.Buffer{} + p := newStreamPrinter("", buff) + p.PrintNode(newNode("a")) + p.Flush() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two nodes", func(t *testing.T) { + expected := `├── a +└── b +` + buff := &bytes.Buffer{} + p := newStreamPrinter("", buff) + p.PrintNode(newNode("a")) + p.PrintNode(newNode("b")) + p.Flush() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two node with complex structure", func(t *testing.T) { + expected := `├── a +│ ├── b +│ │ └── c +│ └── d +└── e + ├── f + │ └── g + └── h +` + buff := &bytes.Buffer{} + p := newStreamPrinter("", buff) + // create the tree + a := newNode("a") + b := a.Add("b") + b.Add("c") + a.Add("d") + p.PrintNode(a) + + e := newNode("e") + f := e.Add("f") + f.Add("g") + e.Add("h") + p.PrintNode(e) + + p.Flush() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) + + t.Run("two node with prefix", func(t *testing.T) { + expected := ` │ ├── a + │ │ ├── b + │ │ │ └── c + │ │ └── d + │ └── e + │ ├── f + │ │ └── g + │ └── h +` + buff := &bytes.Buffer{} + p := newStreamPrinter(" │ ", buff) + // create the tree + a := newNode("a") + b := a.Add("b") + b.Add("c") + a.Add("d") + p.PrintNode(a) + + e := newNode("e") + f := e.Add("f") + f.Add("g") + e.Add("h") + p.PrintNode(e) + + p.Flush() + + if buff.String() != expected { + t.Fatalf("expected %s, got %s", expected, buff.String()) + } + }) +} diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 1b9c1c477..098659de9 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -19,10 +19,11 @@ import ( "fmt" notationregistry "github.com/notaryproject/notation-go/registry" + "github.com/notaryproject/notation/cmd/notation/internal/display" cmderr "github.com/notaryproject/notation/cmd/notation/internal/errors" "github.com/notaryproject/notation/cmd/notation/internal/experimental" + "github.com/notaryproject/notation/cmd/notation/internal/option" "github.com/notaryproject/notation/internal/cmd" - "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" ) @@ -30,6 +31,7 @@ import ( type listOpts struct { cmd.LoggingFlagOpts SecureFlagOpts + option.Common reference string ociLayout bool inputType inputType @@ -73,6 +75,7 @@ Example - [Experimental] List signatures of an OCI artifact identified by a tag if opts.ociLayout { opts.inputType = inputTypeOCILayout } + opts.Common.Parse(cmd) return experimental.CheckFlagsAndWarn(cmd, "oci-layout") }, RunE: func(cmd *cobra.Command, args []string) error { @@ -95,6 +98,7 @@ func runList(ctx context.Context, opts *listOpts) error { ctx = opts.LoggingFlagOpts.InitializeLogger(ctx) // initialize + displayHandler := display.NewListHandler(opts.Printer) reference := opts.reference // always use the Referrers API, if not supported, automatically fallback to // the referrers tag schema @@ -102,53 +106,24 @@ func runList(ctx context.Context, opts *listOpts) error { if err != nil { return err } - targetDesc, resolvedRef, err := resolveReferenceWithWarning(ctx, opts.inputType, reference, sigRepo, "list") + manifestDesc, resolvedRef, err := resolveReference(ctx, opts.inputType, reference, sigRepo, func(ref string, manifestDesc ocispec.Descriptor) { + opts.Printer.PrintErrorf("Warning: Always list the artifact using digest(@sha256:...) rather than a tag(:%s) because resolved digest may not point to the same signed artifact, as tags are mutable.\n", ref) + }) if err != nil { return err } - // print all signature manifest digests - return printSignatureManifestDigests(ctx, targetDesc, sigRepo, resolvedRef, opts.maxSignatures) -} - -// printSignatureManifestDigests returns the signature manifest digests of -// the subject manifest. -func printSignatureManifestDigests(ctx context.Context, targetDesc ocispec.Descriptor, sigRepo notationregistry.Repository, ref string, maxSigs int) error { - titlePrinted := false - printTitle := func() { - if !titlePrinted { - fmt.Println(ref) - fmt.Printf("└── %s\n", notationregistry.ArtifactTypeNotation) - titlePrinted = true - } - } + displayHandler.OnReferenceResolved(resolvedRef) - var prevDigest digest.Digest - err := listSignatures(ctx, sigRepo, targetDesc, maxSigs, func(sigManifestDesc ocispec.Descriptor) error { - // print the previous signature digest - if prevDigest != "" { - printTitle() - fmt.Printf(" ├── %s\n", prevDigest) - } - prevDigest = sigManifestDesc.Digest - return nil - }) - // print the last signature digest - if prevDigest != "" { - printTitle() - fmt.Printf(" └── %s\n", prevDigest) - } - if err != nil { + // list signatures + if err := listSignatures(ctx, sigRepo, manifestDesc, opts.maxSignatures, displayHandler.OnSignatureListed); err != nil { var errExceedMaxSignatures cmderr.ErrorExceedMaxSignatures if !errors.As(err, &errExceedMaxSignatures) { return err } - fmt.Println("Warning:", errExceedMaxSignatures) + opts.Printer.PrintErrorf("Warning: %v\n", err) } - if !titlePrinted { - fmt.Printf("%s has no associated signature\n", ref) - } - return nil + return displayHandler.Render() } // listSignatures lists signatures associated with manifestDesc with number of diff --git a/test/e2e/suite/command/list.go b/test/e2e/suite/command/list.go index 7599ae949..18c36f22a 100644 --- a/test/e2e/suite/command/list.go +++ b/test/e2e/suite/command/list.go @@ -72,17 +72,17 @@ var _ = Describe("notation list", func() { } notation.Exec("list", "--oci-layout", ociLayout.ReferenceWithDigest()). - MatchKeyWords( - "└── application/vnd.cncf.notary.signature", - "└── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f", - ) + MatchContent(ociLayout.ReferenceWithDigest() + ` +└── application/vnd.cncf.notary.signature + └── sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f +`) }) }) It("oci-layout with no signature", func() { HostWithOCILayout(BaseOptionsWithExperimental(), func(notation *utils.ExecOpts, ociLayout *OCILayout, vhost *utils.VirtualHost) { notation.Exec("list", "--oci-layout", ociLayout.ReferenceWithDigest()). - MatchKeyWords("has no associated signature") + MatchContent(ociLayout.ReferenceWithDigest() + " has no associated signatures\n") }) }) diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 new file mode 100644 index 000000000..46af4a72f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"e38aea13f2b051f7183cca64a51ee676f4d4456f41d9fecf15eb367efc7343b9\"]","org.opencontainers.image.created":"2023-06-27T05:30:33Z"},"artifactType":"application/vnd.cncf.notary.signature"}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a new file mode 100644 index 000000000..9e26dfeeb --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 new file mode 100644 index 000000000..764af2de0 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070 @@ -0,0 +1 @@ +awesome notation diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f new file mode 100644 index 000000000..2ad47c0ca --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50","size":2070}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"e38aea13f2b051f7183cca64a51ee676f4d4456f41d9fecf15eb367efc7343b9\"]","org.opencontainers.image.created":"2023-06-27T05:30:33Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 new file mode 100644 index 000000000..434f7fe25 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481 @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6Yjg0NzlkZTNmODhmYjI1OWEwYTllYTgyYTViMmEwNTJhMWVmM2M0ZWJiY2ZjNjE0ODJkNWFlNGM4MzFmOGFmOSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjUxN319","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDI1LTAyLTE3VDA4OjE3OjA5WiJ9","header":{"x5c":["MIIDRjCCAi6gAwIBAgIBZzANBgkqhkiG9w0BAQsFADBSMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTETMBEGA1UEAxMKdGVzdGNlcnQxMTAeFw0yNTAyMTcwODE1NDlaFw0yNTAyMTgwODE1NDlaMFIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJXQTEQMA4GA1UEBxMHU2VhdHRsZTEPMA0GA1UEChMGTm90YXJ5MRMwEQYDVQQDEwp0ZXN0Y2VydDExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4e5o57Z39his+TYHXdvpVw2RuW9a1VjbmWBfXtpG0ZQXV1EuTJ1C58n0zNlSoaXiwxiTCVo4bT8LA2AybsvNwR+MH95luFoVfXZuORcFffSsi76tM9xr3BbMAZE6rDTWahxEuurfkCoqrNLKV4Hl9uIWVPJCavEnUxiGSAUIKGcA+gbqSIRGrNrvDo6qqY8lNeXrbIWJemwzRLdRi+V71xq9tuphmEilzk+80uN7q4D4gdlAn2hpRFZmiSiaVCmZTaiW1miJBcus2r0eWwjCRSpgDg1GqpKKfw2Tncx5Xeg1Ki0Qk2M/GEhXp6zHWkkkHxP4pnx6AOVmMxa/gxZLjQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAJJ0H0mVpMCrpQcNHsp8AyWh/0yoou8esTCvCSKLX6bHjW6uHO6gCDiOQkOyUsnc//lMVBN4s3G3bbQMHoeMjZ0ptFjJN+Mq2PHAzeXZhC4jDnztDcpNXEyrKySSriG8AdJ4jxYbjCaHX1D/z+O47F6dNi2inXJgR+KYKt6SFWAGIKXEkjx4Etuc7t1nFOZYVlBSyY1KAahGUzfusoSTHNdF+ENTa9AgQOtH8x1P8vl74V8dTu7KuFTIYtnDHo8vmblWm0I+et6zSwAIbgQs7bAZXsMt3wvisD0mdn+QpKIEJYbTcPKQRkiZD4K5+6nmmP8rqLKekwWMSlP5zYhgmvc="],"io.cncf.notary.signingAgent":"notation-go/1.3.0+unreleased"},"signature":"V2PFO3FSSSYTf4r7-1sYM_NkYbMN3-ALY-4goPGUn1LblUXsz5Lcgm23K3eQVe1rik949x7PO2TcvonPVWxvLwNiSTULUNxDXcH9oU0lhFPpF7_5_ZwrHxILTycVDs8xGR_aUC6uufQW0suJFwSxhsuRhfJIkmU6-H_pV1_DVP50Fff2haHzgppZBVUqQZmUGl16XaLOVIvYglVc2tyqFbN1S8NJTPMcZomiw4I52rYhSB18Ozyr43sGkBNV_gMJT7PK_0zwRKCdJ7d5Fy9eo3QkUiMiqyHkVsDrXPpqpAReo18P340BOkvQjtBYtXT-T2F6TbuIP6aLXfJ1G9VOGw"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 new file mode 100644 index 000000000..961e959cf --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9 @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.notation.config","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/vnd.oci.image.layer.v1.tar","digest":"sha256:6de5fe1f75e4b1094c98086f403d5fd8117776ebe5aad1609281403d13dea070","size":17,"annotations":{"org.opencontainers.image.title":"e2e.txt"}}],"annotations":{"org.opencontainers.image.created":"2023-06-27T05:01:36Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf new file mode 100644 index 000000000..cee0e2416 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf @@ -0,0 +1 @@ +{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","config":{"mediaType":"application/vnd.cncf.notary.signature","digest":"sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a","size":2},"layers":[{"mediaType":"application/jose+json","digest":"sha256:a54333638acfa773a6b25429ee409b558d39c6f3c45dd14f714d72452bc36481","size":2093}],"subject":{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517},"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"a10293ee855ac897fc6ffefadb0b72d4bd8042029f5108f404d7ca9b4324ca97\"]","org.opencontainers.image.created":"2025-02-17T08:17:09Z"}} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 new file mode 100644 index 000000000..193127179 --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/blobs/sha256/c9af07e038b2b1a463e648986ad7273157511ffeba0b689f33e5472bae471f50 @@ -0,0 +1 @@ +{"payload":"eyJ0YXJnZXRBcnRpZmFjdCI6eyJkaWdlc3QiOiJzaGEyNTY6Yjg0NzlkZTNmODhmYjI1OWEwYTllYTgyYTViMmEwNTJhMWVmM2M0ZWJiY2ZjNjE0ODJkNWFlNGM4MzFmOGFmOSIsIm1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5vY2kuaW1hZ2UubWFuaWZlc3QudjEranNvbiIsInNpemUiOjUxN319","protected":"eyJhbGciOiJQUzI1NiIsImNyaXQiOlsiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSJdLCJjdHkiOiJhcHBsaWNhdGlvbi92bmQuY25jZi5ub3RhcnkucGF5bG9hZC52MStqc29uIiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1NjaGVtZSI6Im5vdGFyeS54NTA5IiwiaW8uY25jZi5ub3Rhcnkuc2lnbmluZ1RpbWUiOiIyMDIzLTA2LTI3VDEzOjMwOjMzKzA4OjAwIn0","header":{"x5c":["MIIDOjCCAiKgAwIBAgIBUTANBgkqhkiG9w0BAQsFADBLMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEMMAoGA1UEAxMDZTJlMCAXDTIyMTIxOTAyNDMzOVoYDzIxMjIxMjE5MDI0MzM5WjBLMQswCQYDVQQGEwJVUzELMAkGA1UECBMCV0ExEDAOBgNVBAcTB1NlYXR0bGUxDzANBgNVBAoTBk5vdGFyeTEMMAoGA1UEAxMDZTJlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwNrKIzpESDkY7Kah+sWKeXDNE7yUBaqW58cyMtkpk9/Q6QLbndMxdt8VjD3y4bSBhZXLdUkW0S4hSNZXQwkYm3yooxJXj+uOI9NnEPt/gAkPYri4TdKpTFSBzE4uJ0HrYDBUir3Dars/CXiusmdCnSWKsgm05ItkHdHEGtor716aWdnGuFNvyzJyaC3XLFpo1OwEwTyxf4Yix4UtvwNDB4TOJRH84avSoFCua8xbRpiBJ0QoX6zY0Yr9qbvMBQIcmpQ3uWOiEeVMDjRE0XzhiWSevVfZTPJcYpsAyBTJGJLIcHU7JMYnwKP6IzUg5T589lvRVD7up4sAC7izOKGzPQIDAQABoycwJTAOBgNVHQ8BAf8EBAMCB4AwEwYDVR0lBAwwCgYIKwYBBQUHAwMwDQYJKoZIhvcNAQELBQADggEBAKacaJ+O9rTIdfLHCdneLQ6RN92Id5dl8PGh/Xig3AWSOtLgIiRRsdxVH5PZkGOHosellQG5fCvQOwB0x8O+2YDKLOfgVIJWd6X85NdvyCdX2ElYRmvX9ON5WVguGLluwkOfJ26M8d8ftXrcc97qaKk4EHS8R/LCWqZNDRiRCA0OtNP9cUkKFaIG1hgWgEieVWnxrCyUDbTX3uCiJKNzSOmI3psF3WIhabU7/7gBm95nWhgwS91qAxavccVkY6hqAPj9tjJH5UPI5RR8kDB5rWiCIx1YHuH+z1eAYUHWVvZvneVniNBI8qGoGBz9HkrX5ecf+V7zaxyy0FoWlEX1z8k="],"io.cncf.notary.signingAgent":"Notation/1.0.0"},"signature":"vJnEBEYvmRaz9jpOKJzGGorX7COBww5KzXVLuZLeB0ponDApf15r4WhajjVXRlbh2qyxZP3v3PkR8-SVh7ku1SDQ416H1uNNjkKJdovnu-CNAdAborfJCUzTLRDyioUXgp8y098dyPabWNo_hNJautZVvQkerEWLul5c40SclrruKtrz1dK2w3BIcDQR6hOIlQ2VvwnmsCkzDbkljlWGwwjR9xfpePhzAMP4K9MQc0KR4Vmu3Qd9aBdxtI9NW477B2XC2jSJ6erh74HCDtJcIpVxI30i-2JXKvSlrATGGR_Tj5x8vFgpkHGmbIEJFLzjqM347O_Q0A5SLIW-rWE2WQ"} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json new file mode 100644 index 000000000..8cda1e0af --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/index.json @@ -0,0 +1 @@ +{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9","size":517,"annotations":{"org.opencontainers.image.ref.name":"v1"}},{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:13e107e0dd246f07ff18998a3d39e226965c41477a1bc1765f7617632ff3f283","size":485,"annotations":{"org.opencontainers.image.ref.name":"sha256-b8479de3f88fb259a0a9ea82a5b2a052a1ef3c4ebbcfc61482d5ae4c831f8af9"}},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:90ceaff260d657d797c408ac73564a9c7bb9d86055877c2a811f0e63b8c6524f","size":728},{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:c3ebe4a20b6832328fc5078a7795ddc1114b896e13fca2add38109c3866b5fbf","size":728,"annotations":{"io.cncf.notary.x509chain.thumbprint#S256":"[\"a10293ee855ac897fc6ffefadb0b72d4bd8042029f5108f404d7ca9b4324ca97\"]","org.opencontainers.image.created":"2025-02-17T08:17:09Z"}}]} \ No newline at end of file diff --git a/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout new file mode 100644 index 000000000..1343d370f --- /dev/null +++ b/test/e2e/testdata/registry/oci_layout/e2e-valid-multiple-signatures/oci-layout @@ -0,0 +1 @@ +{"imageLayoutVersion":"1.0.0"} \ No newline at end of file