Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add client config #8820

Closed
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func ReadPersistentCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Cont
keyringBackend, _ := flagSet.GetString(flags.FlagKeyringBackend)

if keyringBackend != "" {
kr, err := newKeyringFromFlags(clientCtx, keyringBackend)
kr, err := NewKeyringFromFlags(clientCtx, keyringBackend)
if err != nil {
return clientCtx, err
}
Expand Down
119 changes: 119 additions & 0 deletions client/config/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package config

import (
"encoding/json"
"errors"
"fmt"
"path/filepath"

tmcli "github.com/tendermint/tendermint/libs/cli"

"github.com/spf13/cobra"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
)

// Cmd returns a CLI command to interactively create an application CLI
// config file.
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "config <key> [value]",
Short: "Create or query an application CLI configuration file",
RunE: runConfigCmd,
Args: cobra.RangeArgs(0, 2),
}
return cmd
}

func runConfigCmd(cmd *cobra.Command, args []string) error {

clientCtx := client.GetClientContextFromCmd(cmd)

configPath := filepath.Join(clientCtx.HomeDir, "config")

if err := ensureConfigPath(configPath); err != nil {
return fmt.Errorf("couldn't make client config: %v", err)
}

cliConfig, err := getClientConfig(configPath, clientCtx.Viper)
if err != nil {
return fmt.Errorf("couldn't get client config: %v", err)
}

switch len(args) {
case 0:
// print all client config fields to sdt out
s, _ := json.MarshalIndent(cliConfig, "", "\t")
cmd.Println(string(s))

case 1:
// it's a get
// TODO implement method for get
// should i implement getters here?
key := args[0]
switch key {
case flags.FlagChainID:
cmd.Println(cliConfig.ChainID)
case flags.FlagKeyringBackend:
cmd.Println(cliConfig.KeyringBackend)
case tmcli.OutputFlag:
cmd.Println(cliConfig.Output)
case flags.FlagNode:
cmd.Println(cliConfig.Node)
case flags.FlagBroadcastMode:
cmd.Println(cliConfig.BroadcastMode)
// case "trace":
// cmd.Println(cliConfig.Trace)
default:
err := errUnknownConfigKey(key)
return fmt.Errorf("couldn't get the value for the key: %v, error: %v", key, err)
}

case 2:
// it's set

key, value := args[0], args[1]

switch key {
case flags.FlagChainID:
cliConfig.SetChainID(value)
case flags.FlagKeyringBackend:
cliConfig.SetKeyringBackend(value)
case tmcli.OutputFlag:
cliConfig.SetOutput(value)
case flags.FlagNode:
cliConfig.SetNode(value)
case flags.FlagBroadcastMode:
cliConfig.SetBroadcastMode(value)
/*
case "trace":
if err := cliConfig.SetTrace(value); err != nil {
return fmt.Errorf("couldn't parse bool value: %v", err)
}
*/
default:
return errUnknownConfigKey(key)
}

configTemplate, err := initConfigTemplate()
if err != nil {
return fmt.Errorf("could not initiate config template: %v", err)
}

cliConfigFile := filepath.Join(configPath, "client.toml")
if err := writeConfigFile(cliConfigFile, cliConfig, configTemplate); err != nil {
return fmt.Errorf("could not write client config to the file: %v", err)
}

default:
// print error
return errors.New("cound not execute config command")
}

return nil
}

func errUnknownConfigKey(key string) error {
return fmt.Errorf("unknown configuration key: %q", key)
}
96 changes: 96 additions & 0 deletions client/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package config

import (
// "fmt"
"path/filepath"

"github.com/cosmos/cosmos-sdk/client"
)

// Default constants
const (
chainID = ""
keyringBackend = "os"
output = "text"
node = "tcp://localhost:26657"
broadcastMode = "sync"

// trace = false
)

type ClientConfig struct {
ChainID string `mapstructure:"chain-id" json:"chain-id"`
KeyringBackend string `mapstructure:"keyring-backend" json:"keyring-backend"`
Output string `mapstructure:"output" json:"output"`
Node string `mapstructure:"node" json:"node"`
BroadcastMode string `mapstructure:"broadcast-mode" json:"broadcast-mode"`
// Trace bool `mapstructure:"trace" json:"trace"`
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
}

func DefaultClientConfig() *ClientConfig {
return &ClientConfig{chainID, keyringBackend, output, node, broadcastMode}
}

func (c *ClientConfig) SetChainID(chainID string) {
c.ChainID = chainID
}

func (c *ClientConfig) SetKeyringBackend(keyringBackend string) {
c.KeyringBackend = keyringBackend
}

func (c *ClientConfig) SetOutput(output string) {
c.Output = output
}

func (c *ClientConfig) SetNode(node string) {
c.Node = node
}

func (c *ClientConfig) SetBroadcastMode(broadcastMode string) {
c.BroadcastMode = broadcastMode
}

/*
func (c *ClientConfig) SetTrace(trace string) error {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
boolVal, err := strconv.ParseBool(trace)
if err != nil {
return err
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alessio Following your suggestion, I print an error here to os.Stderr. I am unable to add os.Exit(1) cause the runConfigCmd is supposed to return an error.

}
c.Trace = boolVal
return nil
}
*/

func UpdateClientContextFromClientConfig(ctx client.Context) client.Context {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
configPath := filepath.Join(ctx.HomeDir, "config")

/*
if err := ensureConfigPath(configPath); err != nil {
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved
return ctx, fmt.Errorf("couldn't make client config: %v", err)
}

cliConfig, err := getClientConfig(configPath, ctx.Viper)
if err != nil {
return ctx, fmt.Errorf("couldn't get client config: %v", err)
}


keyRing, err := client.NewKeyringFromFlags(ctx, cliConfig.KeyringBackend)
if err != nil {
return ctx, fmt.Errorf("couldn't get key ring: %v", err)
}
*/

cliConfig, _ := getClientConfig(configPath, ctx.Viper)

keyRing, _ := client.NewKeyringFromFlags(ctx, cliConfig.KeyringBackend)
amaury1093 marked this conversation as resolved.
Show resolved Hide resolved

ctx = ctx.WithChainID(cliConfig.ChainID).
WithKeyring(keyRing).
WithOutputFormat(cliConfig.Output).
WithNodeURI(cliConfig.Node).
WithBroadcastMode(cliConfig.BroadcastMode)

return ctx
}
61 changes: 61 additions & 0 deletions client/config/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package config

import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var ErrWrongNumberOfArgs = errors.New("wrong number of arguments")

// For https://github.com/cosmos/cosmos-sdk/issues/3899
func Test_runConfigCmdTwiceWithShorterNodeValue(t *testing.T) {
// Prepare environment
t.Parallel()
cleanup := tmpDir(t)
defer cleanup()
_ = os.RemoveAll(filepath.Join("config"))

// Init command config
cmd := Cmd()
assert.NotNil(t, cmd)

err := cmd.RunE(cmd, []string{"node", "tcp://localhost:26657"})
assert.Nil(t, err)

err = cmd.RunE(cmd, []string{"node", "--get"})
assert.Nil(t, err)

err = cmd.RunE(cmd, []string{"node", "tcp://local:26657"})
assert.Nil(t, err)

err = cmd.RunE(cmd, []string{"node", "--get"})
assert.Nil(t, err)

err = cmd.RunE(cmd, nil)
assert.Nil(t, err)

err = cmd.RunE(cmd, []string{"invalidKey", "--get"})
require.Equal(t, err, errUnknownConfigKey("invalidKey"))

err = cmd.RunE(cmd, []string{"invalidArg1"})
require.Equal(t, err, ErrWrongNumberOfArgs)

err = cmd.RunE(cmd, []string{"invalidKey", "invalidValue"})
require.Equal(t, err, errUnknownConfigKey("invalidKey"))

// TODO add testing of pririty environmental variable, flag and file
// for now manual testign is ok

}

func tmpDir(t *testing.T) func() {
dir, err := ioutil.TempDir("", t.Name()+"_")
require.NoError(t, err)
return func() { _ = os.RemoveAll(dir) }
}
77 changes: 77 additions & 0 deletions client/config/toml.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package config

import (
"bytes"
"os"
"text/template"

"github.com/spf13/viper"
tmos "github.com/tendermint/tendermint/libs/os"
)

const defaultConfigTemplate = `# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml

###############################################################################
### Client Configuration ###
###############################################################################


chain-id = "{{ .ChainID }}"
keyring-backend = "{{ .KeyringBackend }}"
output = "{{ .Output }}"
node = "{{ .Node }}"
broadcast-mode = "{{ .BroadcastMode }}"
`

// InitConfigTemplate initiates config template that will be used in
// WriteConfigFile
func initConfigTemplate() (*template.Template, error) {
tmpl := template.New("clientConfigFileTemplate")
configTemplate, err := tmpl.Parse(defaultConfigTemplate)
if err != nil {
return nil, err
}

return configTemplate, nil
}

// writeConfigFile renders config using the template and writes it to
// configFilePath.
func writeConfigFile(cfgFile string, config *ClientConfig, configTemplate *template.Template) error {
var buffer bytes.Buffer

if err := configTemplate.Execute(&buffer, config); err != nil {
return err
}

tmos.MustWriteFile(cfgFile, buffer.Bytes(), 0644)
return nil

}

func ensureConfigPath(configPath string) error {
if err := os.MkdirAll(configPath, os.ModePerm); err != nil {
return err
}

return nil
}

func getClientConfig(configPath string, v *viper.Viper) (*ClientConfig, error) {

v.AddConfigPath(configPath)
v.SetConfigName("client")
v.SetConfigType("toml")

if err := v.ReadInConfig(); err != nil {
return nil, err
}

conf := DefaultClientConfig()
if err := v.Unmarshal(conf); err != nil {
return nil, err
}

return conf, nil
}
Loading