Browse Source

Reorganize cobra cmd to enable better reuse

pull/1782/head
Ethan Frey 8 years ago
parent
commit
398ac046da
9 changed files with 146 additions and 94 deletions
  1. +119
    -0
      cmd/common.go
  2. +1
    -1
      cmd/get.go
  3. +9
    -2
      cmd/keys/main.go
  4. +1
    -1
      cmd/list.go
  5. +1
    -1
      cmd/new.go
  6. +10
    -85
      cmd/root.go
  7. +1
    -1
      cmd/serve.go
  8. +1
    -1
      cmd/update.go
  9. +3
    -2
      cmd/utils.go

+ 119
- 0
cmd/common.go View File

@ -0,0 +1,119 @@
package cmd
import (
"fmt"
"os"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
data "github.com/tendermint/go-data"
"github.com/tendermint/go-data/base58"
)
/*******
TODO
This file should move into go-common or the like as a basis for all cli tools.
It is here for experimentation of re-use between go-keys and light-client.
*********/
const (
RootFlag = "root"
OutputFlag = "output"
)
func PrepareMainCmd(cmd *cobra.Command, envPrefix, defautRoot string) func() {
cobra.OnInitialize(func() { initEnv(envPrefix) })
cmd.PersistentFlags().StringP(RootFlag, "r", defautRoot, "root directory for config and data")
cmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)")
cmd.PersistentFlags().StringP(OutputFlag, "o", "text", "Output format (text|json)")
cmd.PersistentPreRunE = multiE(bindFlags, setEncoding, validateOutput, cmd.PersistentPreRunE)
return func() { execute(cmd) }
}
// initEnv sets to use ENV variables if set.
func initEnv(prefix string) {
// env variables with TM prefix (eg. TM_ROOT)
viper.SetEnvPrefix(prefix)
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
}
// execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func execute(cmd *cobra.Command) {
if err := cmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
type wrapE func(cmd *cobra.Command, args []string) error
func multiE(fs ...wrapE) wrapE {
return func(cmd *cobra.Command, args []string) error {
for _, f := range fs {
if f != nil {
if err := f(cmd, args); err != nil {
return err
}
}
}
return nil
}
}
func bindFlags(cmd *cobra.Command, args []string) error {
// cmd.Flags() includes flags from this command and all persistent flags from the parent
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
// rootDir is command line flag, env variable, or default $HOME/.tlc
rootDir := viper.GetString("root")
viper.SetConfigName("config") // name of config file (without extension)
viper.AddConfigPath(rootDir) // search root directory
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
// stderr, so if we redirect output to json file, this doesn't appear
// fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
} else if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
// we ignore not found error, only parse error
// stderr, so if we redirect output to json file, this doesn't appear
fmt.Fprintf(os.Stderr, "%#v", err)
}
return nil
}
// setEncoding reads the encoding flag
func setEncoding(cmd *cobra.Command, args []string) error {
// validate and set encoding
enc := viper.GetString("encoding")
switch enc {
case "hex":
data.Encoder = data.HexEncoder
case "b64":
data.Encoder = data.B64Encoder
case "btc":
data.Encoder = base58.BTCEncoder
default:
return errors.Errorf("Unsupported encoding: %s", enc)
}
return nil
}
func validateOutput(cmd *cobra.Command, args []string) error {
// validate output format
output := viper.GetString(OutputFlag)
switch output {
case "text", "json":
default:
return errors.Errorf("Unsupported output format: %s", output)
}
return nil
}

+ 1
- 1
cmd/get.go View File

@ -32,7 +32,7 @@ var getCmd = &cobra.Command{
} }
name := args[0] name := args[0]
info, err := manager.Get(name)
info, err := Manager.Get(name)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
return return


+ 9
- 2
cmd/keys/main.go View File

@ -14,8 +14,15 @@
package main package main
import "github.com/tendermint/go-keys/cmd"
import (
"os"
"github.com/tendermint/go-keys/cmd"
)
func main() { func main() {
cmd.Execute()
cmd.RootCmd.PersistentPreRunE = cmd.SetupKeys
cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc"))
cmd.RootCmd.Execute()
// exec()
} }

+ 1
- 1
cmd/list.go View File

@ -27,7 +27,7 @@ var listCmd = &cobra.Command{
Long: `Return a list of all public keys stored by this key manager Long: `Return a list of all public keys stored by this key manager
along with their associated name and address.`, along with their associated name and address.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
infos, err := manager.List()
infos, err := Manager.List()
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
return return


+ 1
- 1
cmd/new.go View File

@ -50,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) {
return return
} }
info, err := manager.Create(name, pass, algo)
info, err := Manager.Create(name, pass, algo)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
return return


+ 10
- 85
cmd/root.go View File

@ -15,26 +15,19 @@
package cmd package cmd
import ( import (
"fmt"
"os"
"path/filepath" "path/filepath"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/viper" "github.com/spf13/viper"
data "github.com/tendermint/go-data"
"github.com/tendermint/go-data/base58"
keys "github.com/tendermint/go-keys" keys "github.com/tendermint/go-keys"
"github.com/tendermint/go-keys/cryptostore" "github.com/tendermint/go-keys/cryptostore"
"github.com/tendermint/go-keys/storage/filestorage" "github.com/tendermint/go-keys/storage/filestorage"
) )
const KeySubdir = "keys"
var ( var (
rootDir string
output string
keyDir string
manager keys.Manager
Manager keys.Manager
) )
// RootCmd represents the base command when called without any subcommands // RootCmd represents the base command when called without any subcommands
@ -46,87 +39,19 @@ var RootCmd = &cobra.Command{
These keys may be in any format supported by go-crypto and can be These keys may be in any format supported by go-crypto and can be
used by light-clients, full nodes, or any other application that used by light-clients, full nodes, or any other application that
needs to sign with a private key.`, needs to sign with a private key.`,
PersistentPreRunE: bindFlags,
}
// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(-1)
}
}
func init() {
cobra.OnInitialize(initEnv)
RootCmd.PersistentFlags().StringP("root", "r", os.ExpandEnv("$HOME/.tlc"), "root directory for config and data")
RootCmd.PersistentFlags().String("keydir", "keys", "Directory to store private keys (subdir of root)")
RootCmd.PersistentFlags().StringP("output", "o", "text", "Output format (text|json)")
RootCmd.PersistentFlags().StringP("encoding", "e", "hex", "Binary encoding (hex|b64|btc)")
}
// initEnv sets to use ENV variables if set.
func initEnv() {
// env variables with TM prefix (eg. TM_ROOT)
viper.SetEnvPrefix("TM")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
} }
func bindFlags(cmd *cobra.Command, args []string) error {
// cmd.Flags() includes flags from this command and all persistent flags from the parent
if err := viper.BindPFlags(cmd.Flags()); err != nil {
return err
}
// rootDir is command line flag, env variable, or default $HOME/.tlc
rootDir = viper.GetString("root")
viper.SetConfigName("keys") // name of config file (without extension)
viper.AddConfigPath(rootDir) // search root directory
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
// stderr, so if we redirect output to json file, this doesn't appear
fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed())
}
return validateFlags(cmd)
}
// validateFlags asserts all RootCmd flags are valid
func validateFlags(cmd *cobra.Command) error {
// validate output format
output = viper.GetString("output")
switch output {
case "text", "json":
default:
return errors.Errorf("Unsupported output format: %s", output)
}
// validate and set encoding
enc := viper.GetString("encoding")
switch enc {
case "hex":
data.Encoder = data.HexEncoder
case "b64":
data.Encoder = data.B64Encoder
case "btc":
data.Encoder = base58.BTCEncoder
default:
return errors.Errorf("Unsupported encoding: %s", enc)
}
// SetupKeys must be registered in main() on the top level command
// here this is RootCmd, but if we embed keys in eg. light-client,
// that must be responsible for the update
func SetupKeys(cmd *cobra.Command, args []string) error {
// store the keys directory // store the keys directory
keyDir = viper.GetString("keydir")
if !filepath.IsAbs(keyDir) {
keyDir = filepath.Join(rootDir, keyDir)
}
rootDir := viper.GetString("root")
keyDir := filepath.Join(rootDir, KeySubdir)
// and construct the key manager // and construct the key manager
manager = cryptostore.New(
Manager = cryptostore.New(
cryptostore.SecretBox, cryptostore.SecretBox,
filestorage.New(keyDir), filestorage.New(keyDir),
) )
return nil return nil
} }

+ 1
- 1
cmd/serve.go View File

@ -64,7 +64,7 @@ func serveHTTP(cmd *cobra.Command, args []string) error {
} }
router := mux.NewRouter() router := mux.NewRouter()
ks := server.New(manager, viper.GetString("type"))
ks := server.New(Manager, viper.GetString("type"))
ks.Register(router) ks.Register(router)
// only set cors for tcp listener // only set cors for tcp listener


+ 1
- 1
cmd/update.go View File

@ -50,7 +50,7 @@ func updatePassword(cmd *cobra.Command, args []string) {
return return
} }
err = manager.Update(name, oldpass, newpass)
err = Manager.Update(name, oldpass, newpass)
if err != nil { if err != nil {
fmt.Println(err.Error()) fmt.Println(err.Error())
} else { } else {


+ 3
- 2
cmd/utils.go View File

@ -5,6 +5,7 @@ import (
"github.com/bgentry/speakeasy" "github.com/bgentry/speakeasy"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/viper"
data "github.com/tendermint/go-data" data "github.com/tendermint/go-data"
keys "github.com/tendermint/go-keys" keys "github.com/tendermint/go-keys"
) )
@ -39,7 +40,7 @@ func getCheckPassword(prompt, prompt2 string) (string, error) {
} }
func printInfo(info keys.Info) { func printInfo(info keys.Info) {
switch output {
switch viper.Get(OutputFlag) {
case "text": case "text":
addr, err := data.ToText(info.Address) addr, err := data.ToText(info.Address)
if err != nil { if err != nil {
@ -60,7 +61,7 @@ func printInfo(info keys.Info) {
} }
func printInfos(infos keys.Infos) { func printInfos(infos keys.Infos) {
switch output {
switch viper.Get(OutputFlag) {
case "text": case "text":
fmt.Println("All keys:") fmt.Println("All keys:")
for _, i := range infos { for _, i := range infos {


Loading…
Cancel
Save