Merge PR #2972: Fix gaiacli config and make it non-interactive only

* Fix gaiacli config and make it non-interactive only

Closes: #2734

* Update cli tests

* Remove --list, by default and with no args print config

* Small improvements

* Warn user when file doesn't exist

* Fix integration tests
This commit is contained in:
Alessio Treglia
2018-12-04 14:17:45 +01:00
committed by Christopher Goes
parent d8fbae677f
commit d32e4a9fe0
5 changed files with 154 additions and 160 deletions
+119 -93
View File
@@ -1,131 +1,157 @@
package client
import (
"bufio"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"github.com/cosmos/cosmos-sdk/cmd/gaia/app"
"github.com/tendermint/tendermint/libs/cli"
"github.com/cosmos/cosmos-sdk/types"
"github.com/mitchellh/go-homedir"
"github.com/pelletier/go-toml"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
type cliConfig struct {
Home string `toml:"home"`
ChainID string `toml:"chain_id"`
TrustNode bool `toml:"trust_node"`
Output string `toml:"output"`
Node string `toml:"node"`
Trace bool `toml:"trace"`
const (
flagGet = "get"
)
var configDefaults map[string]string
func init() {
configDefaults = map[string]string{
"chain_id": "",
"output": "text",
"node": "tcp://localhost:26657",
}
}
// ConfigCmd returns a CLI command to interactively create a
// Gaia CLI config file.
func ConfigCmd() *cobra.Command {
cfg := &cobra.Command{
Use: "config",
Short: "Interactively creates a Gaia CLI config file",
cmd := &cobra.Command{
Use: "config <key> [value]",
Short: "Create or query a Gaia CLI configuration file",
RunE: runConfigCmd,
Args: cobra.RangeArgs(0, 2),
}
return cfg
cmd.Flags().String(cli.HomeFlag, app.DefaultCLIHome,
"set client's home directory for configuration")
cmd.Flags().Bool(flagGet, false,
"print configuration value or its default if unset")
return cmd
}
func runConfigCmd(cmd *cobra.Command, args []string) error {
home, err := homedir.Dir()
cfgFile, err := ensureConfFile(viper.GetString(cli.HomeFlag))
if err != nil {
return err
}
stdin := BufferStdin()
getAction := viper.GetBool(flagGet)
if getAction && len(args) != 1 {
return fmt.Errorf("wrong number of arguments")
}
gaiaCLIHome, err := handleGaiaCLIHome(home, stdin)
// Load configuration
tree, err := loadConfigFile(cfgFile)
if err != nil {
return err
}
node, err := handleNode(stdin)
if err != nil {
return err
}
trustNode, err := handleTrustNode(stdin)
if err != nil {
return err
}
chainID, err := types.DefaultChainID()
if err != nil {
fmt.Println("Couldn't populate ChainID, so using an empty one.")
}
cfg := &cliConfig{
Home: gaiaCLIHome,
ChainID: chainID,
TrustNode: trustNode,
Output: "text",
Node: node,
Trace: false,
}
return createGaiaCLIConfig(cfg)
}
func handleGaiaCLIHome(dir string, stdin *bufio.Reader) (string, error) {
dirName := ".gaiacli"
home, err := GetString(fmt.Sprintf("Where is your gaiacli home directory? (Default: ~/%s)", dirName), stdin)
if err != nil {
return "", err
}
if home == "" {
home = path.Join(dir, dirName)
}
return home, nil
}
func handleNode(stdin *bufio.Reader) (string, error) {
defaultNode := "tcp://localhost:26657"
node, err := GetString(fmt.Sprintf("Where is your validator node running? (Default: %s)", defaultNode), stdin)
if err != nil {
return "", err
}
if node == "" {
node = defaultNode
}
return node, nil
}
func handleTrustNode(stdin *bufio.Reader) (bool, error) {
return GetConfirmation("Do you trust this node?", stdin)
}
func createGaiaCLIConfig(cfg *cliConfig) error {
cfgPath := path.Join(cfg.Home, "config")
err := os.MkdirAll(cfgPath, os.ModePerm)
if err != nil {
return err
}
data, err := toml.Marshal(*cfg)
if err != nil {
return err
}
cfgFile := path.Join(cfgPath, "config.toml")
if info, err := os.Stat(cfgFile); err == nil && !info.IsDir() {
err = os.Rename(cfgFile, path.Join(cfgPath, "config.toml-old"))
// Print the config and exit
if len(args) == 0 {
s, err := tree.ToTomlString()
if err != nil {
return err
}
fmt.Print(s)
return nil
}
return ioutil.WriteFile(cfgFile, data, os.ModePerm)
key := args[0]
// Get value action
if getAction {
switch key {
case "trace", "trust_node":
fmt.Println(tree.GetDefault(key, false).(bool))
default:
if defaultValue, ok := configDefaults[key]; ok {
fmt.Println(tree.GetDefault(key, defaultValue).(string))
return nil
}
return errUnknownConfigKey(key)
}
return nil
}
// Set value action
value := args[1]
switch key {
case "chain_id", "output", "node":
tree.Set(key, value)
case "trace", "trust_node":
boolVal, err := strconv.ParseBool(value)
if err != nil {
return err
}
tree.Set(key, boolVal)
default:
return errUnknownConfigKey(key)
}
// Save configuration to disk
if err := saveConfigFile(cfgFile, tree); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "configuration saved to %s\n", cfgFile)
return nil
}
func ensureConfFile(rootDir string) (string, error) {
cfgPath := path.Join(rootDir, "config")
if err := os.MkdirAll(cfgPath, os.ModePerm); err != nil {
return "", err
}
return path.Join(cfgPath, "config.toml"), nil
}
func loadConfigFile(cfgFile string) (*toml.Tree, error) {
if _, err := os.Stat(cfgFile); os.IsNotExist(err) {
fmt.Fprintf(os.Stderr, "%s does not exist\n", cfgFile)
return toml.Load(``)
}
bz, err := ioutil.ReadFile(cfgFile)
if err != nil {
return nil, err
}
tree, err := toml.LoadBytes(bz)
if err != nil {
return nil, err
}
return tree, nil
}
func saveConfigFile(cfgFile string, tree *toml.Tree) error {
fp, err := os.OpenFile(cfgFile, os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
return err
}
defer fp.Close()
_, err = tree.WriteTo(fp)
return err
}
func errUnknownConfigKey(key string) error {
return fmt.Errorf("unknown configuration key: %q", key)
}