diff --git a/cmd/oras/discover.go b/cmd/oras/discover.go index b3c284e84..4b6a1acaa 100644 --- a/cmd/oras/discover.go +++ b/cmd/oras/discover.go @@ -17,12 +17,13 @@ import ( ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/xlab/treeprint" ) type discoverOptions struct { targetRef string artifactType string - outputJSON bool + outputType string verbose bool debug bool @@ -51,7 +52,7 @@ Example - Discover artifacts of type "" linked with the specified reference: } cmd.Flags().StringVarP(&opts.artifactType, "artifact-type", "", "", "artifact type") - cmd.Flags().BoolVarP(&opts.outputJSON, "output-json", "", false, "output in JSON format") + cmd.Flags().StringVarP(&opts.outputType, "output", "o", "table", fmt.Sprintf("Format in which to display references (%s, %s, or %s). tree format will show all references including nested", "table", "json", "tree")) cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "verbose output") cmd.Flags().BoolVarP(&opts.debug, "debug", "d", false, "debug mode") @@ -73,29 +74,61 @@ func runDiscover(opts discoverOptions) error { resolver := newResolver(opts.username, opts.password, opts.insecure, opts.plainHTTP, opts.configs...) - desc, refs, err := oras.Discover(ctx, resolver, opts.targetRef, opts.artifactType) + rootNode := treeprint.NewWithRoot(opts.targetRef) + desc, refs, err := getAllReferences(ctx, resolver, opts.targetRef, opts.artifactType, rootNode, opts.outputType == "tree") if err != nil { - if err == reference.ErrObjectRequired { - return fmt.Errorf("image reference format is invalid. Please specify ") - } return err } - if opts.outputJSON { - printDiscoveredReferencesJSON(desc, refs) - } else { - fmt.Println("Discovered", len(refs), "artifacts referencing", opts.targetRef) + switch opts.outputType { + case "tree": + fmt.Println(rootNode.String()) + case "json": + printDiscoveredReferencesJSON(desc, *refs) + default: + fmt.Println("Discovered", len(*refs), "artifacts referencing", opts.targetRef) fmt.Println("Digest:", desc.Digest) - if len(refs) != 0 { + if len(*refs) != 0 { fmt.Println() - printDiscoveredReferencesTable(refs, opts.verbose) + printDiscoveredReferencesTable(*refs, opts.verbose) } } return nil } +func getAllReferences(ctx context.Context, resolver remotes.Resolver, targetRef string, artifactType string, treeNode treeprint.Tree, queryGraph bool) (ocispec.Descriptor, *[]remotes.DiscoveredArtifact, error) { + var results []remotes.DiscoveredArtifact + spec, err := reference.Parse(targetRef) + if err != nil { + return ocispec.Descriptor{}, nil, err + } + + desc, refs, err := oras.Discover(ctx, resolver, targetRef, artifactType) + if err != nil { + if err == reference.ErrObjectRequired { + return ocispec.Descriptor{}, nil, fmt.Errorf("image reference format is invalid. Please specify ") + } + return ocispec.Descriptor{}, nil, err + } + + for _, r := range refs { + branch := treeNode.AddBranch(fmt.Sprintf("[%s]%s", r.Artifact.ArtifactType, r.Digest)) + if queryGraph { + nestedRef := fmt.Sprintf("%s@%s", spec.Locator, r.Digest) + _, refs1, err := getAllReferences(ctx, resolver, nestedRef, "", branch, queryGraph) + if err != nil { + return ocispec.Descriptor{}, nil, err + } + results = append(results, *refs1...) + } + } + results = append(results, refs...) + + return desc, &results, nil +} + func printDiscoveredReferencesTable(refs []remotes.DiscoveredArtifact, verbose bool) { typeNameTitle := "Artifact Type" typeNameLength := len(typeNameTitle) diff --git a/go.mod b/go.mod index 23c759ded..e249bad81 100644 --- a/go.mod +++ b/go.mod @@ -27,6 +27,7 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/spf13/cobra v1.1.3 github.com/stretchr/testify v1.7.0 + github.com/xlab/treeprint v1.1.0 // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/sync v0.0.0-20201207232520-09787c993a3a ) diff --git a/go.sum b/go.sum index 88f8918b9..049735a80 100644 --- a/go.sum +++ b/go.sum @@ -504,6 +504,8 @@ github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmF github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= +github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI=