From 39aa10c1cab4b68c404122225ec37004dec3128c Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Wed, 20 Jul 2022 16:38:52 +0800 Subject: [PATCH 1/6] use cobra for notation cli Signed-off-by: zaihaoyin --- cmd/docker-notation/sign.go | 12 ++- cmd/notation/cache.go | 177 ++++++++++++++++-------------- cmd/notation/cert.go | 170 +++++++++++++++++------------ cmd/notation/cert_gen.go | 18 ++-- cmd/notation/common.go | 141 ++++++++++++++++++------ cmd/notation/key.go | 210 ++++++++++++++++++++++-------------- cmd/notation/list.go | 48 +++++---- cmd/notation/login.go | 79 ++++++++------ cmd/notation/logout.go | 32 ++++-- cmd/notation/main.go | 48 ++++----- cmd/notation/manifest.go | 21 ++-- cmd/notation/plugin.go | 33 +++--- cmd/notation/pull.go | 78 ++++++++------ cmd/notation/push.go | 59 ++++++---- cmd/notation/registry.go | 25 +++-- cmd/notation/sign.go | 97 ++++++++++------- cmd/notation/verify.go | 75 ++++++------- internal/cmd/flags.go | 36 +++---- internal/cmd/options.go | 18 ++++ internal/cmd/signer.go | 12 +-- 20 files changed, 826 insertions(+), 563 deletions(-) create mode 100644 internal/cmd/options.go diff --git a/cmd/docker-notation/sign.go b/cmd/docker-notation/sign.go index fa135a368..7a8f7b2ad 100644 --- a/cmd/docker-notation/sign.go +++ b/cmd/docker-notation/sign.go @@ -32,7 +32,15 @@ var signCommand = &cli.Command{ } func signImage(ctx *cli.Context) error { - signer, err := cmd.GetSigner(ctx) + // TODO: make this change only to make sure the code can be compiled + // According to the https://github.com/notaryproject/notation/discussions/251, + // we can update/deprecate it later + signerOpts := &cmd.SignerFlagOpts{ + Key: ctx.String(cmd.FlagKey.Name), + KeyFile: ctx.String(cmd.FlagKeyFile.Name), + CertFile: ctx.String(cmd.FlagCertFile.Name), + } + signer, err := cmd.GetSigner(signerOpts) if err != nil { return err } @@ -55,7 +63,7 @@ func signImage(ctx *cli.Context) error { } } sig, err := signer.Sign(ctx.Context, desc, notation.SignOptions{ - Expiry: cmd.GetExpiry(ctx), + Expiry: cmd.GetExpiry(ctx.Duration(cmd.FlagExpiry.Name)), }) if err != nil { return err diff --git a/cmd/notation/cache.go b/cmd/notation/cache.go index f42fd612c..20aac623a 100644 --- a/cmd/notation/cache.go +++ b/cmd/notation/cache.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "context" "errors" "fmt" "io/fs" @@ -11,80 +12,107 @@ import ( "github.com/notaryproject/notation/pkg/config" "github.com/opencontainers/go-digest" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" "oras.land/oras-go/v2/registry" ) -var ( - cacheCommand = &cli.Command{ - Name: "cache", - Usage: "Manage signature cache", - Subcommands: []*cli.Command{ - cacheListCommand, - cachePruneCommand, - cacheRemoveCommand, - }, +type cacheListOpts struct { + RemoteFlagOpts + reference string +} +type cachePruneOpts struct { + RemoteFlagOpts + references []string + all bool + purge bool + force bool +} + +type cacheRemoveOpts struct { + RemoteFlagOpts + reference string + sigDigests []string +} + +func cacheCommand() *cobra.Command { + command := &cobra.Command{ + Use: "cache", + Short: "Manage signature cache", } + command.AddCommand(cacheListCommand(nil), cachePruneCommand(nil), cacheRemoveCommand(nil)) + return command +} - cacheListCommand = &cli.Command{ - Name: "list", - Usage: "List signatures in cache", +func cacheListCommand(opts *cacheListOpts) *cobra.Command { + if opts == nil { + opts = &cacheListOpts{} + } + command := &cobra.Command{ + Use: "list [reference|manifest_digest]", Aliases: []string{"ls"}, - Flags: []cli.Flag{ - flagLocal, - flagUsername, - flagPassword, + Short: "List signatures in cache", + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) > 0 { + opts.reference = args[0] + } + }, + RunE: func(cmd *cobra.Command, args []string) error { + return listCachedSignatures(cmd, opts) }, - ArgsUsage: "[reference|manifest_digest]", - Action: listCachedSignatures, } + opts.ApplyFlag(command.Flags()) + return command +} - cachePruneCommand = &cli.Command{ - Name: "prune", - Usage: "Prune signature from cache", - ArgsUsage: "[reference|manifest_digest] ...", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "all", - Aliases: []string{"a"}, - Usage: "prune all cached signatures", - }, - &cli.BoolFlag{ - Name: "purge", - Usage: "remove the signature directory, combined with --all", - }, - &cli.BoolFlag{ - Name: "force", - Aliases: []string{"f"}, - Usage: "do not prompt for confirmation", - }, - flagLocal, - flagUsername, - flagPassword, +func cachePruneCommand(opts *cachePruneOpts) *cobra.Command { + if opts == nil { + opts = &cachePruneOpts{} + } + command := &cobra.Command{ + Use: "prune [reference|manifest_digest]...", + Short: "Prune signature from cache", + Args: cobra.MinimumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.references = args + }, + RunE: func(cmd *cobra.Command, args []string) error { + return pruneCachedSignatures(cmd, opts) }, - Action: pruneCachedSignatures, } + command.Flags().BoolVarP(&opts.all, "all", "a", false, "prune all cached signatures") + command.Flags().BoolVar(&opts.purge, "purge", false, "remove the signature directory, combined with --all") + command.Flags().BoolVarP(&opts.force, "force", "f", false, "do not prompt for confirmation") + opts.ApplyFlag(command.Flags()) + return command +} - cacheRemoveCommand = &cli.Command{ - Name: "remove", - Usage: "Remove signature from cache", - Aliases: []string{"rm"}, - ArgsUsage: " ...", - Flags: []cli.Flag{ - flagLocal, - flagUsername, - flagPassword, +func cacheRemoveCommand(opts *cacheRemoveOpts) *cobra.Command { + if opts == nil { + opts = &cacheRemoveOpts{} + } + command := &cobra.Command{ + Use: "remove [reference|manifest_digest] [signature_digest]...", + Aliases: []string{"rm"}, + Short: "Remove signature from cache", + Args: cobra.MinimumNArgs(2), + PreRun: func(cmd *cobra.Command, args []string) { + opts.reference = args[0] + opts.sigDigests = args[1:] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return removeCachedSignatures(cmd, opts) }, - Action: removeCachedSignatures, } -) + opts.ApplyFlag(command.Flags()) + return command +} -func listCachedSignatures(ctx *cli.Context) error { - if !ctx.Args().Present() { +func listCachedSignatures(command *cobra.Command, opts *cacheListOpts) error { + if command.Flags().NArg() == 0 { return listManifestsWithCachedSignature() } - manifestDigest, err := getManifestDigestFromContext(ctx, ctx.Args().First()) + manifestDigest, err := getManifestDigestFromContext(command.Context(), &opts.RemoteFlagOpts, opts.reference) if err != nil { return err } @@ -113,12 +141,12 @@ func listManifestsWithCachedSignature() error { }) } -func pruneCachedSignatures(ctx *cli.Context) error { - if ctx.Bool("all") { - if !ctx.Bool("force") { +func pruneCachedSignatures(command *cobra.Command, opts *cachePruneOpts) error { + if opts.all { + if !opts.force { fmt.Println("WARNING! This will remove:") fmt.Println("- all cached signatures") - if ctx.Bool("purge") { + if opts.purge { fmt.Println("- all files in the cache signature directory") } fmt.Println() @@ -144,17 +172,17 @@ func pruneCachedSignatures(ctx *cli.Context) error { ); err != nil { return err } - if ctx.Bool("purge") { + if opts.purge { return os.RemoveAll(config.SignatureStoreDirPath) } return nil } - if !ctx.Args().Present() { + if len(opts.references) == 0 { return errors.New("nothing to prune") } - refs := ctx.Args().Slice() - if !ctx.Bool("force") { + refs := opts.references + if !opts.force { fmt.Println("WARNING! This will remove cached signatures for manifests below:") for _, ref := range refs { fmt.Println("-", ref) @@ -165,7 +193,7 @@ func pruneCachedSignatures(ctx *cli.Context) error { } } for _, ref := range refs { - manifestDigest, err := getManifestDigestFromContext(ctx, ref) + manifestDigest, err := getManifestDigestFromContext(command.Context(), &opts.RemoteFlagOpts, ref) if err != nil { return err } @@ -180,24 +208,15 @@ func pruneCachedSignatures(ctx *cli.Context) error { return nil } -func removeCachedSignatures(ctx *cli.Context) error { +func removeCachedSignatures(command *cobra.Command, opts *cacheRemoveOpts) error { // initialize - sigDigests := ctx.Args().Slice() - if len(sigDigests) == 0 { - return errors.New("missing target manifest") - } - sigDigests = sigDigests[1:] - if len(sigDigests) == 0 { - return errors.New("no signature specified") - } - - manifestDigest, err := getManifestDigestFromContext(ctx, ctx.Args().First()) + manifestDigest, err := getManifestDigestFromContext(command.Context(), &opts.RemoteFlagOpts, opts.reference) if err != nil { return err } // core process - for _, sigDigest := range sigDigests { + for _, sigDigest := range opts.sigDigests { path := config.SignaturePath(manifestDigest, digest.Digest(sigDigest)) if err := os.Remove(path); err != nil { return err @@ -235,7 +254,7 @@ func walkCachedSignatureTree(root string, fn func(algorithm string, encodedEntry return nil } -func getManifestDigestFromContext(ctx *cli.Context, ref string) (manifestDigest digest.Digest, err error) { +func getManifestDigestFromContext(ctx context.Context, opts *RemoteFlagOpts, ref string) (manifestDigest digest.Digest, err error) { manifestDigest, err = digest.Parse(ref) if err == nil { return @@ -250,7 +269,7 @@ func getManifestDigestFromContext(ctx *cli.Context, ref string) (manifestDigest return } - manifest, err := getManifestDescriptorFromContextWithReference(ctx, ref) + manifest, err := getManifestDescriptorFromContextWithReference(ctx, opts, ref) if err != nil { return } diff --git a/cmd/notation/cert.go b/cmd/notation/cert.go index fe92eaa1c..9b6fba934 100644 --- a/cmd/notation/cert.go +++ b/cmd/notation/cert.go @@ -11,86 +11,114 @@ import ( "github.com/notaryproject/notation/internal/ioutil" "github.com/notaryproject/notation/internal/slices" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var ( - certCommand = &cli.Command{ - Name: "certificate", +type certAddOpts struct { + path string + name string +} + +type certRemoveOpts struct { + names []string +} + +type certGenerateTestOpts struct { + name string + bits int + expiry time.Duration + trust bool + hosts []string + isDefault bool +} + +func certCommand() *cobra.Command { + command := &cobra.Command{ + Use: "certificate", Aliases: []string{"cert"}, - Usage: "Manage certificates used for verification", - Subcommands: []*cli.Command{ - certAddCommand, - certListCommand, - certRemoveCommand, - certGenerateTestCommand, - }, + Short: "Manage certificates used for verification", } - certAddCommand = &cli.Command{ - Name: "add", - Usage: "Add certificate to verification list", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Usage: "certificate name", - }, + command.AddCommand(certAddCommand(nil), certListCommand(), certRemoveCommand(nil), certGenerateTestCommand(nil)) + return command +} + +func certAddCommand(opts *certAddOpts) *cobra.Command { + if opts == nil { + opts = &certAddOpts{} + } + command := &cobra.Command{ + Use: "add [path]", + Short: "Add certificate to verification list", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.path = args[0] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return addCert(cmd, opts) }, - Action: addCert, } + command.Flags().StringVarP(&opts.name, "name", "n", "", "certificate name") + return command +} - certListCommand = &cli.Command{ - Name: "list", - Usage: "List certificates used for verification", +func certListCommand() *cobra.Command { + command := &cobra.Command{ + Use: "list", Aliases: []string{"ls"}, - Action: listCerts, - } - - certRemoveCommand = &cli.Command{ - Name: "remove", - Usage: "Remove certificate from the verification list", - Aliases: []string{"rm"}, - ArgsUsage: " ...", - Action: removeCerts, - } - - certGenerateTestCommand = &cli.Command{ - Name: "generate-test", - Usage: "Generates a test RSA key and a corresponding self-signed certificate", - ArgsUsage: " ...", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Usage: "key and certificate name", - }, - &cli.IntFlag{ - Name: "bits", - Usage: "RSA key bits", - Aliases: []string{"b"}, - Value: 2048, - }, - &cli.DurationFlag{ - Name: "expiry", - Aliases: []string{"e"}, - Usage: "certificate expiry", - Value: 365 * 24 * time.Hour, - }, - &cli.BoolFlag{ - Name: "trust", - Usage: "add the generated certificate to the verification list", - }, - keyDefaultFlag, + Short: "List certificates used for verification", + RunE: func(cmd *cobra.Command, args []string) error { + return listCerts(cmd) + }, + } + return command +} +func certRemoveCommand(opts *certRemoveOpts) *cobra.Command { + if opts == nil { + opts = &certRemoveOpts{} + } + command := &cobra.Command{ + Use: "remove [name]...", + Aliases: []string{"rm"}, + Short: "Remove certificate from the verification list", + Args: cobra.MinimumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.names = args + }, + RunE: func(cmd *cobra.Command, args []string) error { + return removeCerts(cmd, opts) + }, + } + return command +} +func certGenerateTestCommand(opts *certGenerateTestOpts) *cobra.Command { + if opts == nil { + opts = &certGenerateTestOpts{} + } + command := &cobra.Command{ + Use: "generate-test [host]...", + Short: "Generates a test RSA key and a corresponding self-signed certificate", + Args: cobra.MinimumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.hosts = args + }, + RunE: func(cmd *cobra.Command, args []string) error { + return generateTestCert(opts) }, - Action: generateTestCert, } -) -func addCert(ctx *cli.Context) error { + command.Flags().StringVarP(&opts.name, "name", "n", "", "key and certificate name") + command.Flags().IntVarP(&opts.bits, "bits", "b", 2048, "RSA key bits") + command.Flags().DurationVarP(&opts.expiry, "expiry", "e", 365*24*time.Hour, "certificate expiry") + command.Flags().BoolVar(&opts.trust, "trust", false, "add the generated certificate to the verification list") + setKeyDefaultFlag(command.Flags(), &opts.isDefault) + return command +} + +func addCert(command *cobra.Command, opts *certAddOpts) error { // initialize - path := ctx.Args().First() + + path := opts.path if path == "" { return errors.New("missing certificate path") } @@ -98,7 +126,7 @@ func addCert(ctx *cli.Context) error { if err != nil { return err } - name := ctx.String("name") + name := opts.name // check if the target path is a cert if _, err := x509.ReadCertificateFile(path); err != nil { @@ -133,7 +161,7 @@ func addCertCore(cfg *config.File, name, path string) error { return nil } -func listCerts(ctx *cli.Context) error { +func listCerts(command *cobra.Command) error { // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -144,9 +172,9 @@ func listCerts(ctx *cli.Context) error { return ioutil.PrintCertificateMap(os.Stdout, cfg.VerificationCertificates.Certificates) } -func removeCerts(ctx *cli.Context) error { +func removeCerts(command *cobra.Command, opts *certRemoveOpts) error { // initialize - names := ctx.Args().Slice() + names := opts.names if len(names) == 0 { return errors.New("missing certificate names") } diff --git a/cmd/notation/cert_gen.go b/cmd/notation/cert_gen.go index d27c434cc..5ad3dfda6 100644 --- a/cmd/notation/cert_gen.go +++ b/cmd/notation/cert_gen.go @@ -15,22 +15,21 @@ import ( "github.com/notaryproject/notation/internal/osutil" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" ) -func generateTestCert(ctx *cli.Context) error { +func generateTestCert(opts *certGenerateTestOpts) error { // initialize - hosts := ctx.Args().Slice() + hosts := opts.hosts if len(hosts) == 0 { return errors.New("missing certificate hosts") } - name := ctx.String("name") + name := opts.name if name == "" { name = hosts[0] } // generate RSA private key - bits := ctx.Int("bits") + bits := opts.bits fmt.Println("generating RSA Key with", bits, "bits") key, keyBytes, err := generateTestKey(bits) if err != nil { @@ -38,7 +37,8 @@ func generateTestCert(ctx *cli.Context) error { } // generate self-signed certificate - cert, certBytes, err := generateTestSelfSignedCert(key, hosts, ctx.Duration("expiry")) + expiry := opts.expiry + cert, certBytes, err := generateTestSelfSignedCert(key, hosts, expiry) if err != nil { return err } @@ -63,7 +63,7 @@ func generateTestCert(ctx *cli.Context) error { if err != nil { return err } - isDefault := ctx.Bool(keyDefaultFlag.Name) + isDefault := opts.isDefault keySuite := config.KeySuite{ Name: name, X509KeyPair: &config.X509KeyPair{ @@ -71,11 +71,11 @@ func generateTestCert(ctx *cli.Context) error { CertificatePath: certPath, }, } - err = addKeyCore(cfg, keySuite, ctx.Bool(keyDefaultFlag.Name)) + err = addKeyCore(cfg, keySuite, isDefault) if err != nil { return err } - trust := ctx.Bool("trust") + trust := opts.trust if trust { if err := addCertCore(cfg, name, certPath); err != nil { return err diff --git a/cmd/notation/common.go b/cmd/notation/common.go index 473295dc6..d197a1f10 100644 --- a/cmd/notation/common.go +++ b/cmd/notation/common.go @@ -1,43 +1,114 @@ package main -import "github.com/urfave/cli/v2" +import ( + "os" + + "github.com/spf13/pflag" +) + +const ( + defaultUsernameEnv = "NOTATION_USERNAME" + defaultPasswordEnv = "NOTATION_PASSWORD" + defaultMediaType = "application/vnd.docker.distribution.manifest.v2+json" +) var ( - flagUsername = &cli.StringFlag{ - Name: "username", - Aliases: []string{"u"}, - Usage: "Username for registry operations", - EnvVars: []string{"NOTATION_USERNAME"}, - } - flagPassword = &cli.StringFlag{ - Name: "password", - Aliases: []string{"p"}, - Usage: "Password for registry operations", - EnvVars: []string{"NOTATION_PASSWORD"}, - } - flagPlainHTTP = &cli.BoolFlag{ - Name: "plain-http", - Usage: "Registry access via plain HTTP", - } - flagMediaType = &cli.StringFlag{ - Name: "media-type", - Usage: "specify the media type of the manifest read from file or stdin", - Value: "application/vnd.docker.distribution.manifest.v2+json", - } - flagOutput = &cli.StringFlag{ - Name: "output", - Aliases: []string{"o"}, - Usage: "write signature to a specific path", - } - flagLocal = &cli.BoolFlag{ - Name: "local", - Aliases: []string{"l"}, - Usage: "reference is a local file", - } - flagSignature = &cli.StringSliceFlag{ + flagUsername = &pflag.Flag{ + Name: "username", + Shorthand: "u", + Usage: "Username for registry operations (default from $NOTATION_USERNAME)", + } + setflagUsername = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, flagUsername.Name, flagUsername.Shorthand, "", flagUsername.Usage) + } + + flagPassword = &pflag.Flag{ + Name: "password", + Shorthand: "p", + Usage: "Password for registry operations (default from $NOTATION_PASSWORD)", + } + setFlagPassword = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, flagPassword.Name, flagPassword.Shorthand, "", flagPassword.Usage) + } + + flagPlainHTTP = &pflag.Flag{ + Name: "plain-http", + Usage: "Registry access via plain HTTP", + DefValue: "false", + } + setFlagPlainHTTP = func(fs *pflag.FlagSet, p *bool) { + fs.BoolVar(p, flagPlainHTTP.Name, false, flagPlainHTTP.Usage) + } + + flagMediaType = &pflag.Flag{ + Name: "media-type", + Usage: "specify the media type of the manifest read from file or stdin", + DefValue: defaultMediaType, + } + setFlagMediaType = func(fs *pflag.FlagSet, p *string) { + fs.StringVar(p, flagMediaType.Name, defaultMediaType, flagMediaType.Usage) + } + + flagOutput = &pflag.Flag{ + Name: "output", + Shorthand: "o", + Usage: "write signature to a specific path", + } + setFlagOutput = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, flagOutput.Name, flagOutput.Shorthand, "", flagOutput.Usage) + } + + flagLocal = &pflag.Flag{ + Name: "local", + Shorthand: "l", + Usage: "reference is a local file", + DefValue: "false", + } + setFlagLocal = func(fs *pflag.FlagSet, p *bool) { + fs.BoolVarP(p, flagLocal.Name, flagLocal.Shorthand, false, flagLocal.Usage) + } + + flagSignature = &pflag.Flag{ Name: "signature", - Aliases: []string{"s", "f"}, + Shorthand: "s", Usage: "signature files", - TakesFile: true, + } + setFlagSignature = func(fs *pflag.FlagSet, p *[]string) { + fs.StringSliceVarP(p, flagSignature.Name, flagSignature.Shorthand, []string{}, flagSignature.Usage) } ) + +type SecureFlagOpts struct { + Username string + Password string + PlainHTTP bool +} + +func (opts *SecureFlagOpts) ApplyFlag(fs *pflag.FlagSet) { + setflagUsername(fs, &opts.Username) + setFlagPassword(fs, &opts.Password) + + setFlagPlainHTTP(fs, &opts.PlainHTTP) + opts.Username = os.Getenv(defaultUsernameEnv) + opts.Password = os.Getenv(defaultPasswordEnv) +} + +type CommanFlagOpts struct { + Local bool + MediaType string +} + +func (opts *CommanFlagOpts) ApplyFlag(fs *pflag.FlagSet) { + setFlagMediaType(fs, &opts.MediaType) + setFlagLocal(fs, &opts.Local) +} + +type RemoteFlagOpts struct { + SecureFlagOpts + CommanFlagOpts +} + +func (opts *RemoteFlagOpts) ApplyFlag(fs *pflag.FlagSet) { + opts.SecureFlagOpts.ApplyFlag(fs) + opts.CommanFlagOpts.ApplyFlag(fs) +} diff --git a/cmd/notation/key.go b/cmd/notation/key.go index dfcdd548f..d11a9f3de 100644 --- a/cmd/notation/key.go +++ b/cmd/notation/key.go @@ -1,6 +1,7 @@ package main import ( + "context" "crypto/tls" "errors" "fmt" @@ -12,98 +13,146 @@ import ( "github.com/notaryproject/notation/internal/ioutil" "github.com/notaryproject/notation/internal/slices" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( - keyCommand = &cli.Command{ - Name: "key", - Usage: "Manage keys used for signing", - Subcommands: []*cli.Command{ - keyAddCommand, - keyUpdateCommand, - keyListCommand, - keyRemoveCommand, - }, + keyDefaultFlag = &pflag.Flag{ + Name: "default", + Shorthand: "d", + Usage: "mark as default", + } + setKeyDefaultFlag = func(fs *pflag.FlagSet, p *bool) { + fs.BoolVarP(p, keyDefaultFlag.Name, keyDefaultFlag.Shorthand, false, keyDefaultFlag.Usage) } +) + +type keyAddOpts struct { + name string + plugin string + id string + pluginConfig string + isDefault bool + keyPath string + certPath string +} + +type keyUpdateOpts struct { + name string + isDefault bool +} - keyDefaultFlag = &cli.BoolFlag{ - Name: "default", - Aliases: []string{"d"}, - Usage: "mark as default", - } - - keyAddCommand = &cli.Command{ - Name: "add", - Usage: "Add key to signing key list", - ArgsUsage: "[ ]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "name", - Aliases: []string{"n"}, - Usage: "key name", - Required: true, - }, - &cli.StringFlag{ - Name: "plugin", - Aliases: []string{"p"}, - Usage: "signing plugin name", - }, - &cli.StringFlag{ - Name: "id", - Usage: "key id (required if --plugin is set)", - }, - cmd.FlagPluginConfig, - keyDefaultFlag, +type keyRemoveOpts struct { + names []string +} + +func keyCommand() *cobra.Command { + command := &cobra.Command{ + Use: "key", + Short: "Manage keys used for signing", + } + command.AddCommand(keyAddCommand(nil), keyUpdateCommand(nil), keyListCommand(), keyRemoveCommand(nil)) + return command +} + +func keyAddCommand(opts *keyAddOpts) *cobra.Command { + if opts == nil { + opts = &keyAddOpts{} + } + command := &cobra.Command{ + Use: "add [key_path cert_path]", + Short: "Add key to signing key list", + Args: cobra.MaximumNArgs(2), + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) >= 2 { + opts.keyPath = args[0] + opts.certPath = args[1] + } + }, + RunE: func(cmd *cobra.Command, args []string) error { + return addKey(cmd, opts) }, - Action: addKey, } + command.Flags().StringVarP(&opts.name, "name", "n", "", "key name") + command.MarkFlagRequired("name") - keyUpdateCommand = &cli.Command{ - Name: "update", - Usage: "Update key in signing key list", - Aliases: []string{"set"}, - ArgsUsage: "", - Flags: []cli.Flag{ - keyDefaultFlag, + command.Flags().StringVarP(&opts.plugin, "plugin", "p", "", "signing plugin name") + command.Flags().StringVar(&opts.id, "id", "", "key id (required if --plugin is set)") + + cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) + setKeyDefaultFlag(command.Flags(), &opts.isDefault) + return command +} + +func keyUpdateCommand(opts *keyUpdateOpts) *cobra.Command { + if opts == nil { + opts = &keyUpdateOpts{} + } + command := &cobra.Command{ + Use: "update [name]", + Aliases: []string{"set"}, + Short: "Update key in signing key list", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.name = args[0] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return updateKey(cmd, opts) }, - Action: updateKey, } - keyListCommand = &cli.Command{ - Name: "list", - Usage: "List keys used for signing", + setKeyDefaultFlag(command.Flags(), &opts.isDefault) + return command +} + +func keyListCommand() *cobra.Command { + return &cobra.Command{ + Use: "list", Aliases: []string{"ls"}, - Action: listKeys, + Short: "List keys used for signing", + RunE: func(cmd *cobra.Command, args []string) error { + return listKeys(cmd) + }, } +} - keyRemoveCommand = &cli.Command{ - Name: "remove", - Usage: "Remove key from signing key list", - Aliases: []string{"rm"}, - ArgsUsage: "[name] ...", - Action: removeKeys, +func keyRemoveCommand(opts *keyRemoveOpts) *cobra.Command { + if opts == nil { + opts = &keyRemoveOpts{} + } + return &cobra.Command{ + Use: "remove [name]...", + Aliases: []string{"rm"}, + Short: "Remove key from signing key list", + Args: cobra.MinimumNArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.names = args + }, + RunE: func(cmd *cobra.Command, args []string) error { + return removeKeys(cmd, opts) + }, } -) +} -func addKey(ctx *cli.Context) error { +func addKey(command *cobra.Command, opts *keyAddOpts) error { cfg, err := config.LoadOrDefault() if err != nil { return err } var key config.KeySuite - pluginName := ctx.String("plugin") - name := ctx.String("name") + pluginName := opts.plugin + name := opts.name if pluginName != "" { - key, err = addExternalKey(ctx, pluginName, name) + key, err = addExternalKey(command.Context(), opts, pluginName, name) } else { - key, err = newX509KeyPair(ctx, name) + key, err = newX509KeyPair(opts, name) } if err != nil { return err } - isDefault := ctx.Bool(keyDefaultFlag.Name) + isDefault := opts.isDefault err = addKeyCore(cfg, key, isDefault) if err != nil { return err @@ -122,20 +171,20 @@ func addKey(ctx *cli.Context) error { return nil } -func addExternalKey(ctx *cli.Context, pluginName, keyName string) (config.KeySuite, error) { - id := ctx.String("id") +func addExternalKey(ctx context.Context, opts *keyAddOpts, pluginName, keyName string) (config.KeySuite, error) { + id := opts.id if id == "" { return config.KeySuite{}, errors.New("missing key id") } mgr := manager.New(config.PluginDirPath) - p, err := mgr.Get(ctx.Context, pluginName) + p, err := mgr.Get(ctx, pluginName) if err != nil { return config.KeySuite{}, err } if p.Err != nil { return config.KeySuite{}, fmt.Errorf("invalid plugin: %w", p.Err) } - pluginConfig, err := cmd.ParseFlagPluginConfig(ctx) + pluginConfig, err := cmd.ParseFlagPluginConfig(opts.pluginConfig) if err != nil { return config.KeySuite{}, err } @@ -149,20 +198,19 @@ func addExternalKey(ctx *cli.Context, pluginName, keyName string) (config.KeySui }, nil } -func newX509KeyPair(ctx *cli.Context, keyName string) (config.KeySuite, error) { - args := ctx.Args() - switch args.Len() { - case 0: +func newX509KeyPair(opts *keyAddOpts, keyName string) (config.KeySuite, error) { + if opts.keyPath == "" { return config.KeySuite{}, errors.New("missing key and certificate paths") - case 1: + } + if opts.certPath == "" { return config.KeySuite{}, errors.New("missing certificate path for the corresponding key") } - keyPath, err := filepath.Abs(args.Get(0)) + keyPath, err := filepath.Abs(opts.keyPath) if err != nil { return config.KeySuite{}, err } - certPath, err := filepath.Abs(args.Get(1)) + certPath, err := filepath.Abs(opts.certPath) if err != nil { return config.KeySuite{}, err } @@ -188,9 +236,9 @@ func addKeyCore(cfg *config.File, key config.KeySuite, markDefault bool) error { return nil } -func updateKey(ctx *cli.Context) error { +func updateKey(command *cobra.Command, opts *keyUpdateOpts) error { // initialize - name := ctx.Args().First() + name := opts.name if name == "" { return errors.New("missing key name") } @@ -203,7 +251,7 @@ func updateKey(ctx *cli.Context) error { if !slices.Contains(cfg.SigningKeys.Keys, name) { return errors.New(name + ": not found") } - if !ctx.Bool(keyDefaultFlag.Name) { + if !opts.isDefault { return nil } if cfg.SigningKeys.Default != name { @@ -218,7 +266,7 @@ func updateKey(ctx *cli.Context) error { return nil } -func listKeys(ctx *cli.Context) error { +func listKeys(command *cobra.Command) error { // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -229,9 +277,9 @@ func listKeys(ctx *cli.Context) error { return ioutil.PrintKeyMap(os.Stdout, cfg.SigningKeys.Default, cfg.SigningKeys.Keys) } -func removeKeys(ctx *cli.Context) error { +func removeKeys(command *cobra.Command, opts *keyRemoveOpts) error { // initialize - names := ctx.Args().Slice() + names := opts.names if len(names) == 0 { return errors.New("missing key names") } diff --git a/cmd/notation/list.go b/cmd/notation/list.go index 05400e1c6..b27ec3e03 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -1,43 +1,51 @@ package main import ( - "errors" "fmt" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var listCommand = &cli.Command{ - Name: "list", - Usage: "List signatures from remote", - Aliases: []string{"ls"}, - ArgsUsage: "", - Flags: []cli.Flag{ - flagUsername, - flagPassword, - }, - Action: runList, +type listOpts struct { + SecureFlagOpts + reference string } -func runList(ctx *cli.Context) error { - // initialize - if !ctx.Args().Present() { - return errors.New("no reference specified") +func listCommand(opts *listOpts) *cobra.Command { + if opts == nil { + opts = &listOpts{} + } + cmd := &cobra.Command{ + Use: "list [reference]", + Aliases: []string{"ls"}, + Short: "List signatures from remote", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.reference = args[0] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runList(cmd, opts) + }, } + opts.ApplyFlag(cmd.Flags()) + return cmd +} - reference := ctx.Args().First() - sigRepo, err := getSignatureRepository(ctx, reference) +func runList(command *cobra.Command, opts *listOpts) error { + // initialize + reference := opts.reference + sigRepo, err := getSignatureRepository(&opts.SecureFlagOpts, reference) if err != nil { return err } // core process - manifestDesc, err := getManifestDescriptorFromReference(ctx, reference) + manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, reference) if err != nil { return err } - sigManifests, err := sigRepo.ListSignatureManifests(ctx.Context, manifestDesc.Digest) + sigManifests, err := sigRepo.ListSignatureManifests(command.Context(), manifestDesc.Digest) if err != nil { return fmt.Errorf("lookup signature failure: %v", err) } diff --git a/cmd/notation/login.go b/cmd/notation/login.go index 3d1329817..1316f32ce 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "io" @@ -8,41 +9,55 @@ import ( "strings" "github.com/notaryproject/notation/pkg/auth" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" orasauth "oras.land/oras-go/v2/registry/remote/auth" ) -var loginCommand = &cli.Command{ - Name: "login", - Usage: "Provides credentials for authenticated registry operations", - UsageText: `notation login [options] [server] - -Example - Login with provided username and password: - notation login -u -p registry.example.com +type loginOpts struct { + SecureFlagOpts + passwordStdin bool + server string +} -Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: - notation login registry.example.com`, - ArgsUsage: "[server]", - Flags: []cli.Flag{ - flagUsername, - flagPassword, - &cli.BoolFlag{ - Name: "password-stdin", - Usage: "Take the password from stdin", +func loginCommand(opts *loginOpts) *cobra.Command { + if opts == nil { + opts = &loginOpts{} + } + command := &cobra.Command{ + Use: "login [options] [server]", + Short: "Provides credentials for authenticated registry operations", + Long: `notation login [options] [server] + + Example - Login with provided username and password: + notation login -u -p registry.example.com + + Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: + notation login registry.example.com`, + Args: cobra.ExactArgs(1), + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := readPassword(opts); err != nil { + return err + } + opts.server = args[0] + return nil + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runLogin(cmd, opts) }, - }, - Before: readPassword, - Action: runLogin, + } + command.Flags().BoolVar(&opts.passwordStdin, "passowrd-stdin", false, "Take the password from stdin") + opts.ApplyFlag(command.Flags()) + return command } -func runLogin(ctx *cli.Context) error { +func runLogin(cmd *cobra.Command, opts *loginOpts) error { // initialize - if !ctx.Args().Present() { + if opts.server == "" { return errors.New("no hostname specified") } - serverAddress := ctx.Args().First() + serverAddress := opts.server - if err := validateAuthConfig(ctx, serverAddress); err != nil { + if err := validateAuthConfig(cmd.Context(), opts, serverAddress); err != nil { return err } @@ -52,8 +67,8 @@ func runLogin(ctx *cli.Context) error { } // init creds creds := newCredentialFromInput( - ctx.String(flagUsername.Name), - ctx.String(flagPassword.Name), + opts.Username, + opts.Password, ) if err = nativeStore.Store(serverAddress, creds); err != nil { return fmt.Errorf("failed to store credentials: %v", err) @@ -61,12 +76,12 @@ func runLogin(ctx *cli.Context) error { return nil } -func validateAuthConfig(ctx *cli.Context, serverAddress string) error { - registry, err := getRegistryClient(ctx, serverAddress) +func validateAuthConfig(ctx context.Context, opts *loginOpts, serverAddress string) error { + registry, err := getRegistryClient(&opts.SecureFlagOpts, serverAddress) if err != nil { return err } - return registry.Ping(ctx.Context) + return registry.Ping(ctx) } func newCredentialFromInput(username, password string) orasauth.Credential { @@ -80,13 +95,13 @@ func newCredentialFromInput(username, password string) orasauth.Credential { return c } -func readPassword(ctx *cli.Context) error { - if ctx.Bool("password-stdin") { +func readPassword(opts *loginOpts) error { + if opts.passwordStdin { password, err := readLine() if err != nil { return err } - ctx.Set(flagPassword.Name, password) + opts.Password = password } return nil } diff --git a/cmd/notation/logout.go b/cmd/notation/logout.go index 5eb707210..008ac2e03 100644 --- a/cmd/notation/logout.go +++ b/cmd/notation/logout.go @@ -4,22 +4,36 @@ import ( "errors" "github.com/notaryproject/notation/pkg/auth" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var logoutCommand = &cli.Command{ - Name: "logout", - Usage: "Log out the specified registry hostname", - ArgsUsage: "[server]", - Action: runLogout, +type logoutOpts struct { + server string } -func runLogout(ctx *cli.Context) error { +func logoutCommand(opts *logoutOpts) *cobra.Command { + if opts == nil { + opts = &logoutOpts{} + } + return &cobra.Command{ + Use: "logout [server]", + Short: "Log out the specified registry hostname", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.server = args[0] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runLogout(cmd, opts) + }, + } +} + +func runLogout(cmd *cobra.Command, opts *logoutOpts) error { // initialize - if !ctx.Args().Present() { + if opts.server == "" { return errors.New("no hostname specified") } - serverAddress := ctx.Args().First() + serverAddress := opts.server nativeStore, err := auth.GetCredentialsStore(serverAddress) if err != nil { return err diff --git a/cmd/notation/main.go b/cmd/notation/main.go index fd2bea3a4..bddc03568 100644 --- a/cmd/notation/main.go +++ b/cmd/notation/main.go @@ -2,40 +2,32 @@ package main import ( "log" - "os" "github.com/notaryproject/notation/internal/version" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) func main() { - app := &cli.App{ - Name: "notation", - Usage: "Notation - Notary V2", - Version: version.GetVersion(), - Authors: []*cli.Author{ - { - Name: "CNCF Notary Project", - }, - }, - Flags: []cli.Flag{ - flagPlainHTTP, - }, - Commands: []*cli.Command{ - signCommand, - verifyCommand, - pushCommand, - pullCommand, - listCommand, - certCommand, - keyCommand, - cacheCommand, - pluginCommand, - loginCommand, - logoutCommand, - }, + cmd := &cobra.Command{ + Use: "notation", + Short: "Notation - Notary V2", + Version: version.GetVersion(), + SilenceUsage: true, } - if err := app.Run(os.Args); err != nil { + cmd.AddCommand( + signCommand(nil), + verifyCommand(nil), + pushCommand(nil), + pullCommand(nil), + listCommand(nil), + certCommand(), + keyCommand(), + cacheCommand(), + pluginCommand(), + loginCommand(nil), + logoutCommand(nil)) + cmd.PersistentFlags().Bool(flagPlainHTTP.Name, false, flagPlainHTTP.Usage) + if err := cmd.Execute(); err != nil { log.Fatal(err) } } diff --git a/cmd/notation/manifest.go b/cmd/notation/manifest.go index cce70cb87..f701b2b8d 100644 --- a/cmd/notation/manifest.go +++ b/cmd/notation/manifest.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "io" "math" @@ -8,40 +9,38 @@ import ( "github.com/notaryproject/notation-go" "github.com/opencontainers/go-digest" - "github.com/urfave/cli/v2" "oras.land/oras-go/v2/registry" ) -func getManifestDescriptorFromContext(ctx *cli.Context) (notation.Descriptor, error) { - ref := ctx.Args().First() +func getManifestDescriptorFromContext(ctx context.Context, opts *RemoteFlagOpts, ref string) (notation.Descriptor, error) { if ref == "" { return notation.Descriptor{}, errors.New("missing reference") } - return getManifestDescriptorFromContextWithReference(ctx, ref) + return getManifestDescriptorFromContextWithReference(ctx, opts, ref) } -func getManifestDescriptorFromContextWithReference(ctx *cli.Context, ref string) (notation.Descriptor, error) { - if ctx.Bool(flagLocal.Name) { - mediaType := ctx.String(flagMediaType.Name) +func getManifestDescriptorFromContextWithReference(ctx context.Context, opts *RemoteFlagOpts, ref string) (notation.Descriptor, error) { + if opts.Local { + mediaType := opts.MediaType if ref == "-" { return getManifestDescriptorFromReader(os.Stdin, mediaType) } return getManifestDescriptorFromFile(ref, mediaType) } - return getManifestDescriptorFromReference(ctx, ref) + return getManifestDescriptorFromReference(ctx, &opts.SecureFlagOpts, ref) } -func getManifestDescriptorFromReference(ctx *cli.Context, reference string) (notation.Descriptor, error) { +func getManifestDescriptorFromReference(ctx context.Context, opts *SecureFlagOpts, reference string) (notation.Descriptor, error) { ref, err := registry.ParseReference(reference) if err != nil { return notation.Descriptor{}, err } - repo, err := getRepositoryClient(ctx, ref) + repo, err := getRepositoryClient(opts, ref) if err != nil { return notation.Descriptor{}, err } - return repo.Resolve(ctx.Context, ref.ReferenceOrDefault()) + return repo.Resolve(ctx, ref.ReferenceOrDefault()) } func getManifestDescriptorFromFile(path, mediaType string) (notation.Descriptor, error) { diff --git a/cmd/notation/plugin.go b/cmd/notation/plugin.go index 47153ab74..911510101 100644 --- a/cmd/notation/plugin.go +++ b/cmd/notation/plugin.go @@ -6,29 +6,32 @@ import ( "github.com/notaryproject/notation-go/plugin/manager" "github.com/notaryproject/notation/internal/ioutil" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var ( - pluginCommand = &cli.Command{ - Name: "plugin", - Usage: "Manage plugins", - Subcommands: []*cli.Command{ - pluginListCommand, - }, +func pluginCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "plugin", + Short: "Manage plugins", } + cmd.AddCommand(pluginListCommand()) + return cmd +} - pluginListCommand = &cli.Command{ - Name: "list", - Usage: "List registered plugins", +func pluginListCommand() *cobra.Command { + return &cobra.Command{ + Use: "list", Aliases: []string{"ls"}, - Action: listPlugins, + Short: "List registered plugins", + RunE: func(cmd *cobra.Command, args []string) error { + return listPlugins(cmd) + }, } -) +} -func listPlugins(ctx *cli.Context) error { +func listPlugins(command *cobra.Command) error { mgr := manager.New(config.PluginDirPath) - plugins, err := mgr.List(ctx.Context) + plugins, err := mgr.List(command.Context()) if err != nil { return err } diff --git a/cmd/notation/pull.go b/cmd/notation/pull.go index 3d4051dcc..bf1e194c9 100644 --- a/cmd/notation/pull.go +++ b/cmd/notation/pull.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "path/filepath" @@ -10,66 +11,78 @@ import ( "github.com/notaryproject/notation/pkg/cache" "github.com/notaryproject/notation/pkg/config" "github.com/opencontainers/go-digest" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" "oras.land/oras-go/v2/registry" ) -var pullCommand = &cli.Command{ - Name: "pull", - Usage: "Pull signatures from remote", - ArgsUsage: "", - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "strict", - Usage: "pull the signature without lookup the manifest", +type pullOpts struct { + SecureFlagOpts + strict bool + reference string + output string +} + +func pullCommand(opts *pullOpts) *cobra.Command { + if opts == nil { + opts = &pullOpts{} + } + cmd := &cobra.Command{ + Use: "pull [reference]", + Short: "Pull signatures from remote", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.reference = args[0] }, - flagOutput, - flagUsername, - flagPassword, - }, - Action: runPull, + RunE: func(cmd *cobra.Command, args []string) error { + return runPull(cmd, opts) + }, + } + cmd.Flags().BoolVar(&opts.strict, "strict", false, "pull the signature without lookup the manifest") + setFlagOutput(cmd.Flags(), &opts.output) + opts.ApplyFlag(cmd.Flags()) + return cmd } -func runPull(ctx *cli.Context) error { +func runPull(command *cobra.Command, opts *pullOpts) error { // initialize - if !ctx.Args().Present() { + if opts.reference == "" { return errors.New("no reference specified") } - reference := ctx.Args().First() - sigRepo, err := getSignatureRepository(ctx, reference) + reference := opts.reference + sigRepo, err := getSignatureRepository(&opts.SecureFlagOpts, reference) if err != nil { return err } // core process - if ctx.Bool("strict") { - return pullSignatureStrict(ctx, sigRepo, reference) + if opts.strict { + return pullSignatureStrict(command.Context(), opts, sigRepo, reference) } - manifestDesc, err := getManifestDescriptorFromReference(ctx, reference) + manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, reference) if err != nil { return err } - sigManifests, err := sigRepo.ListSignatureManifests(ctx.Context, manifestDesc.Digest) + sigManifests, err := sigRepo.ListSignatureManifests(command.Context(), manifestDesc.Digest) if err != nil { return fmt.Errorf("list signature manifests failure: %v", err) } - path := ctx.String(flagOutput.Name) + path := opts.output for _, sigManifest := range sigManifests { sigDigest := sigManifest.Blob.Digest if path != "" { outputPath := filepath.Join(path, sigDigest.Encoded()+config.SignatureExtension) - sig, err := sigRepo.Get(ctx.Context, sigDigest) + sig, err := sigRepo.Get(command.Context(), sigDigest) if err != nil { return fmt.Errorf("get signature failure: %v: %v", sigDigest, err) } if err := osutil.WriteFile(outputPath, sig); err != nil { return fmt.Errorf("fail to write signature: %v: %v", sigDigest, err) } - } else if err := cache.PullSignature(ctx.Context, sigRepo, manifestDesc.Digest, sigDigest); err != nil { + } else if err := cache.PullSignature(command.Context(), sigRepo, manifestDesc.Digest, sigDigest); err != nil { return err } @@ -80,7 +93,7 @@ func runPull(ctx *cli.Context) error { return nil } -func pullSignatureStrict(ctx *cli.Context, sigRepo notationregistry.SignatureRepository, reference string) error { +func pullSignatureStrict(ctx context.Context, opts *pullOpts, sigRepo notationregistry.SignatureRepository, reference string) error { ref, err := registry.ParseReference(reference) if err != nil { return err @@ -90,11 +103,11 @@ func pullSignatureStrict(ctx *cli.Context, sigRepo notationregistry.SignatureRep return fmt.Errorf("invalid signature digest: %v", err) } - sig, err := sigRepo.Get(ctx.Context, sigDigest) + sig, err := sigRepo.Get(ctx, sigDigest) if err != nil { return fmt.Errorf("get signature failure: %v: %v", sigDigest, err) } - outputPath := ctx.String(flagOutput.Name) + outputPath := opts.output if outputPath == "" { outputPath = sigDigest.Encoded() + config.SignatureExtension } @@ -107,19 +120,18 @@ func pullSignatureStrict(ctx *cli.Context, sigRepo notationregistry.SignatureRep return nil } -func pullSignatures(ctx *cli.Context, manifestDigest digest.Digest) error { - reference := ctx.Args().First() - sigRepo, err := getSignatureRepository(ctx, reference) +func pullSignatures(command *cobra.Command, reference string, opts *SecureFlagOpts, manifestDigest digest.Digest) error { + sigRepo, err := getSignatureRepository(opts, reference) if err != nil { return err } - sigManifests, err := sigRepo.ListSignatureManifests(ctx.Context, manifestDigest) + sigManifests, err := sigRepo.ListSignatureManifests(command.Context(), manifestDigest) if err != nil { return fmt.Errorf("lookup signature failure: %v", err) } for _, sigManifest := range sigManifests { - if err := cache.PullSignature(ctx.Context, sigRepo, manifestDigest, sigManifest.Blob.Digest); err != nil { + if err := cache.PullSignature(command.Context(), sigRepo, manifestDigest, sigManifest.Blob.Digest); err != nil { return err } } diff --git a/cmd/notation/push.go b/cmd/notation/push.go index 1d5eab871..eb53344dd 100644 --- a/cmd/notation/push.go +++ b/cmd/notation/push.go @@ -1,6 +1,7 @@ package main import ( + "context" "errors" "fmt" "os" @@ -8,32 +9,46 @@ import ( "github.com/notaryproject/notation-go" "github.com/notaryproject/notation/pkg/cache" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var pushCommand = &cli.Command{ - Name: "push", - Usage: "Push signature to remote", - ArgsUsage: "", - Flags: []cli.Flag{ - flagSignature, - flagUsername, - flagPassword, - }, - Action: runPush, +type pushOpts struct { + SecureFlagOpts + reference string + signatures []string } -func runPush(ctx *cli.Context) error { +func pushCommand(opts *pushOpts) *cobra.Command { + if opts == nil { + opts = &pushOpts{} + } + cmd := &cobra.Command{ + Use: "push [reference]", + Short: "Push signature to remote", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.reference = args[0] + }, + RunE: func(cmd *cobra.Command, args []string) error { + return runPush(cmd, opts) + }, + } + setFlagSignature(cmd.Flags(), &opts.signatures) + opts.ApplyFlag(cmd.Flags()) + return cmd +} + +func runPush(command *cobra.Command, opts *pushOpts) error { // initialize - if !ctx.Args().Present() { + if opts.reference == "" { return errors.New("no reference specified") } - ref := ctx.Args().First() - manifestDesc, err := getManifestDescriptorFromReference(ctx, ref) + ref := opts.reference + manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, ref) if err != nil { return err } - sigPaths := ctx.StringSlice(flagSignature.Name) + sigPaths := opts.signatures if len(sigPaths) == 0 { sigDigests, err := cache.SignatureDigests(manifestDesc.Digest) if err != nil { @@ -45,7 +60,7 @@ func runPush(ctx *cli.Context) error { } // core process - sigRepo, err := getSignatureRepository(ctx, ref) + sigRepo, err := getSignatureRepository(&opts.SecureFlagOpts, ref) if err != nil { return err } @@ -55,7 +70,7 @@ func runPush(ctx *cli.Context) error { return err } // pass in nonempty annotations if needed - sigDesc, _, err := sigRepo.PutSignatureManifest(ctx.Context, sig, manifestDesc, make(map[string]string)) + sigDesc, _, err := sigRepo.PutSignatureManifest(command.Context(), sig, manifestDesc, make(map[string]string)) if err != nil { return fmt.Errorf("put signature manifest failure: %v", err) } @@ -67,20 +82,20 @@ func runPush(ctx *cli.Context) error { return nil } -func pushSignature(ctx *cli.Context, ref string, sig []byte) (notation.Descriptor, error) { +func pushSignature(ctx context.Context, opts *SecureFlagOpts, ref string, sig []byte) (notation.Descriptor, error) { // initialize - sigRepo, err := getSignatureRepository(ctx, ref) + sigRepo, err := getSignatureRepository(opts, ref) if err != nil { return notation.Descriptor{}, err } - manifestDesc, err := getManifestDescriptorFromReference(ctx, ref) + manifestDesc, err := getManifestDescriptorFromReference(ctx, opts, ref) if err != nil { return notation.Descriptor{}, err } // core process // pass in nonempty annotations if needed - sigDesc, _, err := sigRepo.PutSignatureManifest(ctx.Context, sig, manifestDesc, make(map[string]string)) + sigDesc, _, err := sigRepo.PutSignatureManifest(ctx, sig, manifestDesc, make(map[string]string)) if err != nil { return notation.Descriptor{}, fmt.Errorf("put signature manifest failure: %v", err) } diff --git a/cmd/notation/registry.go b/cmd/notation/registry.go index 551398add..61e0c53ec 100644 --- a/cmd/notation/registry.go +++ b/cmd/notation/registry.go @@ -8,45 +8,45 @@ import ( "github.com/notaryproject/notation/internal/version" loginauth "github.com/notaryproject/notation/pkg/auth" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" "oras.land/oras-go/v2/registry" "oras.land/oras-go/v2/registry/remote" "oras.land/oras-go/v2/registry/remote/auth" ) -func getSignatureRepository(ctx *cli.Context, reference string) (notationregistry.SignatureRepository, error) { +func getSignatureRepository(opts *SecureFlagOpts, reference string) (notationregistry.SignatureRepository, error) { ref, err := registry.ParseReference(reference) if err != nil { return nil, err } - return getRepositoryClient(ctx, ref) + return getRepositoryClient(opts, ref) } -func getRegistryClient(ctx *cli.Context, serverAddress string) (*remote.Registry, error) { +func getRegistryClient(opts *SecureFlagOpts, serverAddress string) (*remote.Registry, error) { reg, err := remote.NewRegistry(serverAddress) if err != nil { return nil, err } - reg.Client, reg.PlainHTTP, err = getAuthClient(ctx, reg.Reference) + reg.Client, reg.PlainHTTP, err = getAuthClient(opts, reg.Reference) if err != nil { return nil, err } return reg, nil } -func getRepositoryClient(ctx *cli.Context, ref registry.Reference) (*notationregistry.RepositoryClient, error) { - authClient, plainHTTP, err := getAuthClient(ctx, ref) +func getRepositoryClient(opts *SecureFlagOpts, ref registry.Reference) (*notationregistry.RepositoryClient, error) { + authClient, plainHTTP, err := getAuthClient(opts, ref) if err != nil { return nil, err } return notationregistry.NewRepositoryClient(authClient, ref, plainHTTP), nil } -func getAuthClient(ctx *cli.Context, ref registry.Reference) (*auth.Client, bool, error) { +func getAuthClient(opts *SecureFlagOpts, ref registry.Reference) (*auth.Client, bool, error) { var plainHTTP bool - if ctx.IsSet(flagPlainHTTP.Name) { - plainHTTP = ctx.Bool(flagPlainHTTP.Name) + + if opts.PlainHTTP { + plainHTTP = opts.PlainHTTP } else { plainHTTP = config.IsRegistryInsecure(ref.Registry) if !plainHTTP { @@ -55,10 +55,9 @@ func getAuthClient(ctx *cli.Context, ref registry.Reference) (*auth.Client, bool } } } - cred := auth.Credential{ - Username: ctx.String(flagUsername.Name), - Password: ctx.String(flagPassword.Name), + Username: opts.Username, + Password: opts.Password, } if cred.Username == "" { cred = auth.Credential{ diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 47104f218..ef7666f93 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -1,7 +1,9 @@ package main import ( + "context" "fmt" + "time" "github.com/notaryproject/notation-go" "github.com/notaryproject/notation-go/crypto/timestamp" @@ -9,58 +11,71 @@ import ( "github.com/notaryproject/notation/internal/osutil" "github.com/notaryproject/notation/pkg/config" "github.com/opencontainers/go-digest" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var signCommand = &cli.Command{ - Name: "sign", - Usage: "Signs artifacts", - ArgsUsage: "", - Flags: []cli.Flag{ - cmd.FlagKey, - cmd.FlagKeyFile, - cmd.FlagCertFile, - cmd.FlagTimestamp, - cmd.FlagExpiry, - cmd.FlagReference, - flagLocal, - flagOutput, - &cli.BoolFlag{ - Name: "push", - Usage: "push after successful signing", - Value: true, +type signOpts struct { + cmd.SignerFlagOpts + RemoteFlagOpts + timestamp string + expiry time.Duration + originReference string + output string + push bool + pushReference string + pluginConfig string + reference string +} + +func signCommand(opts *signOpts) *cobra.Command { + if opts == nil { + opts = &signOpts{} + } + command := &cobra.Command{ + Use: "sign [reference]", + Short: "Signs artifacts", + Args: cobra.ExactArgs(1), + PreRun: func(cmd *cobra.Command, args []string) { + opts.reference = args[0] }, - &cli.StringFlag{ - Name: "push-reference", - Usage: "different remote to store signature", + RunE: func(cmd *cobra.Command, args []string) error { + return runSign(cmd, opts) }, - flagUsername, - flagPassword, - flagMediaType, - cmd.FlagPluginConfig, - }, - Action: runSign, + } + opts.SignerFlagOpts.ApplyFlag(command.Flags()) + opts.RemoteFlagOpts.ApplyFlag(command.Flags()) + + cmd.SetPflagTimestamp(command.Flags(), &opts.timestamp) + cmd.SetPflagExpiry(command.Flags(), &opts.expiry) + cmd.SetPflagReference(command.Flags(), &opts.originReference) + setFlagOutput(command.Flags(), &opts.output) + + command.Flags().BoolVar(&opts.push, "push", true, "push after successful signing") + command.Flags().StringVar(&opts.pushReference, "push-reference", "", "different remote to store signature") + + cmd.SetPflagPluginConfig(command.Flags(), &opts.pluginConfig) + return command } -func runSign(ctx *cli.Context) error { +func runSign(command *cobra.Command, cmdOpts *signOpts) error { // initialize - signer, err := cmd.GetSigner(ctx) + signer, err := cmd.GetSigner(&cmdOpts.SignerFlagOpts) if err != nil { return err } // core process - desc, opts, err := prepareSigningContent(ctx) + desc, opts, err := prepareSigningContent(command.Context(), cmdOpts) if err != nil { return err } - sig, err := signer.Sign(ctx.Context, desc, opts) + sig, err := signer.Sign(command.Context(), desc, opts) if err != nil { return err } // write out - path := ctx.String(flagOutput.Name) + path := cmdOpts.output if path == "" { path = config.SignaturePath(digest.Digest(desc.Digest), digest.FromBytes(sig)) } @@ -68,11 +83,11 @@ func runSign(ctx *cli.Context) error { return err } - if ref := ctx.String("push-reference"); ctx.Bool("push") && !(ctx.Bool(flagLocal.Name) && ref == "") { + if ref := cmdOpts.pushReference; cmdOpts.push && !(cmdOpts.Local && ref == "") { if ref == "" { - ref = ctx.Args().First() + ref = cmdOpts.reference } - if _, err := pushSignature(ctx, ref, sig); err != nil { + if _, err := pushSignature(command.Context(), &cmdOpts.SecureFlagOpts, ref, sig); err != nil { return fmt.Errorf("fail to push signature to %q: %v: %v", ref, desc.Digest, @@ -85,28 +100,28 @@ func runSign(ctx *cli.Context) error { return nil } -func prepareSigningContent(ctx *cli.Context) (notation.Descriptor, notation.SignOptions, error) { - manifestDesc, err := getManifestDescriptorFromContext(ctx) +func prepareSigningContent(ctx context.Context, opts *signOpts) (notation.Descriptor, notation.SignOptions, error) { + manifestDesc, err := getManifestDescriptorFromContext(ctx, &opts.RemoteFlagOpts, opts.reference) if err != nil { return notation.Descriptor{}, notation.SignOptions{}, err } - if identity := ctx.String(cmd.FlagReference.Name); identity != "" { + if identity := opts.originReference; identity != "" { manifestDesc.Annotations = map[string]string{ "identity": identity, } } var tsa timestamp.Timestamper - if endpoint := ctx.String(cmd.FlagTimestamp.Name); endpoint != "" { + if endpoint := opts.timestamp; endpoint != "" { if tsa, err = timestamp.NewHTTPTimestamper(nil, endpoint); err != nil { return notation.Descriptor{}, notation.SignOptions{}, err } } - pluginConfig, err := cmd.ParseFlagPluginConfig(ctx) + pluginConfig, err := cmd.ParseFlagPluginConfig(opts.pluginConfig) if err != nil { return notation.Descriptor{}, notation.SignOptions{}, err } return manifestDesc, notation.SignOptions{ - Expiry: cmd.GetExpiry(ctx), + Expiry: cmd.GetExpiry(opts.expiry), TSA: tsa, PluginConfig: pluginConfig, }, nil diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index ad4ad0492..1f69b2df8 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -13,53 +13,57 @@ import ( "github.com/notaryproject/notation/pkg/cache" "github.com/notaryproject/notation/pkg/config" "github.com/opencontainers/go-digest" - "github.com/urfave/cli/v2" + "github.com/spf13/cobra" ) -var verifyCommand = &cli.Command{ - Name: "verify", - Usage: "Verifies OCI Artifacts", - ArgsUsage: "", - Flags: []cli.Flag{ - flagSignature, - &cli.StringSliceFlag{ - Name: "cert", - Aliases: []string{"c"}, - Usage: "certificate names for verification", - }, - &cli.StringSliceFlag{ - Name: cmd.FlagCertFile.Name, - Usage: "certificate files for verification", - TakesFile: true, +type verifyOpts struct { + RemoteFlagOpts + signatures []string + certs []string + certFiles []string + pull bool + reference string +} + +func verifyCommand(opts *verifyOpts) *cobra.Command { + if opts == nil { + opts = &verifyOpts{} + } + command := &cobra.Command{ + Use: "verify [reference]", + Short: "Verifies OCI Artifacts", + PreRun: func(cmd *cobra.Command, args []string) { + if len(args) > 0 { + opts.reference = args[0] + } }, - &cli.BoolFlag{ - Name: "pull", - Usage: "pull remote signatures before verification", - Value: true, + RunE: func(cmd *cobra.Command, args []string) error { + return runVerify(cmd, opts) }, - flagLocal, - flagUsername, - flagPassword, - flagMediaType, - }, - Action: runVerify, + } + setFlagSignature(command.Flags(), &opts.signatures) + command.Flags().StringSliceVarP(&opts.certs, "cert", "c", []string{}, "certificate names for verification") + command.Flags().StringSliceVar(&opts.certFiles, cmd.PflagCertFile.Name, []string{}, "certificate files for verification") + command.Flags().BoolVar(&opts.pull, "pull", true, "pull remote signatures before verification") + opts.ApplyFlag(command.Flags()) + return command } -func runVerify(ctx *cli.Context) error { +func runVerify(command *cobra.Command, opts *verifyOpts) error { // initialize - verifier, err := getVerifier(ctx) + verifier, err := getVerifier(opts) if err != nil { return err } - manifestDesc, err := getManifestDescriptorFromContext(ctx) + manifestDesc, err := getManifestDescriptorFromContext(command.Context(), &opts.RemoteFlagOpts, opts.reference) if err != nil { return err } - sigPaths := ctx.StringSlice(flagSignature.Name) + sigPaths := opts.signatures if len(sigPaths) == 0 { - if !ctx.Bool(flagLocal.Name) && ctx.Bool("pull") { - if err := pullSignatures(ctx, digest.Digest(manifestDesc.Digest)); err != nil { + if !opts.Local && opts.pull { + if err := pullSignatures(command, opts.reference, &opts.SecureFlagOpts, digest.Digest(manifestDesc.Digest)); err != nil { return err } } @@ -74,7 +78,7 @@ func runVerify(ctx *cli.Context) error { } // core process - if err := verifySignatures(ctx.Context, verifier, manifestDesc, sigPaths); err != nil { + if err := verifySignatures(command.Context(), verifier, manifestDesc, sigPaths); err != nil { return err } @@ -110,9 +114,8 @@ func verifySignatures(ctx context.Context, verifier notation.Verifier, manifestD return lastErr } -func getVerifier(ctx *cli.Context) (notation.Verifier, error) { - certPaths := ctx.StringSlice(cmd.FlagCertFile.Name) - certPaths, err := appendCertPathFromName(certPaths, ctx.StringSlice("cert")) +func getVerifier(opts *verifyOpts) (notation.Verifier, error) { + certPaths, err := appendCertPathFromName(opts.certFiles, opts.certs) if err != nil { return nil, err } diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 9381e8db1..798206907 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -7,7 +7,6 @@ import ( "strings" "time" - "github.com/spf13/cobra" "github.com/spf13/pflag" "github.com/urfave/cli/v2" ) @@ -62,24 +61,24 @@ var ( Shorthand: "k", Usage: "signing key name", } - SetPflagKey = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVarP(p, PflagKey.Name, PflagKey.Shorthand, "", PflagKey.Usage) + SetPflagKey = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, PflagKey.Name, PflagKey.Shorthand, "", PflagKey.Usage) } PflagKeyFile = &pflag.Flag{ Name: "key-file", Usage: "signing key file", } - SetPflagKeyFile = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVar(p, PflagKeyFile.Name, "", PflagKeyFile.Usage) + SetPflagKeyFile = func(fs *pflag.FlagSet, p *string) { + fs.StringVar(p, PflagKeyFile.Name, "", PflagKeyFile.Usage) } PflagCertFile = &pflag.Flag{ Name: "cert-file", Usage: "signing certificate file", } - SetPflagCertFile = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVar(p, PflagCertFile.Name, "", PflagCertFile.Usage) + SetPflagCertFile = func(fs *pflag.FlagSet, p *string) { + fs.StringVar(p, PflagCertFile.Name, "", PflagCertFile.Usage) } PflagTimestamp = &pflag.Flag{ @@ -87,8 +86,8 @@ var ( Shorthand: "t", Usage: "timestamp the signed signature via the remote TSA", } - SetPflagTimestamp = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVarP(p, PflagTimestamp.Name, PflagTimestamp.Shorthand, "", PflagTimestamp.Usage) + SetPflagTimestamp = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, PflagTimestamp.Name, PflagTimestamp.Shorthand, "", PflagTimestamp.Usage) } PflagExpiry = &pflag.Flag{ @@ -96,8 +95,8 @@ var ( Shorthand: "e", Usage: "expire duration", } - SetPflagExpiry = func(cmd *cobra.Command, p *time.Duration) { - cmd.Flags().DurationVarP(p, PflagExpiry.Name, PflagExpiry.Shorthand, time.Duration(0), PflagExpiry.Usage) + SetPflagExpiry = func(fs *pflag.FlagSet, p *time.Duration) { + fs.DurationVarP(p, PflagExpiry.Name, PflagExpiry.Shorthand, time.Duration(0), PflagExpiry.Usage) } PflagReference = &pflag.Flag{ @@ -105,8 +104,8 @@ var ( Shorthand: "r", Usage: "original reference", } - SetPflagReference = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVarP(p, PflagReference.Name, PflagReference.Shorthand, "", PflagReference.Usage) + SetPflagReference = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, PflagReference.Name, PflagReference.Shorthand, "", PflagReference.Usage) } // TODO: cobra does not support letter shorthand @@ -115,8 +114,8 @@ var ( Shorthand: "c", Usage: "list of comma-separated {key}={value} pairs that are passed as is to the plugin, refer plugin documentation to set appropriate values", } - SetPflagPluginConfig = func(cmd *cobra.Command, p *string) { - cmd.Flags().StringVarP(p, PflagPluginConfig.Name, PflagPluginConfig.Shorthand, "", PflagPluginConfig.Usage) + SetPflagPluginConfig = func(fs *pflag.FlagSet, p *string) { + fs.StringVarP(p, PflagPluginConfig.Name, PflagPluginConfig.Shorthand, "", PflagPluginConfig.Usage) } ) @@ -126,11 +125,10 @@ type KeyValueSlice interface { String() string } -func ParseFlagPluginConfig(ctx *cli.Context) (map[string]string, error) { - val := ctx.String(FlagPluginConfig.Name) - pluginConfig, err := ParseKeyValueListFlag(val) +func ParseFlagPluginConfig(config string) (map[string]string, error) { + pluginConfig, err := ParseKeyValueListFlag(config) if err != nil { - return nil, fmt.Errorf("could not parse %q as value for flag %s: %s", val, FlagPluginConfig.Name, err) + return nil, fmt.Errorf("could not parse %q as value for flag %s: %s", pluginConfig, FlagPluginConfig.Name, err) } return pluginConfig, nil } diff --git a/internal/cmd/options.go b/internal/cmd/options.go new file mode 100644 index 000000000..df3ccd5a6 --- /dev/null +++ b/internal/cmd/options.go @@ -0,0 +1,18 @@ +package cmd + +import ( + "github.com/spf13/pflag" +) + +// SignerFlagOpts cmd opts for using cmd.GetSigner +type SignerFlagOpts struct { + Key string + KeyFile string + CertFile string +} + +func (opts *SignerFlagOpts) ApplyFlag(fs *pflag.FlagSet) { + SetPflagKey(fs, &opts.Key) + SetPflagKeyFile(fs, &opts.KeyFile) + SetPflagCertFile(fs, &opts.CertFile) +} diff --git a/internal/cmd/signer.go b/internal/cmd/signer.go index 5be9dece2..7c8b63a8f 100644 --- a/internal/cmd/signer.go +++ b/internal/cmd/signer.go @@ -8,19 +8,18 @@ import ( "github.com/notaryproject/notation-go/plugin/manager" "github.com/notaryproject/notation-go/signature" "github.com/notaryproject/notation/pkg/config" - "github.com/urfave/cli/v2" ) // GetSigner returns a signer according to the CLI context. -func GetSigner(ctx *cli.Context) (notation.Signer, error) { +func GetSigner(opts *SignerFlagOpts) (notation.Signer, error) { // Construct a signer from key and cert file if provided as CLI arguments - if keyPath := ctx.String(FlagKeyFile.Name); keyPath != "" { - certPath := ctx.String(FlagCertFile.Name) + if keyPath := opts.KeyFile; keyPath != "" { + certPath := opts.CertFile return signature.NewSignerFromFiles(keyPath, certPath) } // Construct a signer from preconfigured key pair in config.json // if key name is provided as the CLI argument - key, err := config.ResolveKey(ctx.String(FlagKey.Name)) + key, err := config.ResolveKey(opts.Key) if err != nil { return nil, err } @@ -41,8 +40,7 @@ func GetSigner(ctx *cli.Context) (notation.Signer, error) { } // GetExpiry returns the signature expiry according to the CLI context. -func GetExpiry(ctx *cli.Context) time.Time { - expiry := ctx.Duration(FlagExpiry.Name) +func GetExpiry(expiry time.Duration) time.Time { if expiry == 0 { return time.Time{} } From 06398102f3a59263edd74d24ce67a403a2a9fed4 Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Thu, 21 Jul 2022 10:06:30 +0800 Subject: [PATCH 2/6] typo fix Signed-off-by: zaihaoyin --- cmd/notation/common.go | 8 ++++---- cmd/notation/login.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/notation/common.go b/cmd/notation/common.go index d197a1f10..4ae283651 100644 --- a/cmd/notation/common.go +++ b/cmd/notation/common.go @@ -93,22 +93,22 @@ func (opts *SecureFlagOpts) ApplyFlag(fs *pflag.FlagSet) { opts.Password = os.Getenv(defaultPasswordEnv) } -type CommanFlagOpts struct { +type CommonFlagOpts struct { Local bool MediaType string } -func (opts *CommanFlagOpts) ApplyFlag(fs *pflag.FlagSet) { +func (opts *CommonFlagOpts) ApplyFlag(fs *pflag.FlagSet) { setFlagMediaType(fs, &opts.MediaType) setFlagLocal(fs, &opts.Local) } type RemoteFlagOpts struct { SecureFlagOpts - CommanFlagOpts + CommonFlagOpts } func (opts *RemoteFlagOpts) ApplyFlag(fs *pflag.FlagSet) { opts.SecureFlagOpts.ApplyFlag(fs) - opts.CommanFlagOpts.ApplyFlag(fs) + opts.CommonFlagOpts.ApplyFlag(fs) } diff --git a/cmd/notation/login.go b/cmd/notation/login.go index 1316f32ce..d50aff78c 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -45,7 +45,7 @@ func loginCommand(opts *loginOpts) *cobra.Command { return runLogin(cmd, opts) }, } - command.Flags().BoolVar(&opts.passwordStdin, "passowrd-stdin", false, "Take the password from stdin") + command.Flags().BoolVar(&opts.passwordStdin, "password-stdin", false, "Take the password from stdin") opts.ApplyFlag(command.Flags()) return command } From 27d692e90628d47fec54d59fccfd528175b6210c Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Thu, 21 Jul 2022 10:47:10 +0800 Subject: [PATCH 3/6] clean some code Signed-off-by: zaihaoyin --- cmd/notation/common.go | 1 - cmd/notation/login.go | 8 ++++---- internal/cmd/flags.go | 1 - 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/cmd/notation/common.go b/cmd/notation/common.go index 4ae283651..23ce8321e 100644 --- a/cmd/notation/common.go +++ b/cmd/notation/common.go @@ -87,7 +87,6 @@ type SecureFlagOpts struct { func (opts *SecureFlagOpts) ApplyFlag(fs *pflag.FlagSet) { setflagUsername(fs, &opts.Username) setFlagPassword(fs, &opts.Password) - setFlagPlainHTTP(fs, &opts.PlainHTTP) opts.Username = os.Getenv(defaultUsernameEnv) opts.Password = os.Getenv(defaultPasswordEnv) diff --git a/cmd/notation/login.go b/cmd/notation/login.go index d50aff78c..1ccb30171 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -28,11 +28,11 @@ func loginCommand(opts *loginOpts) *cobra.Command { Short: "Provides credentials for authenticated registry operations", Long: `notation login [options] [server] - Example - Login with provided username and password: - notation login -u -p registry.example.com +Example - Login with provided username and password: + notation login -u -p registry.example.com - Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: - notation login registry.example.com`, +Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: + notation login registry.example.com`, Args: cobra.ExactArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { if err := readPassword(opts); err != nil { diff --git a/internal/cmd/flags.go b/internal/cmd/flags.go index 798206907..475dc4be4 100644 --- a/internal/cmd/flags.go +++ b/internal/cmd/flags.go @@ -108,7 +108,6 @@ var ( fs.StringVarP(p, PflagReference.Name, PflagReference.Shorthand, "", PflagReference.Usage) } - // TODO: cobra does not support letter shorthand PflagPluginConfig = &pflag.Flag{ Name: "pluginConfig", Shorthand: "c", From 24c1f6ca158897921cc51dca7687a74026d56f12 Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Fri, 22 Jul 2022 10:07:42 +0800 Subject: [PATCH 4/6] fix naming and indention Signed-off-by: zaihaoyin --- cmd/docker-notation/sign.go | 2 +- cmd/notation/cache.go | 6 +++--- cmd/notation/cert_gen.go | 3 +-- cmd/notation/common.go | 13 ++++++++----- cmd/notation/key.go | 4 ++-- cmd/notation/list.go | 2 +- cmd/notation/login.go | 2 +- cmd/notation/pull.go | 2 +- cmd/notation/push.go | 2 +- cmd/notation/sign.go | 4 ++-- cmd/notation/verify.go | 2 +- internal/cmd/options.go | 3 ++- 12 files changed, 24 insertions(+), 21 deletions(-) diff --git a/cmd/docker-notation/sign.go b/cmd/docker-notation/sign.go index 7a8f7b2ad..54e9a04d4 100644 --- a/cmd/docker-notation/sign.go +++ b/cmd/docker-notation/sign.go @@ -34,7 +34,7 @@ var signCommand = &cli.Command{ func signImage(ctx *cli.Context) error { // TODO: make this change only to make sure the code can be compiled // According to the https://github.com/notaryproject/notation/discussions/251, - // we can update/deprecate it later + // we can update/deprecate it later signerOpts := &cmd.SignerFlagOpts{ Key: ctx.String(cmd.FlagKey.Name), KeyFile: ctx.String(cmd.FlagKeyFile.Name), diff --git a/cmd/notation/cache.go b/cmd/notation/cache.go index 20aac623a..8e6497854 100644 --- a/cmd/notation/cache.go +++ b/cmd/notation/cache.go @@ -60,7 +60,7 @@ func cacheListCommand(opts *cacheListOpts) *cobra.Command { return listCachedSignatures(cmd, opts) }, } - opts.ApplyFlag(command.Flags()) + opts.ApplyFlags(command.Flags()) return command } @@ -82,7 +82,7 @@ func cachePruneCommand(opts *cachePruneOpts) *cobra.Command { command.Flags().BoolVarP(&opts.all, "all", "a", false, "prune all cached signatures") command.Flags().BoolVar(&opts.purge, "purge", false, "remove the signature directory, combined with --all") command.Flags().BoolVarP(&opts.force, "force", "f", false, "do not prompt for confirmation") - opts.ApplyFlag(command.Flags()) + opts.ApplyFlags(command.Flags()) return command } @@ -103,7 +103,7 @@ func cacheRemoveCommand(opts *cacheRemoveOpts) *cobra.Command { return removeCachedSignatures(cmd, opts) }, } - opts.ApplyFlag(command.Flags()) + opts.ApplyFlags(command.Flags()) return command } diff --git a/cmd/notation/cert_gen.go b/cmd/notation/cert_gen.go index 5ad3dfda6..e17683336 100644 --- a/cmd/notation/cert_gen.go +++ b/cmd/notation/cert_gen.go @@ -37,8 +37,7 @@ func generateTestCert(opts *certGenerateTestOpts) error { } // generate self-signed certificate - expiry := opts.expiry - cert, certBytes, err := generateTestSelfSignedCert(key, hosts, expiry) + cert, certBytes, err := generateTestSelfSignedCert(key, hosts, opts.expiry) if err != nil { return err } diff --git a/cmd/notation/common.go b/cmd/notation/common.go index 23ce8321e..9d19890fb 100644 --- a/cmd/notation/common.go +++ b/cmd/notation/common.go @@ -84,7 +84,8 @@ type SecureFlagOpts struct { PlainHTTP bool } -func (opts *SecureFlagOpts) ApplyFlag(fs *pflag.FlagSet) { +// ApplyFlags set flags and their default values for the FlagSet +func (opts *SecureFlagOpts) ApplyFlags(fs *pflag.FlagSet) { setflagUsername(fs, &opts.Username) setFlagPassword(fs, &opts.Password) setFlagPlainHTTP(fs, &opts.PlainHTTP) @@ -97,7 +98,8 @@ type CommonFlagOpts struct { MediaType string } -func (opts *CommonFlagOpts) ApplyFlag(fs *pflag.FlagSet) { +// ApplyFlags set flags and their default values for the FlagSet +func (opts *CommonFlagOpts) ApplyFlags(fs *pflag.FlagSet) { setFlagMediaType(fs, &opts.MediaType) setFlagLocal(fs, &opts.Local) } @@ -107,7 +109,8 @@ type RemoteFlagOpts struct { CommonFlagOpts } -func (opts *RemoteFlagOpts) ApplyFlag(fs *pflag.FlagSet) { - opts.SecureFlagOpts.ApplyFlag(fs) - opts.CommonFlagOpts.ApplyFlag(fs) +// ApplyFlags set flags and their default values for the FlagSet +func (opts *RemoteFlagOpts) ApplyFlags(fs *pflag.FlagSet) { + opts.SecureFlagOpts.ApplyFlags(fs) + opts.CommonFlagOpts.ApplyFlags(fs) } diff --git a/cmd/notation/key.go b/cmd/notation/key.go index d11a9f3de..f002f6000 100644 --- a/cmd/notation/key.go +++ b/cmd/notation/key.go @@ -112,7 +112,7 @@ func keyListCommand() *cobra.Command { Aliases: []string{"ls"}, Short: "List keys used for signing", RunE: func(cmd *cobra.Command, args []string) error { - return listKeys(cmd) + return listKeys() }, } } @@ -266,7 +266,7 @@ func updateKey(command *cobra.Command, opts *keyUpdateOpts) error { return nil } -func listKeys(command *cobra.Command) error { +func listKeys() error { // core process cfg, err := config.LoadOrDefault() if err != nil { diff --git a/cmd/notation/list.go b/cmd/notation/list.go index b27ec3e03..e654c7c55 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -27,7 +27,7 @@ func listCommand(opts *listOpts) *cobra.Command { return runList(cmd, opts) }, } - opts.ApplyFlag(cmd.Flags()) + opts.ApplyFlags(cmd.Flags()) return cmd } diff --git a/cmd/notation/login.go b/cmd/notation/login.go index 1ccb30171..c547d5450 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -46,7 +46,7 @@ Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: }, } command.Flags().BoolVar(&opts.passwordStdin, "password-stdin", false, "Take the password from stdin") - opts.ApplyFlag(command.Flags()) + opts.ApplyFlags(command.Flags()) return command } diff --git a/cmd/notation/pull.go b/cmd/notation/pull.go index bf1e194c9..8596c096d 100644 --- a/cmd/notation/pull.go +++ b/cmd/notation/pull.go @@ -39,7 +39,7 @@ func pullCommand(opts *pullOpts) *cobra.Command { } cmd.Flags().BoolVar(&opts.strict, "strict", false, "pull the signature without lookup the manifest") setFlagOutput(cmd.Flags(), &opts.output) - opts.ApplyFlag(cmd.Flags()) + opts.ApplyFlags(cmd.Flags()) return cmd } diff --git a/cmd/notation/push.go b/cmd/notation/push.go index eb53344dd..1448a8862 100644 --- a/cmd/notation/push.go +++ b/cmd/notation/push.go @@ -34,7 +34,7 @@ func pushCommand(opts *pushOpts) *cobra.Command { }, } setFlagSignature(cmd.Flags(), &opts.signatures) - opts.ApplyFlag(cmd.Flags()) + opts.ApplyFlags(cmd.Flags()) return cmd } diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index ef7666f93..9dedecd4a 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -42,8 +42,8 @@ func signCommand(opts *signOpts) *cobra.Command { return runSign(cmd, opts) }, } - opts.SignerFlagOpts.ApplyFlag(command.Flags()) - opts.RemoteFlagOpts.ApplyFlag(command.Flags()) + opts.SignerFlagOpts.ApplyFlags(command.Flags()) + opts.RemoteFlagOpts.ApplyFlags(command.Flags()) cmd.SetPflagTimestamp(command.Flags(), &opts.timestamp) cmd.SetPflagExpiry(command.Flags(), &opts.expiry) diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index 1f69b2df8..c33ef5a68 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -45,7 +45,7 @@ func verifyCommand(opts *verifyOpts) *cobra.Command { command.Flags().StringSliceVarP(&opts.certs, "cert", "c", []string{}, "certificate names for verification") command.Flags().StringSliceVar(&opts.certFiles, cmd.PflagCertFile.Name, []string{}, "certificate files for verification") command.Flags().BoolVar(&opts.pull, "pull", true, "pull remote signatures before verification") - opts.ApplyFlag(command.Flags()) + opts.ApplyFlags(command.Flags()) return command } diff --git a/internal/cmd/options.go b/internal/cmd/options.go index df3ccd5a6..f48a7273c 100644 --- a/internal/cmd/options.go +++ b/internal/cmd/options.go @@ -11,7 +11,8 @@ type SignerFlagOpts struct { CertFile string } -func (opts *SignerFlagOpts) ApplyFlag(fs *pflag.FlagSet) { +// ApplyFlags set flags and their default values for the FlagSet +func (opts *SignerFlagOpts) ApplyFlags(fs *pflag.FlagSet) { SetPflagKey(fs, &opts.Key) SetPflagKeyFile(fs, &opts.KeyFile) SetPflagCertFile(fs, &opts.CertFile) From c93181998cb4a065d3f3ff1162ae96c65d41e162 Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Mon, 25 Jul 2022 11:55:47 +0800 Subject: [PATCH 5/6] better error message when args checker failed Signed-off-by: zaihaoyin --- cmd/notation/cache.go | 24 ++++++++++++-------- cmd/notation/cert.go | 48 +++++++++++++++++++--------------------- cmd/notation/cert_gen.go | 4 ---- cmd/notation/key.go | 38 ++++++++++++++----------------- cmd/notation/list.go | 8 +++++-- cmd/notation/login.go | 12 +++++----- cmd/notation/logout.go | 10 ++++----- cmd/notation/pull.go | 11 +++++---- cmd/notation/push.go | 10 ++++----- cmd/notation/sign.go | 8 +++++-- cmd/notation/verify.go | 8 ++++--- 11 files changed, 94 insertions(+), 87 deletions(-) diff --git a/cmd/notation/cache.go b/cmd/notation/cache.go index 8e6497854..f5216af0f 100644 --- a/cmd/notation/cache.go +++ b/cmd/notation/cache.go @@ -51,10 +51,11 @@ func cacheListCommand(opts *cacheListOpts) *cobra.Command { Use: "list [reference|manifest_digest]", Aliases: []string{"ls"}, Short: "List signatures in cache", - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { opts.reference = args[0] } + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return listCachedSignatures(cmd, opts) @@ -71,9 +72,12 @@ func cachePruneCommand(opts *cachePruneOpts) *cobra.Command { command := &cobra.Command{ Use: "prune [reference|manifest_digest]...", Short: "Prune signature from cache", - Args: cobra.MinimumNArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("nothing to prune") + } opts.references = args + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return pruneCachedSignatures(cmd, opts) @@ -94,10 +98,16 @@ func cacheRemoveCommand(opts *cacheRemoveOpts) *cobra.Command { Use: "remove [reference|manifest_digest] [signature_digest]...", Aliases: []string{"rm"}, Short: "Remove signature from cache", - Args: cobra.MinimumNArgs(2), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing target manifest") + } opts.reference = args[0] + if len(args) == 1 { + return errors.New("no signature specified") + } opts.sigDigests = args[1:] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return removeCachedSignatures(cmd, opts) @@ -177,10 +187,6 @@ func pruneCachedSignatures(command *cobra.Command, opts *cachePruneOpts) error { } return nil } - - if len(opts.references) == 0 { - return errors.New("nothing to prune") - } refs := opts.references if !opts.force { fmt.Println("WARNING! This will remove cached signatures for manifests below:") diff --git a/cmd/notation/cert.go b/cmd/notation/cert.go index 9b6fba934..b476adf66 100644 --- a/cmd/notation/cert.go +++ b/cmd/notation/cert.go @@ -50,12 +50,15 @@ func certAddCommand(opts *certAddOpts) *cobra.Command { command := &cobra.Command{ Use: "add [path]", Short: "Add certificate to verification list", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing certificate path") + } opts.path = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return addCert(cmd, opts) + return addCert(opts) }, } command.Flags().StringVarP(&opts.name, "name", "n", "", "certificate name") @@ -68,7 +71,7 @@ func certListCommand() *cobra.Command { Aliases: []string{"ls"}, Short: "List certificates used for verification", RunE: func(cmd *cobra.Command, args []string) error { - return listCerts(cmd) + return listCerts() }, } return command @@ -81,12 +84,15 @@ func certRemoveCommand(opts *certRemoveOpts) *cobra.Command { Use: "remove [name]...", Aliases: []string{"rm"}, Short: "Remove certificate from the verification list", - Args: cobra.MinimumNArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing certificate names") + } opts.names = args + return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return removeCerts(cmd, opts) + return removeCerts(opts) }, } return command @@ -98,9 +104,12 @@ func certGenerateTestCommand(opts *certGenerateTestOpts) *cobra.Command { command := &cobra.Command{ Use: "generate-test [host]...", Short: "Generates a test RSA key and a corresponding self-signed certificate", - Args: cobra.MinimumNArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing certificate hosts") + } opts.hosts = args + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return generateTestCert(opts) @@ -115,14 +124,9 @@ func certGenerateTestCommand(opts *certGenerateTestOpts) *cobra.Command { return command } -func addCert(command *cobra.Command, opts *certAddOpts) error { +func addCert(opts *certAddOpts) error { // initialize - - path := opts.path - if path == "" { - return errors.New("missing certificate path") - } - path, err := filepath.Abs(path) + path, err := filepath.Abs(opts.path) if err != nil { return err } @@ -161,7 +165,7 @@ func addCertCore(cfg *config.File, name, path string) error { return nil } -func listCerts(command *cobra.Command) error { +func listCerts() error { // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -172,13 +176,7 @@ func listCerts(command *cobra.Command) error { return ioutil.PrintCertificateMap(os.Stdout, cfg.VerificationCertificates.Certificates) } -func removeCerts(command *cobra.Command, opts *certRemoveOpts) error { - // initialize - names := opts.names - if len(names) == 0 { - return errors.New("missing certificate names") - } - +func removeCerts(opts *certRemoveOpts) error { // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -186,7 +184,7 @@ func removeCerts(command *cobra.Command, opts *certRemoveOpts) error { } var removedNames []string - for _, name := range names { + for _, name := range opts.names { idx := slices.Index(cfg.VerificationCertificates.Certificates, name) if idx < 0 { return errors.New(name + ": not found") diff --git a/cmd/notation/cert_gen.go b/cmd/notation/cert_gen.go index e17683336..00479f9bc 100644 --- a/cmd/notation/cert_gen.go +++ b/cmd/notation/cert_gen.go @@ -7,7 +7,6 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" - "errors" "fmt" "math/big" "net" @@ -20,9 +19,6 @@ import ( func generateTestCert(opts *certGenerateTestOpts) error { // initialize hosts := opts.hosts - if len(hosts) == 0 { - return errors.New("missing certificate hosts") - } name := opts.name if name == "" { name = hosts[0] diff --git a/cmd/notation/key.go b/cmd/notation/key.go index f002f6000..8d5c7ce7c 100644 --- a/cmd/notation/key.go +++ b/cmd/notation/key.go @@ -63,12 +63,12 @@ func keyAddCommand(opts *keyAddOpts) *cobra.Command { command := &cobra.Command{ Use: "add [key_path cert_path]", Short: "Add key to signing key list", - Args: cobra.MaximumNArgs(2), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { if len(args) >= 2 { opts.keyPath = args[0] opts.certPath = args[1] } + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return addKey(cmd, opts) @@ -93,12 +93,15 @@ func keyUpdateCommand(opts *keyUpdateOpts) *cobra.Command { Use: "update [name]", Aliases: []string{"set"}, Short: "Update key in signing key list", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing key name") + } opts.name = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return updateKey(cmd, opts) + return updateKey(opts) }, } @@ -125,12 +128,15 @@ func keyRemoveCommand(opts *keyRemoveOpts) *cobra.Command { Use: "remove [name]...", Aliases: []string{"rm"}, Short: "Remove key from signing key list", - Args: cobra.MinimumNArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing key names") + } opts.names = args + return nil }, RunE: func(cmd *cobra.Command, args []string) error { - return removeKeys(cmd, opts) + return removeKeys(opts) }, } } @@ -236,13 +242,9 @@ func addKeyCore(cfg *config.File, key config.KeySuite, markDefault bool) error { return nil } -func updateKey(command *cobra.Command, opts *keyUpdateOpts) error { +func updateKey(opts *keyUpdateOpts) error { // initialize name := opts.name - if name == "" { - return errors.New("missing key name") - } - // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -277,13 +279,7 @@ func listKeys() error { return ioutil.PrintKeyMap(os.Stdout, cfg.SigningKeys.Default, cfg.SigningKeys.Keys) } -func removeKeys(command *cobra.Command, opts *keyRemoveOpts) error { - // initialize - names := opts.names - if len(names) == 0 { - return errors.New("missing key names") - } - +func removeKeys(opts *keyRemoveOpts) error { // core process cfg, err := config.LoadOrDefault() if err != nil { @@ -292,7 +288,7 @@ func removeKeys(command *cobra.Command, opts *keyRemoveOpts) error { prevDefault := cfg.SigningKeys.Default var removedNames []string - for _, name := range names { + for _, name := range opts.names { idx := slices.Index(cfg.SigningKeys.Keys, name) if idx < 0 { return errors.New(name + ": not found") diff --git a/cmd/notation/list.go b/cmd/notation/list.go index e654c7c55..66a3a78aa 100644 --- a/cmd/notation/list.go +++ b/cmd/notation/list.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "github.com/spf13/cobra" @@ -19,9 +20,12 @@ func listCommand(opts *listOpts) *cobra.Command { Use: "list [reference]", Aliases: []string{"ls"}, Short: "List signatures from remote", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no reference specified") + } opts.reference = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runList(cmd, opts) diff --git a/cmd/notation/login.go b/cmd/notation/login.go index c547d5450..56147afe2 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -33,12 +33,17 @@ Example - Login with provided username and password: Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: notation login registry.example.com`, - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no hostname specified") + } + opts.server = args[0] + return nil + }, PreRunE: func(cmd *cobra.Command, args []string) error { if err := readPassword(opts); err != nil { return err } - opts.server = args[0] return nil }, RunE: func(cmd *cobra.Command, args []string) error { @@ -52,9 +57,6 @@ Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: func runLogin(cmd *cobra.Command, opts *loginOpts) error { // initialize - if opts.server == "" { - return errors.New("no hostname specified") - } serverAddress := opts.server if err := validateAuthConfig(cmd.Context(), opts, serverAddress); err != nil { diff --git a/cmd/notation/logout.go b/cmd/notation/logout.go index 008ac2e03..b6eeb20c8 100644 --- a/cmd/notation/logout.go +++ b/cmd/notation/logout.go @@ -18,9 +18,12 @@ func logoutCommand(opts *logoutOpts) *cobra.Command { return &cobra.Command{ Use: "logout [server]", Short: "Log out the specified registry hostname", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no hostname specified") + } opts.server = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runLogout(cmd, opts) @@ -30,9 +33,6 @@ func logoutCommand(opts *logoutOpts) *cobra.Command { func runLogout(cmd *cobra.Command, opts *logoutOpts) error { // initialize - if opts.server == "" { - return errors.New("no hostname specified") - } serverAddress := opts.server nativeStore, err := auth.GetCredentialsStore(serverAddress) if err != nil { diff --git a/cmd/notation/pull.go b/cmd/notation/pull.go index 8596c096d..3dc7138a8 100644 --- a/cmd/notation/pull.go +++ b/cmd/notation/pull.go @@ -29,9 +29,12 @@ func pullCommand(opts *pullOpts) *cobra.Command { cmd := &cobra.Command{ Use: "pull [reference]", Short: "Pull signatures from remote", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no reference specified") + } opts.reference = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runPull(cmd, opts) @@ -45,10 +48,6 @@ func pullCommand(opts *pullOpts) *cobra.Command { func runPull(command *cobra.Command, opts *pullOpts) error { // initialize - if opts.reference == "" { - return errors.New("no reference specified") - } - reference := opts.reference sigRepo, err := getSignatureRepository(&opts.SecureFlagOpts, reference) if err != nil { diff --git a/cmd/notation/push.go b/cmd/notation/push.go index 1448a8862..98128937d 100644 --- a/cmd/notation/push.go +++ b/cmd/notation/push.go @@ -25,9 +25,12 @@ func pushCommand(opts *pushOpts) *cobra.Command { cmd := &cobra.Command{ Use: "push [reference]", Short: "Push signature to remote", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("no reference specified") + } opts.reference = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runPush(cmd, opts) @@ -40,9 +43,6 @@ func pushCommand(opts *pushOpts) *cobra.Command { func runPush(command *cobra.Command, opts *pushOpts) error { // initialize - if opts.reference == "" { - return errors.New("no reference specified") - } ref := opts.reference manifestDesc, err := getManifestDescriptorFromReference(command.Context(), &opts.SecureFlagOpts, ref) if err != nil { diff --git a/cmd/notation/sign.go b/cmd/notation/sign.go index 9dedecd4a..ae902402c 100644 --- a/cmd/notation/sign.go +++ b/cmd/notation/sign.go @@ -2,6 +2,7 @@ package main import ( "context" + "errors" "fmt" "time" @@ -34,9 +35,12 @@ func signCommand(opts *signOpts) *cobra.Command { command := &cobra.Command{ Use: "sign [reference]", Short: "Signs artifacts", - Args: cobra.ExactArgs(1), - PreRun: func(cmd *cobra.Command, args []string) { + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing reference") + } opts.reference = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runSign(cmd, opts) diff --git a/cmd/notation/verify.go b/cmd/notation/verify.go index c33ef5a68..a71733cf4 100644 --- a/cmd/notation/verify.go +++ b/cmd/notation/verify.go @@ -32,10 +32,12 @@ func verifyCommand(opts *verifyOpts) *cobra.Command { command := &cobra.Command{ Use: "verify [reference]", Short: "Verifies OCI Artifacts", - PreRun: func(cmd *cobra.Command, args []string) { - if len(args) > 0 { - opts.reference = args[0] + Args: func(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("missing reference") } + opts.reference = args[0] + return nil }, RunE: func(cmd *cobra.Command, args []string) error { return runVerify(cmd, opts) From 4a0f2d88c34cbdee90c2a9bc1e678e2ffedded3f Mon Sep 17 00:00:00 2001 From: zaihaoyin Date: Wed, 27 Jul 2022 15:12:45 +0800 Subject: [PATCH 6/6] nit:delete space Signed-off-by: zaihaoyin --- cmd/notation/cache.go | 1 + cmd/notation/login.go | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/notation/cache.go b/cmd/notation/cache.go index f5216af0f..def604d22 100644 --- a/cmd/notation/cache.go +++ b/cmd/notation/cache.go @@ -20,6 +20,7 @@ type cacheListOpts struct { RemoteFlagOpts reference string } + type cachePruneOpts struct { RemoteFlagOpts references []string diff --git a/cmd/notation/login.go b/cmd/notation/login.go index 56147afe2..71c09157a 100644 --- a/cmd/notation/login.go +++ b/cmd/notation/login.go @@ -27,12 +27,12 @@ func loginCommand(opts *loginOpts) *cobra.Command { Use: "login [options] [server]", Short: "Provides credentials for authenticated registry operations", Long: `notation login [options] [server] - + Example - Login with provided username and password: - notation login -u -p registry.example.com - + notation login -u -p registry.example.com + Example - Login using $NOTATION_USERNAME $NOTATION_PASSWORD variables: - notation login registry.example.com`, + notation login registry.example.com`, Args: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("no hostname specified")