diff --git a/cmd/tendermint/commands/root.go b/cmd/tendermint/commands/root.go index a54b50069..e6a175665 100644 --- a/cmd/tendermint/commands/root.go +++ b/cmd/tendermint/commands/root.go @@ -18,7 +18,11 @@ var ( ) func init() { - RootCmd.PersistentFlags().String("log_level", config.LogLevel, "Log level") + registerFlagsRootCmd(RootCmd) +} + +func registerFlagsRootCmd(cmd *cobra.Command) { + cmd.PersistentFlags().String("log_level", config.LogLevel, "Log level") } // ParseConfig retrieves the default environment configuration, diff --git a/cmd/tendermint/commands/root_test.go b/cmd/tendermint/commands/root_test.go index 8217ee166..59d258af7 100644 --- a/cmd/tendermint/commands/root_test.go +++ b/cmd/tendermint/commands/root_test.go @@ -1,7 +1,10 @@ package commands import ( + "fmt" + "io/ioutil" "os" + "path/filepath" "strconv" "testing" @@ -12,6 +15,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tmlibs/cli" + cmn "github.com/tendermint/tmlibs/common" ) var ( @@ -22,89 +26,151 @@ const ( rootName = "root" ) -// isolate provides a clean setup and returns a copy of RootCmd you can -// modify in the test cases. -// NOTE: it unsets all TM* env variables. -func isolate(cmds ...*cobra.Command) cli.Executable { +// clearConfig clears env vars, the given root dir, and resets viper. +func clearConfig(dir string) { if err := os.Unsetenv("TMHOME"); err != nil { panic(err) } if err := os.Unsetenv("TM_HOME"); err != nil { panic(err) } - if err := os.RemoveAll(defaultRoot); err != nil { + + if err := os.RemoveAll(dir); err != nil { panic(err) } - viper.Reset() config = cfg.DefaultConfig() - r := &cobra.Command{ - Use: rootName, +} + +// prepare new rootCmd +func testRootCmd() *cobra.Command { + rootCmd := &cobra.Command{ + Use: RootCmd.Use, PersistentPreRunE: RootCmd.PersistentPreRunE, + Run: func(cmd *cobra.Command, args []string) {}, } - r.AddCommand(cmds...) - wr := cli.PrepareBaseCmd(r, "TM", defaultRoot) - return wr + registerFlagsRootCmd(rootCmd) + var l string + rootCmd.PersistentFlags().String("log", l, "Log") + return rootCmd } -func TestRootConfig(t *testing.T) { - assert, require := assert.New(t), require.New(t) +func testSetup(rootDir string, args []string, env map[string]string) error { + clearConfig(defaultRoot) - // we pre-create a config file we can refer to in the rest of - // the test cases. - cvals := map[string]string{ - "moniker": "monkey", - "fast_sync": "false", + rootCmd := testRootCmd() + cmd := cli.PrepareBaseCmd(rootCmd, "TM", defaultRoot) + + // run with the args and env + args = append([]string{rootCmd.Use}, args...) + return cli.RunWithArgs(cmd, args, env) +} + +func TestRootHome(t *testing.T) { + newRoot := filepath.Join(defaultRoot, "something-else") + cases := []struct { + args []string + env map[string]string + root string + }{ + {nil, nil, defaultRoot}, + {[]string{"--home", newRoot}, nil, newRoot}, + {nil, map[string]string{"TMHOME": newRoot}, newRoot}, + } + + for i, tc := range cases { + idxString := strconv.Itoa(i) + + err := testSetup(defaultRoot, tc.args, tc.env) + require.Nil(t, err, idxString) + + assert.Equal(t, tc.root, config.RootDir, idxString) + assert.Equal(t, tc.root, config.P2P.RootDir, idxString) + assert.Equal(t, tc.root, config.Consensus.RootDir, idxString) + assert.Equal(t, tc.root, config.Mempool.RootDir, idxString) } - // proper types of the above settings - cfast := false - conf, err := cli.WriteDemoConfig(cvals) - require.Nil(err) +} + +func TestRootFlagsEnv(t *testing.T) { + // defaults defaults := cfg.DefaultConfig() - dmax := defaults.P2P.MaxNumPeers + defaultLogLvl := defaults.LogLevel cases := []struct { args []string env map[string]string - root string - moniker string - fastSync bool - maxPeer int + logLevel string }{ - {nil, nil, defaultRoot, defaults.Moniker, defaults.FastSync, dmax}, - // try multiple ways of setting root (two flags, cli vs. env) - {[]string{"--home", conf}, nil, conf, cvals["moniker"], cfast, dmax}, - {nil, map[string]string{"TMHOME": conf}, conf, cvals["moniker"], cfast, dmax}, - // check setting p2p subflags two different ways - {[]string{"--p2p.max_num_peers", "420"}, nil, defaultRoot, defaults.Moniker, defaults.FastSync, 420}, - {nil, map[string]string{"TM_P2P_MAX_NUM_PEERS": "17"}, defaultRoot, defaults.Moniker, defaults.FastSync, 17}, - // try to set env that have no flags attached... - {[]string{"--home", conf}, map[string]string{"TM_MONIKER": "funny"}, conf, "funny", cfast, dmax}, + {[]string{"--log", "debug"}, nil, defaultLogLvl}, // wrong flag + {[]string{"--log_level", "debug"}, nil, "debug"}, // right flag + {nil, map[string]string{"TM_LOW": "debug"}, defaultLogLvl}, // wrong env flag + {nil, map[string]string{"MT_LOG_LEVEL": "debug"}, defaultLogLvl}, // wrong env prefix + {nil, map[string]string{"TM_LOG_LEVEL": "debug"}, "debug"}, // right env } - for idx, tc := range cases { - i := strconv.Itoa(idx) - // test command that does nothing, except trigger unmarshalling in root - noop := &cobra.Command{ - Use: "noop", - RunE: func(cmd *cobra.Command, args []string) error { - return nil - }, - } - noop.Flags().Int("p2p.max_num_peers", defaults.P2P.MaxNumPeers, "") - cmd := isolate(noop) - - args := append([]string{rootName, noop.Use}, tc.args...) - err := cli.RunWithArgs(cmd, args, tc.env) - require.Nil(err, i) - assert.Equal(tc.root, config.RootDir, i) - assert.Equal(tc.root, config.P2P.RootDir, i) - assert.Equal(tc.root, config.Consensus.RootDir, i) - assert.Equal(tc.root, config.Mempool.RootDir, i) - assert.Equal(tc.moniker, config.Moniker, i) - assert.Equal(tc.fastSync, config.FastSync, i) - assert.Equal(tc.maxPeer, config.P2P.MaxNumPeers, i) + for i, tc := range cases { + idxString := strconv.Itoa(i) + + err := testSetup(defaultRoot, tc.args, tc.env) + require.Nil(t, err, idxString) + + assert.Equal(t, tc.logLevel, config.LogLevel, idxString) } +} +func TestRootConfig(t *testing.T) { + + // write non-default config + nonDefaultLogLvl := "abc:debug" + cvals := map[string]string{ + "log_level": nonDefaultLogLvl, + } + + cases := []struct { + args []string + env map[string]string + + logLvl string + }{ + {nil, nil, nonDefaultLogLvl}, // should load config + {[]string{"--log_level=abc:info"}, nil, "abc:info"}, // flag over rides + {nil, map[string]string{"TM_LOG_LEVEL": "abc:info"}, "abc:info"}, // env over rides + } + + for i, tc := range cases { + idxString := strconv.Itoa(i) + clearConfig(defaultRoot) + + // XXX: path must match cfg.defaultConfigPath + configFilePath := filepath.Join(defaultRoot, "config") + err := cmn.EnsureDir(configFilePath, 0700) + require.Nil(t, err) + + // write the non-defaults to a different path + // TODO: support writing sub configs so we can test that too + err = WriteConfigVals(configFilePath, cvals) + require.Nil(t, err) + + rootCmd := testRootCmd() + cmd := cli.PrepareBaseCmd(rootCmd, "TM", defaultRoot) + + // run with the args and env + tc.args = append([]string{rootCmd.Use}, tc.args...) + err = cli.RunWithArgs(cmd, tc.args, tc.env) + require.Nil(t, err, idxString) + + assert.Equal(t, tc.logLvl, config.LogLevel, idxString) + } +} + +// WriteConfigVals writes a toml file with the given values. +// It returns an error if writing was impossible. +func WriteConfigVals(dir string, vals map[string]string) error { + data := "" + for k, v := range vals { + data = data + fmt.Sprintf("%s = \"%s\"\n", k, v) + } + cfile := filepath.Join(dir, "config.toml") + return ioutil.WriteFile(cfile, []byte(data), 0666) } diff --git a/config/config.go b/config/config.go index f93b79241..9103de103 100644 --- a/config/config.go +++ b/config/config.go @@ -7,11 +7,12 @@ import ( "time" ) -// Note: Most of the structs & relevant comments + the +// NOTE: Most of the structs & relevant comments + the // default configuration options were used to manually // generate the config.toml. Please reflect any changes // made here in the defaultConfigTemplate constant in // config/toml.go +// NOTE: tmlibs/cli must know to look in the config dir! var ( DefaultTendermintDir = ".tendermint" defaultConfigDir = "config" diff --git a/config/toml.go b/config/toml.go index e7bd26103..bdc9f5a6c 100644 --- a/config/toml.go +++ b/config/toml.go @@ -20,6 +20,8 @@ func init() { /****** these are for production settings ***********/ +// EnsureRoot creates the root, config, and data directories if they don't exist, +// and panics if it fails. func EnsureRoot(rootDir string) { if err := cmn.EnsureDir(rootDir, 0700); err != nil { cmn.PanicSanity(err.Error()) diff --git a/glide.lock b/glide.lock index b83358864..383772f05 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 072c8e685dd519c1f509da67379b70451a681bf3ef6cbd82900a1f68c55bbe16 -updated: 2017-12-29T11:08:17.355999228-05:00 +hash: 9399a10e80d255104f8ec07b5d495c41d8a3f7a421f9da97ebd78c65189f381d +updated: 2018-01-18T23:11:10.703734578-05:00 imports: - name: github.com/btcsuite/btcd version: 2e60448ffcc6bf78332d1fe590260095f554dd78 @@ -10,7 +10,7 @@ imports: - name: github.com/fsnotify/fsnotify version: 4da3e2cfbabc9f751898f250b49f2439785783a1 - name: github.com/go-kit/kit - version: 953e747656a7bbb5e1f998608b460458958b70cc + version: 53f10af5d5c7375d4655a3d6852457ed17ab5cc7 subpackages: - log - log/level @@ -68,13 +68,13 @@ imports: - name: github.com/mitchellh/mapstructure version: 06020f85339e21b2478f756a78e295255ffa4d6a - name: github.com/pelletier/go-toml - version: 0131db6d737cfbbfb678f8b7d92e55e27ce46224 + version: 4e9e0ee19b60b13eb79915933f44d8ed5f268bdd - name: github.com/pkg/errors version: 645ef00459ed84a119197bfb8d8205042c6df63d - name: github.com/rcrowley/go-metrics version: e181e095bae94582363434144c61a9653aff6e50 - name: github.com/spf13/afero - version: 57afd63c68602b63ed976de00dd066ccb3c319db + version: 8d919cbe7e2627e417f3e45c3c0e489a5b7e2536 subpackages: - mem - name: github.com/spf13/cast @@ -88,7 +88,7 @@ imports: - name: github.com/spf13/viper version: 25b30aa063fc18e48662b86996252eabdcf2f0c7 - name: github.com/syndtr/goleveldb - version: 34011bf325bce385408353a30b101fe5e923eb6e + version: adf24ef3f94bd13ec4163060b21a5678f22b429b subpackages: - leveldb - leveldb/cache @@ -129,7 +129,7 @@ imports: subpackages: - iavl - name: github.com/tendermint/tmlibs - version: 91b4b534ad78e442192c8175db92a06a51064064 + version: 15e51fa76086a3c505f227679c2478043ae7261b subpackages: - autofile - cli @@ -144,7 +144,7 @@ imports: - pubsub/query - test - name: golang.org/x/crypto - version: 95a4943f35d008beabde8c11e5075a1b714e6419 + version: 94eea52f7b742c7cbe0b03b22f0c4c8631ece122 subpackages: - curve25519 - nacl/box @@ -165,11 +165,11 @@ imports: - lex/httplex - trace - name: golang.org/x/sys - version: 83801418e1b59fb1880e363299581ee543af32ca + version: 8b4580aae2a0dd0c231a45d3ccb8434ff533b840 subpackages: - unix - name: golang.org/x/text - version: e19ae1496984b1c655b8044a65c0300a3c878dd3 + version: 57961680700a5336d15015c8c50686ca5ba362a4 subpackages: - secure/bidirule - transform @@ -199,7 +199,7 @@ imports: - tap - transport - name: gopkg.in/go-playground/validator.v9 - version: b1f51f36f1c98cc97f777d6fc9d4b05eaa0cabb5 + version: 61caf9d3038e1af346dbf5c2e16f6678e1548364 - name: gopkg.in/yaml.v2 version: 287cf08546ab5e7e37d55a84f7ed3fd1db036de5 testImports: diff --git a/glide.yaml b/glide.yaml index b7846d641..2302a6dcf 100644 --- a/glide.yaml +++ b/glide.yaml @@ -34,7 +34,7 @@ import: subpackages: - iavl - package: github.com/tendermint/tmlibs - version: v0.6.0 + version: v0.6.1 subpackages: - autofile - cli