<!-- < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < ☺ v ✰ Thanks for creating a PR! ✰ v Before smashing the submit button please review the checkboxes. v If a checkbox is n/a - please still include it but + a little note why ☺ > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > --> ## Description <!-- Add a description of the changes that this PR introduces and the files that are the most critical to review. --> closes: #9106 --- Before we can merge this PR, please make sure that all the following items have been checked off. If any of the checklist items are not applicable, please leave them but write a little note why. - [x] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). - [ ] Wrote unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) - [ ] Updated relevant documentation (`docs/`) or specification (`x/<module>/spec/`) - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` - [ ] Re-reviewed `Files changed` in the Github PR explorer - [ ] Review `Codecov Report` in the comment section below once CI passes
440 lines
13 KiB
Go
440 lines
13 KiB
Go
package server_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/cosmos/cosmos-sdk/client"
|
|
"github.com/cosmos/cosmos-sdk/client/flags"
|
|
"github.com/cosmos/cosmos-sdk/server"
|
|
"github.com/cosmos/cosmos-sdk/server/config"
|
|
"github.com/cosmos/cosmos-sdk/simapp"
|
|
genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
|
|
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
|
|
)
|
|
|
|
var cancelledInPreRun = errors.New("Cancelled in prerun")
|
|
|
|
// Used in each test to run the function under test via Cobra
|
|
// but to always halt the command
|
|
func preRunETestImpl(cmd *cobra.Command, args []string) error {
|
|
err := server.InterceptConfigsPreRunHandler(cmd, "", nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return cancelledInPreRun
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerCreatesConfigFilesWhenMissing(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
cmd := server.StartCmd(nil, "/foobar")
|
|
if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
|
|
t.Fatalf("Could not set home flag [%T] %v", err, err)
|
|
}
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
if err := cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
// Test that config.toml is created
|
|
configTomlPath := path.Join(tempDir, "config", "config.toml")
|
|
s, err := os.Stat(configTomlPath)
|
|
if err != nil {
|
|
t.Fatalf("Could not stat config.toml after run %v", err)
|
|
}
|
|
|
|
if !s.Mode().IsRegular() {
|
|
t.Fatal("config.toml not created as regular file")
|
|
}
|
|
|
|
if s.Size() == 0 {
|
|
t.Fatal("config.toml created as empty file")
|
|
}
|
|
|
|
// Test that tendermint config is initialized
|
|
if serverCtx.Config == nil {
|
|
t.Fatal("tendermint config not created")
|
|
}
|
|
|
|
// Test that app.toml is created
|
|
appTomlPath := path.Join(tempDir, "config", "app.toml")
|
|
s, err = os.Stat(appTomlPath)
|
|
if err != nil {
|
|
t.Fatalf("Could not stat app.toml after run %v", err)
|
|
}
|
|
|
|
if !s.Mode().IsRegular() {
|
|
t.Fatal("appp.toml not created as regular file")
|
|
}
|
|
|
|
if s.Size() == 0 {
|
|
t.Fatal("config.toml created as empty file")
|
|
}
|
|
|
|
// Test that the config for use in server/start.go is created
|
|
if serverCtx.Viper == nil {
|
|
t.Error("app config Viper instance not created")
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerReadsConfigToml(t *testing.T) {
|
|
const testDbBackend = "awesome_test_db"
|
|
tempDir := t.TempDir()
|
|
err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("creating config dir failed: %v", err)
|
|
}
|
|
configTomlPath := path.Join(tempDir, "config", "config.toml")
|
|
writer, err := os.Create(configTomlPath)
|
|
if err != nil {
|
|
t.Fatalf("creating config.toml file failed: %v", err)
|
|
}
|
|
|
|
_, err = writer.WriteString(fmt.Sprintf("db_backend = '%s'\n", testDbBackend))
|
|
if err != nil {
|
|
t.Fatalf("Failed writing string to config.toml: %v", err)
|
|
}
|
|
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("Failed closing config.toml: %v", err)
|
|
}
|
|
|
|
cmd := server.StartCmd(nil, "/foobar")
|
|
if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
|
|
t.Fatalf("Could not set home flag [%T] %v", err, err)
|
|
}
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if testDbBackend != serverCtx.Config.DBBackend {
|
|
t.Error("DBPath was not set from config.toml")
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerReadsAppToml(t *testing.T) {
|
|
const testHaltTime = 1337
|
|
tempDir := t.TempDir()
|
|
err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("creating config dir failed: %v", err)
|
|
}
|
|
appTomlPath := path.Join(tempDir, "config", "app.toml")
|
|
writer, err := os.Create(appTomlPath)
|
|
if err != nil {
|
|
t.Fatalf("creating app.toml file failed: %v", err)
|
|
}
|
|
|
|
_, err = writer.WriteString(fmt.Sprintf("halt-time = %d\n", testHaltTime))
|
|
if err != nil {
|
|
t.Fatalf("Failed writing string to app.toml: %v", err)
|
|
}
|
|
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("Failed closing app.toml: %v", err)
|
|
}
|
|
cmd := server.StartCmd(nil, tempDir)
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if testHaltTime != serverCtx.Viper.GetInt("halt-time") {
|
|
t.Error("Halt time was not set from app.toml")
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerReadsFlags(t *testing.T) {
|
|
const testAddr = "tcp://127.1.2.3:12345"
|
|
tempDir := t.TempDir()
|
|
cmd := server.StartCmd(nil, "/foobar")
|
|
|
|
if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
|
|
t.Fatalf("Could not set home flag [%T] %v", err, err)
|
|
}
|
|
|
|
// This flag is added by tendermint
|
|
if err := cmd.Flags().Set("rpc.laddr", testAddr); err != nil {
|
|
t.Fatalf("Could not set address flag [%T] %v", err, err)
|
|
}
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if testAddr != serverCtx.Config.RPC.ListenAddress {
|
|
t.Error("RPCListenAddress was not set from command flags")
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerReadsEnvVars(t *testing.T) {
|
|
const testAddr = "tcp://127.1.2.3:12345"
|
|
tempDir := t.TempDir()
|
|
cmd := server.StartCmd(nil, "/foobar")
|
|
if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
|
|
t.Fatalf("Could not set home flag [%T] %v", err, err)
|
|
}
|
|
|
|
executableName, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatalf("Could not get executable name: %v", err)
|
|
}
|
|
basename := path.Base(executableName)
|
|
basename = strings.ReplaceAll(basename, ".", "_")
|
|
// This is added by tendermint
|
|
envVarName := fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename))
|
|
os.Setenv(envVarName, testAddr)
|
|
t.Cleanup(func() {
|
|
os.Unsetenv(envVarName)
|
|
})
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if testAddr != serverCtx.Config.RPC.ListenAddress {
|
|
t.Errorf("RPCListenAddress was not set from env. var. %q", envVarName)
|
|
}
|
|
}
|
|
|
|
/*
|
|
The following tests are here to check the precedence of each
|
|
of the configuration sources. A common setup functionality is used
|
|
to avoid duplication of code between tests.
|
|
*/
|
|
|
|
var (
|
|
TestAddrExpected = "tcp://127.126.125.124:12345" // expected to be used in test
|
|
TestAddrNotExpected = "tcp://127.127.127.127:11111" // not expected to be used in test
|
|
)
|
|
|
|
type precedenceCommon struct {
|
|
envVarName string
|
|
flagName string
|
|
configTomlPath string
|
|
|
|
cmd *cobra.Command
|
|
}
|
|
|
|
func newPrecedenceCommon(t *testing.T) precedenceCommon {
|
|
retval := precedenceCommon{}
|
|
|
|
// Determine the env. var. name based off the executable name
|
|
executableName, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatalf("Could not get executable name: %v", err)
|
|
}
|
|
basename := path.Base(executableName)
|
|
basename = strings.ReplaceAll(basename, ".", "_")
|
|
basename = strings.ReplaceAll(basename, "-", "_")
|
|
// Store the name of the env. var.
|
|
retval.envVarName = fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename))
|
|
|
|
// Store the flag name. This flag is added by tendermint
|
|
retval.flagName = "rpc.laddr"
|
|
|
|
// Create a tempdir and create './config' under that
|
|
tempDir := t.TempDir()
|
|
err = os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("creating config dir failed: %v", err)
|
|
}
|
|
// Store the path for config.toml
|
|
retval.configTomlPath = path.Join(tempDir, "config", "config.toml")
|
|
|
|
// always remove the env. var. after each test execution
|
|
t.Cleanup(func() {
|
|
// This should not fail but if it does just panic
|
|
if err := os.Unsetenv(retval.envVarName); err != nil {
|
|
panic("Could not clear configuration env. var. used in test")
|
|
}
|
|
})
|
|
|
|
// Set up the command object that is used in this test
|
|
retval.cmd = server.StartCmd(nil, tempDir)
|
|
retval.cmd.PreRunE = preRunETestImpl
|
|
|
|
return retval
|
|
}
|
|
|
|
func (v precedenceCommon) setAll(t *testing.T, setFlag *string, setEnvVar *string, setConfigFile *string) {
|
|
if setFlag != nil {
|
|
if err := v.cmd.Flags().Set(v.flagName, *setFlag); err != nil {
|
|
t.Fatalf("Failed setting flag %q", v.flagName)
|
|
}
|
|
}
|
|
|
|
if setEnvVar != nil {
|
|
os.Setenv(v.envVarName, *setEnvVar)
|
|
}
|
|
|
|
if setConfigFile != nil {
|
|
writer, err := os.Create(v.configTomlPath)
|
|
if err != nil {
|
|
t.Fatalf("creating config.toml file failed: %v", err)
|
|
}
|
|
|
|
_, err = writer.WriteString(fmt.Sprintf("[rpc]\nladdr = \"%s\"\n", *setConfigFile))
|
|
if err != nil {
|
|
t.Fatalf("Failed writing string to config.toml: %v", err)
|
|
}
|
|
|
|
if err := writer.Close(); err != nil {
|
|
t.Fatalf("Failed closing config.toml: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerPrecedenceFlag(t *testing.T) {
|
|
testCommon := newPrecedenceCommon(t)
|
|
testCommon.setAll(t, &TestAddrExpected, &TestAddrNotExpected, &TestAddrNotExpected)
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := testCommon.cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
|
|
t.Fatalf("RPCListenAddress was not set from flag %q", testCommon.flagName)
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerPrecedenceEnvVar(t *testing.T) {
|
|
testCommon := newPrecedenceCommon(t)
|
|
testCommon.setAll(t, nil, &TestAddrExpected, &TestAddrNotExpected)
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := testCommon.cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
|
|
t.Errorf("RPCListenAddress was not set from env. var. %q", testCommon.envVarName)
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerPrecedenceConfigFile(t *testing.T) {
|
|
testCommon := newPrecedenceCommon(t)
|
|
testCommon.setAll(t, nil, nil, &TestAddrExpected)
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := testCommon.cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
|
|
t.Errorf("RPCListenAddress was not read from file %q", testCommon.configTomlPath)
|
|
}
|
|
}
|
|
|
|
func TestInterceptConfigsPreRunHandlerPrecedenceConfigDefault(t *testing.T) {
|
|
testCommon := newPrecedenceCommon(t)
|
|
// Do not set anything
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
|
|
if err := testCommon.cmd.ExecuteContext(ctx); err != cancelledInPreRun {
|
|
t.Fatalf("function failed with [%T] %v", err, err)
|
|
}
|
|
|
|
if "tcp://127.0.0.1:26657" != serverCtx.Config.RPC.ListenAddress {
|
|
t.Error("RPCListenAddress is not using default")
|
|
}
|
|
}
|
|
|
|
// Ensure that if interceptConfigs encounters any error other than non-existen errors
|
|
// that we correctly return the offending error, for example a permission error.
|
|
// See https://github.com/cosmos/cosmos-sdk/issues/7578
|
|
func TestInterceptConfigsWithBadPermissions(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
subDir := filepath.Join(tempDir, "nonPerms")
|
|
if err := os.Mkdir(subDir, 0600); err != nil {
|
|
t.Fatalf("Failed to create sub directory: %v", err)
|
|
}
|
|
cmd := server.StartCmd(nil, "/foobar")
|
|
if err := cmd.Flags().Set(flags.FlagHome, subDir); err != nil {
|
|
t.Fatalf("Could not set home flag [%T] %v", err, err)
|
|
}
|
|
|
|
cmd.PreRunE = preRunETestImpl
|
|
|
|
serverCtx := &server.Context{}
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
if err := cmd.ExecuteContext(ctx); !os.IsPermission(err) {
|
|
t.Fatalf("Failed to catch permissions error, got: [%T] %v", err, err)
|
|
}
|
|
}
|
|
|
|
func TestEmptyMinGasPrices(t *testing.T) {
|
|
tempDir := t.TempDir()
|
|
err := os.Mkdir(filepath.Join(tempDir, "config"), os.ModePerm)
|
|
require.NoError(t, err)
|
|
encCfg := simapp.MakeTestEncodingConfig()
|
|
|
|
// Run InitCmd to create necessary config files.
|
|
clientCtx := client.Context{}.WithHomeDir(tempDir).WithJSONCodec(encCfg.Marshaler)
|
|
serverCtx := server.NewDefaultContext()
|
|
ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
|
|
ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
|
|
cmd := genutilcli.InitCmd(simapp.ModuleBasics, tempDir)
|
|
cmd.SetArgs([]string{"appnode-test"})
|
|
err = cmd.ExecuteContext(ctx)
|
|
require.NoError(t, err)
|
|
|
|
// Modify app.toml.
|
|
appCfgTempFilePath := filepath.Join(tempDir, "config", "app.toml")
|
|
appConf := config.DefaultConfig()
|
|
appConf.BaseConfig.MinGasPrices = ""
|
|
config.WriteConfigFile(appCfgTempFilePath, appConf)
|
|
|
|
// Run StartCmd.
|
|
cmd = server.StartCmd(nil, tempDir)
|
|
cmd.PreRunE = func(cmd *cobra.Command, _ []string) error {
|
|
return server.InterceptConfigsPreRunHandler(cmd, "", nil)
|
|
}
|
|
err = cmd.ExecuteContext(ctx)
|
|
require.Errorf(t, err, sdkerrors.ErrAppConfig.Error())
|
|
}
|