Skip to content

Commit

Permalink
Fix gaiacli config and make it non-interactive only
Browse files Browse the repository at this point in the history
Closes: #2734
  • Loading branch information
Alessio Treglia committed Dec 4, 2018
1 parent d8fbae6 commit 7b94da2
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 106 deletions.
9 changes: 0 additions & 9 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 0 additions & 4 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,6 @@
name = "github.com/rakyll/statik"
version = "=v0.1.4"

[[constraint]]
name = "github.com/mitchellh/go-homedir"
version = "1.0.0"

## transitive deps, without releases:

[[override]]
Expand Down
1 change: 1 addition & 0 deletions PENDING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ BREAKING CHANGES
* Gaia CLI (`gaiacli`)
* [cli] [\#2595](https://github.com/cosmos/cosmos-sdk/issues/2595) Remove `keys new` in favor of `keys add` incorporating existing functionality with addition of key recovery functionality.
* [cli] [\#2971](https://github.com/cosmos/cosmos-sdk/pull/2971) Additional verification when running `gaiad gentx`
* [cli] [\#2734](https://github.com/cosmos/cosmos-sdk/issues/2734) Rewrite `gaiacli config`. It is now a non-interactive config utility.

* Gaia
- [#128](https://github.com/tendermint/devops/issues/128) Updated CircleCI job to trigger website build on every push to master/develop.
Expand Down
173 changes: 103 additions & 70 deletions client/config.go
Original file line number Diff line number Diff line change
@@ -1,131 +1,164 @@
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"
)

const (
flagGet = "get"
flagList = "list"
)

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"`
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.MaximumNArgs(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")
cmd.Flags().BoolP(flagList, "l", false,
"list all variables set in the config file, along with their values")
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, listAction := viper.GetBool(flagGet), viper.GetBool(flagList)
// Args validation
if listAction && getAction {
return fmt.Errorf("only one action at a time")
}

gaiaCLIHome, err := handleGaiaCLIHome(home, stdin)
if err != nil {
return err
if (listAction && len(args) != 0) ||
(getAction && len(args) != 1) || len(args) != 2 {
return fmt.Errorf("wrong number of arguments")
}

node, err := handleNode(stdin)
// Load configuration
tree, err := loadConfigFile(cfgFile)
if err != nil {
return err
}

trustNode, err := handleTrustNode(stdin)
if err != nil {
return err
// List action
if listAction {
s, err := tree.ToTomlString()
if err != nil {
return err
}
fmt.Print(s)
return nil
}

chainID, err := types.DefaultChainID()
key := args[0]
// Get value action
if getAction {
switch key {
case "chain_id", "output", "node":
defaultValue := configDefaults[key]
fmt.Println(tree.GetDefault(key, defaultValue).(string))
case "trace", "trust_node":
fmt.Println(tree.GetDefault(key, false).(bool))
default:
return errUnknownConfigKey(key)
}
return nil
}

if err != nil {
fmt.Println("Couldn't populate ChainID, so using an empty one.")
// 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)
}

cfg := &cliConfig{
Home: gaiaCLIHome,
ChainID: chainID,
TrustNode: trustNode,
Output: "text",
Node: node,
Trace: false,
// Save configuration to disk
if err := saveConfigFile(cfgFile, tree); err != nil {
return err
}
fmt.Fprintf(os.Stderr, "configuration saved to %q\n", cfgFile)

return createGaiaCLIConfig(cfg)
return nil
}

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 {
func ensureConfFile(rootDir string) (string, error) {
cfgPath := path.Join(rootDir, "config")
if err := os.MkdirAll(cfgPath, os.ModePerm); err != nil {
return "", err
}

if home == "" {
home = path.Join(dir, dirName)
}

return home, nil
return path.Join(cfgPath, "config.toml"), 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
func loadConfigFile(cfgFile string) (*toml.Tree, error) {
if _, err := os.Stat(cfgFile); os.IsNotExist(err) {
return toml.Load(``)
}

if node == "" {
node = defaultNode
bz, err := ioutil.ReadFile(cfgFile)
if err != nil {
return nil, err
}

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)
tree, err := toml.LoadBytes(bz)
if err != nil {
return err
return nil, err
}

data, err := toml.Marshal(*cfg)
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()

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"))
if err != nil {
return err
}
}
_, err = tree.WriteTo(fp)
return err
}

return ioutil.WriteFile(cfgFile, data, os.ModePerm)
func errUnknownConfigKey(key string) error {
return fmt.Errorf("unknown configuration key: %q", key)
}
30 changes: 7 additions & 23 deletions cmd/gaia/cli_test/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -563,36 +563,20 @@ func TestGaiaCLIConfig(t *testing.T) {
t.Parallel()
chainID, servAddr, port, gaiadHome, gaiacliHome, _ := initializeFixtures(t)
node := fmt.Sprintf("%s:%s", servAddr, port)
executeWrite(t, fmt.Sprintf("gaiacli --home=%s config", gaiadHome), gaiacliHome, node, "y")
chainID := executeInit(t, fmt.Sprintf("gaiad init -o --moniker=foo --home=%s", gaiadHome))
executeWrite(t, fmt.Sprintf(`gaiacli --home=%s config node %s`, gaiacliHome, node))
executeWrite(t, fmt.Sprintf(`gaiacli --home=%s config output text`, gaiacliHome))
executeWrite(t, fmt.Sprintf(`gaiacli --home=%s config trust_node true`, gaiacliHome))
executeWrite(t, fmt.Sprintf(`gaiacli --home=%s config chain_id %s`, gaiacliHome, chainID))
executeWrite(t, fmt.Sprintf(`gaiacli --home=%s config trace false`, gaiacliHome))
config, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
require.NoError(t, err)
expectedConfig := fmt.Sprintf(`chain_id = "%s"
home = "%s"
node = "%s"
output = "text"
trace = false
trust_node = true
`, chainID, gaiacliHome, node)
require.Equal(t, expectedConfig, string(config))
// ensure a backup gets created
executeWrite(t, "gaiacli config", gaiacliHome, node, "y", "y")
configBackup, err := ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml-old"))
require.NoError(t, err)
require.Equal(t, expectedConfig, string(configBackup))

require.NoError(t, os.RemoveAll(gaiadHome))
executeWrite(t, "gaiacli config", gaiacliHome, node, "y")

// ensure it works without an initialized gaiad state
expectedConfig = fmt.Sprintf(`chain_id = ""
home = "%s"
node = "%s"
output = "text"
trace = false
trust_node = true
`, gaiacliHome, node)
config, err = ioutil.ReadFile(path.Join(gaiacliHome, "config", "config.toml"))
require.NoError(t, err)
`, chainID, node)
require.Equal(t, expectedConfig, string(config))
cleanupDirs(gaiadHome, gaiacliHome)
}
Expand Down

0 comments on commit 7b94da2

Please sign in to comment.