From 8d49927d0f22b522859fd491d3c1da2944b984bf Mon Sep 17 00:00:00 2001 From: Shiwei Zhang Date: Mon, 29 Mar 2021 13:11:44 +0800 Subject: [PATCH] support artifact discovery for oras cli Signed-off-by: Shiwei Zhang --- cmd/oras/discover.go | 92 ++++++++++++++++++++++++++++++++++++++++++++ cmd/oras/main.go | 9 ++++- pkg/oras/discover.go | 58 ++++++++++++++-------------- 3 files changed, 129 insertions(+), 30 deletions(-) create mode 100644 cmd/oras/discover.go diff --git a/cmd/oras/discover.go b/cmd/oras/discover.go new file mode 100644 index 000000000..f79e68662 --- /dev/null +++ b/cmd/oras/discover.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "errors" + "fmt" + + ctxo "github.com/deislabs/oras/pkg/context" + "github.com/deislabs/oras/pkg/oras" + + "github.com/containerd/containerd/reference" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +type discoverOptions struct { + targetRef string + artifactType string + verbose bool + + debug bool + configs []string + username string + password string + insecure bool + plainHTTP bool +} + +func discoverCmd() *cobra.Command { + var opts discoverOptions + cmd := &cobra.Command{ + Use: "discover [options] ", + Short: "discover artifacts from remote registry", + Long: `discover artifacts from remote registry + +Example - Discover artifacts of type "" linked with the specified reference: + oras discover --artifact-type application/vnd.cncf.notary.v2 localhost:5000/hello:latest +`, + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + if opts.artifactType == "" { + return errors.New("artifact type not specified") + } + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + opts.targetRef = args[0] + return runDiscover(opts) + }, + } + + cmd.Flags().StringVarP(&opts.artifactType, "artifact-type", "", "", "artifact type") + cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "verbose output") + + cmd.Flags().BoolVarP(&opts.debug, "debug", "d", false, "debug mode") + cmd.Flags().StringArrayVarP(&opts.configs, "config", "c", nil, "auth config path") + cmd.Flags().StringVarP(&opts.username, "username", "u", "", "registry username") + cmd.Flags().StringVarP(&opts.password, "password", "p", "", "registry password") + cmd.Flags().BoolVarP(&opts.insecure, "insecure", "", false, "allow connections to SSL registry without certs") + cmd.Flags().BoolVarP(&opts.plainHTTP, "plain-http", "", false, "use plain http and not https") + return cmd +} + +func runDiscover(opts discoverOptions) error { + ctx := context.Background() + if opts.debug { + logrus.SetLevel(logrus.DebugLevel) + } else if !opts.verbose { + ctx = ctxo.WithLoggerDiscarded(ctx) + } + + resolver := newResolver(opts.username, opts.password, opts.insecure, opts.plainHTTP, opts.configs...) + + desc, artifacts, err := oras.Discover(ctx, resolver, opts.targetRef, opts.artifactType) + if err != nil { + if err == reference.ErrObjectRequired { + return fmt.Errorf("image reference format is invalid. Please specify ") + } + return err + } + + fmt.Println("Discovered", len(artifacts), "artifacts referencing", opts.targetRef) + fmt.Println("Digest:", desc.Digest) + for _, artifact := range artifacts { + fmt.Println("Reference:", artifact.ArtifactType) + for _, blob := range artifact.Blobs { + fmt.Println("-", blob.Digest) + } + } + + return nil +} diff --git a/cmd/oras/main.go b/cmd/oras/main.go index 927a101c1..7593a45c5 100644 --- a/cmd/oras/main.go +++ b/cmd/oras/main.go @@ -11,7 +11,14 @@ func main() { Use: "oras [command]", SilenceUsage: true, } - cmd.AddCommand(pullCmd(), pushCmd(), loginCmd(), logoutCmd(), versionCmd()) + cmd.AddCommand( + pullCmd(), + pushCmd(), + discoverCmd(), + loginCmd(), + logoutCmd(), + versionCmd(), + ) if err := cmd.Execute(); err != nil { os.Exit(1) } diff --git a/pkg/oras/discover.go b/pkg/oras/discover.go index 496ce6ec9..47b626570 100644 --- a/pkg/oras/discover.go +++ b/pkg/oras/discover.go @@ -1,29 +1,29 @@ -package oras - -import ( - "context" - - "github.com/containerd/containerd/remotes" - artifactspec "github.com/notaryproject/artifacts/specs-go/v2" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" -) - -// Discover discovers artifacts referencing the specified artifact -func Discover(ctx context.Context, resolver remotes.Resolver, ref, artifactType string) (ocispec.Descriptor, []artifactspec.Artifact, error) { - _, desc, err := resolver.Resolve(ctx, ref) - if err != nil { - return ocispec.Descriptor{}, nil, err - } - - discoverer, err := resolver.Discoverer(ctx, ref) - if err != nil { - return ocispec.Descriptor{}, nil, err - } - - artifacts, err := discoverer.Discover(ctx, desc, artifactType) - if err != nil { - return ocispec.Descriptor{}, nil, err - } - - return desc, artifacts, err -} +package oras + +import ( + "context" + + "github.com/containerd/containerd/remotes" + artifactspec "github.com/notaryproject/artifacts/specs-go/v2" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +// Discover discovers artifacts referencing the specified artifact +func Discover(ctx context.Context, resolver remotes.Resolver, ref, artifactType string) (ocispec.Descriptor, []artifactspec.Artifact, error) { + _, desc, err := resolver.Resolve(ctx, ref) + if err != nil { + return ocispec.Descriptor{}, nil, err + } + + discoverer, err := resolver.Discoverer(ctx, ref) + if err != nil { + return ocispec.Descriptor{}, nil, err + } + + artifacts, err := discoverer.Discover(ctx, desc, artifactType) + if err != nil { + return ocispec.Descriptor{}, nil, err + } + + return desc, artifacts, err +}