diff --git a/cmd/oras/internal/display/content/discard.go b/cmd/oras/internal/display/content/discard.go index cdf543797..546d73c68 100644 --- a/cmd/oras/internal/display/content/discard.go +++ b/cmd/oras/internal/display/content/discard.go @@ -24,7 +24,12 @@ func (discardHandler) OnContentFetched(ocispec.Descriptor, []byte) error { return nil } +// OnContentCreated implements ManifestIndexCreateHandler. +func (discardHandler) OnContentCreated(content []byte) error { + return nil +} + // NewDiscardHandler returns a new discard handler. -func NewDiscardHandler() ManifestFetchHandler { +func NewDiscardHandler() discardHandler { return discardHandler{} } diff --git a/cmd/oras/internal/display/content/interface.go b/cmd/oras/internal/display/content/interface.go index 2c35fc552..9642aa82d 100644 --- a/cmd/oras/internal/display/content/interface.go +++ b/cmd/oras/internal/display/content/interface.go @@ -24,3 +24,12 @@ type ManifestFetchHandler interface { // OnContentFetched is called after the manifest content is fetched. OnContentFetched(desc ocispec.Descriptor, content []byte) error } + +// ManifestIndexCreateHandler handles raw output for manifest index create events. +type ManifestIndexCreateHandler interface { + // OnContentCreated is called after the index content is created. + OnContentCreated(content []byte) error +} + +// ManifestIndexUpdateHandler handles raw output for manifest index update events. +type ManifestIndexUpdateHandler ManifestIndexCreateHandler diff --git a/cmd/oras/internal/display/content/manifest_index.go b/cmd/oras/internal/display/content/manifest_index.go new file mode 100644 index 000000000..6fd9717e4 --- /dev/null +++ b/cmd/oras/internal/display/content/manifest_index.go @@ -0,0 +1,54 @@ +/* +Copyright The ORAS 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 content + +import ( + "fmt" + "io" + "os" + + "oras.land/oras/cmd/oras/internal/output" +) + +// manifestIndexCreate handles raw content output. +type manifestIndexCreate struct { + pretty bool + stdout io.Writer + outputPath string +} + +// OnContentCreated is called after index content is created. +func (h *manifestIndexCreate) OnContentCreated(manifest []byte) error { + out := h.stdout + if h.outputPath != "-" && h.outputPath != "" { + f, err := os.Create(h.outputPath) + if err != nil { + return fmt.Errorf("failed to open %q: %w", h.outputPath, err) + } + defer f.Close() + out = f + } + return output.PrintJSON(out, manifest, h.pretty) +} + +// NewManifestIndexCreateHandler creates a new handler. +func NewManifestIndexCreateHandler(out io.Writer, pretty bool, outputPath string) ManifestIndexCreateHandler { + return &manifestIndexCreate{ + pretty: pretty, + stdout: out, + outputPath: outputPath, + } +} diff --git a/cmd/oras/internal/display/handler.go b/cmd/oras/internal/display/handler.go index 9fec99a56..b38c92948 100644 --- a/cmd/oras/internal/display/handler.go +++ b/cmd/oras/internal/display/handler.go @@ -174,26 +174,56 @@ func NewManifestPushHandler(printer *output.Printer) metadata.ManifestPushHandle return text.NewManifestPushHandler(printer) } -// NewManifestIndexCreateHandler returns status and metadata handlers for index create command. -func NewManifestIndexCreateHandler(outputPath string, printer *output.Printer) (status.ManifestIndexCreateHandler, metadata.ManifestIndexCreateHandler, error) { +// NewManifestIndexCreateHandler returns status, metadata and content handlers for index create command. +func NewManifestIndexCreateHandler(outputPath string, printer *output.Printer, pretty bool) ( + status.ManifestIndexCreateHandler, + metadata.ManifestIndexCreateHandler, + content.ManifestIndexCreateHandler, + error) { var statusHandler status.ManifestIndexCreateHandler - if outputPath == "-" { + var metadataHandler metadata.ManifestIndexCreateHandler + var contentHandler content.ManifestIndexCreateHandler + switch outputPath { + case "": + statusHandler = status.NewTextManifestIndexCreateHandler(printer) + metadataHandler = text.NewManifestIndexCreateHandler(printer) + contentHandler = content.NewDiscardHandler() + case "-": statusHandler = status.NewDiscardHandler() - } else { + metadataHandler = metadata.NewDiscardHandler() + contentHandler = content.NewManifestIndexCreateHandler(printer, pretty, outputPath) + default: statusHandler = status.NewTextManifestIndexCreateHandler(printer) + metadataHandler = text.NewManifestIndexCreateHandler(printer) + contentHandler = content.NewManifestIndexCreateHandler(printer, pretty, outputPath) } - return statusHandler, text.NewManifestIndexCreateHandler(printer), nil + return statusHandler, metadataHandler, contentHandler, nil } -// NewManifestIndexUpdateHandler returns status and metadata handlers for index update command. -func NewManifestIndexUpdateHandler(outputPath string, printer *output.Printer) (status.ManifestIndexUpdateHandler, metadata.ManifestIndexUpdateHandler, error) { +// NewManifestIndexUpdateHandler returns status, metadata and content handlers for index update command. +func NewManifestIndexUpdateHandler(outputPath string, printer *output.Printer, pretty bool) ( + status.ManifestIndexUpdateHandler, + metadata.ManifestIndexUpdateHandler, + content.ManifestIndexUpdateHandler, + error) { var statusHandler status.ManifestIndexUpdateHandler - if outputPath == "-" { + var metadataHandler metadata.ManifestIndexUpdateHandler + var contentHandler content.ManifestIndexUpdateHandler + switch outputPath { + case "": + statusHandler = status.NewTextManifestIndexUpdateHandler(printer) + metadataHandler = text.NewManifestIndexCreateHandler(printer) + contentHandler = content.NewDiscardHandler() + case "-": statusHandler = status.NewDiscardHandler() - } else { + metadataHandler = metadata.NewDiscardHandler() + contentHandler = content.NewManifestIndexCreateHandler(printer, pretty, outputPath) + default: statusHandler = status.NewTextManifestIndexUpdateHandler(printer) + metadataHandler = text.NewManifestIndexCreateHandler(printer) + contentHandler = content.NewManifestIndexCreateHandler(printer, pretty, outputPath) } - return statusHandler, text.NewManifestIndexCreateHandler(printer), nil + return statusHandler, metadataHandler, contentHandler, nil } // NewCopyHandler returns copy handlers. diff --git a/cmd/oras/internal/display/metadata/discard.go b/cmd/oras/internal/display/metadata/discard.go index 52c203eb2..bab771270 100644 --- a/cmd/oras/internal/display/metadata/discard.go +++ b/cmd/oras/internal/display/metadata/discard.go @@ -15,7 +15,10 @@ limitations under the License. package metadata -import ocispec "github.com/opencontainers/image-spec/specs-go/v1" +import ( + "github.com/opencontainers/go-digest" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) type discard struct{} @@ -28,3 +31,13 @@ func NewDiscardHandler() discard { func (discard) OnFetched(string, ocispec.Descriptor, []byte) error { return nil } + +// OnTagged implements ManifestIndexCreateHandler. +func (discard) OnTagged(desc ocispec.Descriptor, tag string) error { + return nil +} + +// OnCompleted implements ManifestIndexCreateHandler. +func (discard) OnCompleted(digest digest.Digest) error { + return nil +} diff --git a/cmd/oras/root/manifest/index/create.go b/cmd/oras/root/manifest/index/create.go index 4c982d2cc..831c57ff7 100644 --- a/cmd/oras/root/manifest/index/create.go +++ b/cmd/oras/root/manifest/index/create.go @@ -20,10 +20,8 @@ import ( "context" "encoding/json" "fmt" - "os" "strings" - "github.com/opencontainers/go-digest" "github.com/opencontainers/image-spec/specs-go" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/spf13/cobra" @@ -109,7 +107,7 @@ func createIndex(cmd *cobra.Command, opts createOptions) error { if err != nil { return err } - displayStatus, displayMetadata, err := display.NewManifestIndexCreateHandler(opts.outputPath, opts.Printer) + displayStatus, displayMetadata, displayContent, err := display.NewManifestIndexCreateHandler(opts.outputPath, opts.Printer, opts.Pretty.Pretty) if err != nil { return err } @@ -133,19 +131,15 @@ func createIndex(cmd *cobra.Command, opts createOptions) error { if err := displayStatus.OnIndexPacked(descriptor.ShortDigest(desc)); err != nil { return err } - - switch opts.outputPath { - case "": - err = pushIndex(ctx, displayStatus.OnIndexPushed, displayMetadata.OnCompleted, displayMetadata.OnTagged, target, desc, indexBytes, opts.Reference, opts.extraRefs, opts.AnnotatedReference()) - case "-": - err = opts.Output(os.Stdout, indexBytes) - default: - if err := displayMetadata.OnCompleted(desc.Digest); err != nil { + if err := displayContent.OnContentCreated(indexBytes); err != nil { + return err + } + if opts.outputPath == "" { + if err := pushIndex(ctx, displayStatus.OnIndexPushed, displayMetadata.OnTagged, target, desc, indexBytes, opts.Reference, opts.extraRefs, opts.AnnotatedReference()); err != nil { return err } - err = os.WriteFile(opts.outputPath, indexBytes, 0666) } - return err + return displayMetadata.OnCompleted(desc.Digest) } func fetchSourceManifests(ctx context.Context, displayStatus status.ManifestIndexCreateHandler, target oras.ReadOnlyTarget, sources []string) ([]ocispec.Descriptor, error) { @@ -199,10 +193,7 @@ func getPlatform(ctx context.Context, target oras.ReadOnlyTarget, manifestBytes return &platform, nil } -func pushIndex(ctx context.Context, - onIndexPushed func(path string) error, - onCompleted func(digest digest.Digest) error, - onTagged func(desc ocispec.Descriptor, tag string) error, +func pushIndex(ctx context.Context, onIndexPushed func(path string) error, onTagged func(desc ocispec.Descriptor, tag string) error, target oras.Target, desc ocispec.Descriptor, content []byte, ref string, extraRefs []string, path string) error { // push the index var err error @@ -223,5 +214,5 @@ func pushIndex(ctx context.Context, return err } } - return onCompleted(desc.Digest) + return nil } diff --git a/cmd/oras/root/manifest/index/update.go b/cmd/oras/root/manifest/index/update.go index 35e28a170..61c0c326f 100644 --- a/cmd/oras/root/manifest/index/update.go +++ b/cmd/oras/root/manifest/index/update.go @@ -19,7 +19,6 @@ import ( "context" "encoding/json" "fmt" - "os" "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" @@ -114,7 +113,7 @@ func updateIndex(cmd *cobra.Command, opts updateOptions) error { if err := opts.EnsureReferenceNotEmpty(cmd, true); err != nil { return err } - displayStatus, displayMetadata, err := display.NewManifestIndexUpdateHandler(opts.outputPath, opts.Printer) + displayStatus, displayMetadata, displayContent, err := display.NewManifestIndexUpdateHandler(opts.outputPath, opts.Printer, opts.Pretty.Pretty) if err != nil { return err } @@ -134,30 +133,25 @@ func updateIndex(cmd *cobra.Command, opts updateOptions) error { if err != nil { return err } - index.Manifests = manifests indexBytes, err := json.Marshal(index) if err != nil { return err } desc := content.NewDescriptorFromBytes(index.MediaType, indexBytes) - if err := displayStatus.OnIndexUpdated(desc.Digest); err != nil { return err } path := getPushPath(opts.RawReference, opts.Type, opts.Reference, opts.Path) - switch opts.outputPath { - case "": - err = pushIndex(ctx, displayStatus.OnIndexPushed, displayMetadata.OnCompleted, displayMetadata.OnTagged, target, desc, indexBytes, opts.Reference, opts.tags, path) - case "-": - err = opts.Output(os.Stdout, indexBytes) - default: - if err := displayMetadata.OnCompleted(desc.Digest); err != nil { + if err := displayContent.OnContentCreated(indexBytes); err != nil { + return err + } + if opts.outputPath == "" { + if err := pushIndex(ctx, displayStatus.OnIndexPushed, displayMetadata.OnTagged, target, desc, indexBytes, opts.Reference, opts.tags, path); err != nil { return err } - err = os.WriteFile(opts.outputPath, indexBytes, 0666) } - return err + return displayMetadata.OnCompleted(desc.Digest) } func fetchIndex(ctx context.Context, handler status.ManifestIndexUpdateHandler, target oras.ReadOnlyTarget, reference string) (ocispec.Index, error) {