mirror of
https://github.com/tendermint/tendermint.git
synced 2026-08-22 15:16:24 +00:00
Reorganize cobra cmd to enable better reuse
This commit is contained in:
+119
@@ -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
@@ -32,7 +32,7 @@ var getCmd = &cobra.Command{
|
||||
}
|
||||
name := args[0]
|
||||
|
||||
info, err := manager.Get(name)
|
||||
info, err := Manager.Get(name)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
|
||||
+9
-2
@@ -14,8 +14,15 @@
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/tendermint/go-keys/cmd"
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/tendermint/go-keys/cmd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
cmd.RootCmd.PersistentPreRunE = cmd.SetupKeys
|
||||
cmd.PrepareMainCmd(cmd.RootCmd, "TM", os.ExpandEnv("$HOME/.tlc"))
|
||||
cmd.RootCmd.Execute()
|
||||
// exec()
|
||||
}
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ var listCmd = &cobra.Command{
|
||||
Long: `Return a list of all public keys stored by this key manager
|
||||
along with their associated name and address.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
infos, err := manager.List()
|
||||
infos, err := Manager.List()
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ func newPassword(cmd *cobra.Command, args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
info, err := manager.Create(name, pass, algo)
|
||||
info, err := Manager.Create(name, pass, algo)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
return
|
||||
|
||||
+10
-85
@@ -15,26 +15,19 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"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"
|
||||
keys "github.com/tendermint/go-keys"
|
||||
"github.com/tendermint/go-keys/cryptostore"
|
||||
"github.com/tendermint/go-keys/storage/filestorage"
|
||||
)
|
||||
|
||||
const KeySubdir = "keys"
|
||||
|
||||
var (
|
||||
rootDir string
|
||||
output string
|
||||
keyDir string
|
||||
manager keys.Manager
|
||||
Manager keys.Manager
|
||||
)
|
||||
|
||||
// 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
|
||||
used by light-clients, full nodes, or any other application that
|
||||
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
|
||||
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
|
||||
manager = cryptostore.New(
|
||||
Manager = cryptostore.New(
|
||||
cryptostore.SecretBox,
|
||||
filestorage.New(keyDir),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ func serveHTTP(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
router := mux.NewRouter()
|
||||
ks := server.New(manager, viper.GetString("type"))
|
||||
ks := server.New(Manager, viper.GetString("type"))
|
||||
ks.Register(router)
|
||||
|
||||
// only set cors for tcp listener
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ func updatePassword(cmd *cobra.Command, args []string) {
|
||||
return
|
||||
}
|
||||
|
||||
err = manager.Update(name, oldpass, newpass)
|
||||
err = Manager.Update(name, oldpass, newpass)
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
} else {
|
||||
|
||||
+3
-2
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/bgentry/speakeasy"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/viper"
|
||||
data "github.com/tendermint/go-data"
|
||||
keys "github.com/tendermint/go-keys"
|
||||
)
|
||||
@@ -39,7 +40,7 @@ func getCheckPassword(prompt, prompt2 string) (string, error) {
|
||||
}
|
||||
|
||||
func printInfo(info keys.Info) {
|
||||
switch output {
|
||||
switch viper.Get(OutputFlag) {
|
||||
case "text":
|
||||
addr, err := data.ToText(info.Address)
|
||||
if err != nil {
|
||||
@@ -60,7 +61,7 @@ func printInfo(info keys.Info) {
|
||||
}
|
||||
|
||||
func printInfos(infos keys.Infos) {
|
||||
switch output {
|
||||
switch viper.Get(OutputFlag) {
|
||||
case "text":
|
||||
fmt.Println("All keys:")
|
||||
for _, i := range infos {
|
||||
|
||||
Reference in New Issue
Block a user