Skip to content

Commit

Permalink
feat: support formatted output in manifest fetch
Browse files Browse the repository at this point in the history
Signed-off-by: Billy Zha <[email protected]>
  • Loading branch information
qweeah committed Dec 4, 2023
1 parent 4cc2295 commit f31eca2
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 10 deletions.
37 changes: 37 additions & 0 deletions cmd/oras/internal/meta/manfiest_fetch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
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 meta

import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)

type manifestFetch struct {
DigestReference
Config Descriptor `json:"config"`
Layers []Descriptor `json:"layers"`
}

// NewManifestFetch creates a new manifest fetch metadata for formatting.
func NewManifestFetch(path string, digest string, manifest ocispec.Manifest) manifestFetch {
mf := manifestFetch{
DigestReference: ToDigestReference(path, digest),
}
for _, layer := range manifest.Layers {
mf.Layers = append(mf.Layers, ToDescriptor(path, layer))
}
return mf
}
13 changes: 9 additions & 4 deletions cmd/oras/internal/meta/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ type DigestReference struct {
Reference string `json:"reference"`
}

// ToDigestReference converts a name and digest to a digest reference.
func ToDigestReference(name string, digest string) DigestReference {
return DigestReference{
Reference: name + "@" + digest,
}
}

// Descriptor is a descriptor with digest reference.
type Descriptor struct {
DigestReference
Expand All @@ -31,9 +38,7 @@ type Descriptor struct {
// ToDescriptor converts a descriptor to a descriptor with digest reference.
func ToDescriptor(name string, desc ocispec.Descriptor) Descriptor {
return Descriptor{
DigestReference: DigestReference{
Reference: name + "@" + desc.Digest.String(),
},
Descriptor: desc,
DigestReference: ToDigestReference(name, desc.Digest.String()),
Descriptor: desc,
}
}
42 changes: 37 additions & 5 deletions cmd/oras/root/manifest/fetch.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ package manifest
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"

ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/spf13/cobra"
"oras.land/oras-go/v2"
"oras.land/oras-go/v2/registry/remote"
"oras.land/oras/cmd/oras/internal/display"
"oras.land/oras/cmd/oras/internal/meta"
"oras.land/oras/cmd/oras/internal/option"
"oras.land/oras/internal/docker"
)

type fetchOptions struct {
Expand All @@ -36,6 +38,7 @@ type fetchOptions struct {
option.Platform
option.Pretty
option.Target
option.Format

mediaTypes []string
outputPath string
Expand Down Expand Up @@ -71,14 +74,27 @@ Example - Fetch raw manifest from an OCI layout archive file 'layout.tar':
`,
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
if opts.outputPath == "-" && opts.OutputDescriptor {
return errors.New("`--output -` cannot be used with `--descriptor` at the same time")
toCheck := []struct {
name string
isPresent func() bool
}{
{"--output -", func() bool { return opts.outputPath == "-" }},
{"--format", func() bool { return opts.Template != "" }},
{"--descriptor", func() bool { return opts.OutputDescriptor }},
}
for i := range toCheck {
for j := i + 1; j < len(toCheck); j++ {
if toCheck[i].isPresent() && toCheck[j].isPresent() {
return fmt.Errorf("`%s` cannot be used with `%s` at the same time", toCheck[i].name, toCheck[j].name)
}
}
}
opts.RawReference = args[0]
return option.Parse(&opts)
},
Aliases: []string{"get"},
RunE: func(cmd *cobra.Command, args []string) error {
display.Set(opts.Template, opts.TTY)
return fetchManifest(cmd.Context(), opts)
},
}
Expand Down Expand Up @@ -129,7 +145,24 @@ func fetchManifest(ctx context.Context, opts fetchOptions) (fetchErr error) {
return fmt.Errorf("failed to fetch the content of %q: %w", opts.RawReference, err)
}

if opts.outputPath == "" || opts.outputPath == "-" {
if opts.Template != "" {
// output formatted data
switch desc.MediaType {
case ocispec.MediaTypeImageManifest, docker.MediaTypeManifest:
var manifest ocispec.Manifest
if err := json.Unmarshal(content, &manifest); err != nil {
return err
}
if err := json.Unmarshal(content, &manifest); err != nil {
return err
}
if err = opts.WriteTo(os.Stdout, meta.NewManifestFetch(opts.outputPath, desc.Digest.String(), manifest)); err != nil {
return err
}
default:
return fmt.Errorf("cannot apply template to %q: unsupported media type %s", opts.RawReference, desc.MediaType)
}
} else if opts.outputPath == "" || opts.outputPath == "-" {
// output manifest content
return opts.Output(os.Stdout, content)
}
Expand All @@ -148,6 +181,5 @@ func fetchManifest(ctx context.Context, opts fetchOptions) (fetchErr error) {
}
return opts.Output(os.Stdout, descBytes)
}

return nil
}
2 changes: 1 addition & 1 deletion cmd/oras/root/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ func doPull(ctx context.Context, src oras.ReadOnlyTarget, dst oras.GraphTarget,
for _, s := range successors {
if name, ok := s.Annotations[ocispec.AnnotationTitle]; ok {
result.filesLock.Lock()
result.files = append(result.files, meta.NewFile(name, po.Output, desc, fmt.Sprintf("%s@%s", po.Path, desc.Digest)))
result.files = append(result.files, meta.NewFile(name, po.Output, desc, po.Path))
result.filesLock.Unlock()
if err := printOnce(&printed, s, promptRestored, po.Verbose, tracked); err != nil {
return err
Expand Down

0 comments on commit f31eca2

Please sign in to comment.