From c5df6a355a94d57bd3636c8b1c43a4d0283ce78e Mon Sep 17 00:00:00 2001 From: Chill Validation <92176880+chillyvee@users.noreply.github.com> Date: Wed, 12 Jul 2023 17:56:56 +0900 Subject: [PATCH] feat(cosmovisor): Cosmovisor upgrade plan without automatic lower case (#16919) --- tools/cosmovisor/CHANGELOG.md | 1 + tools/cosmovisor/README.md | 1 + tools/cosmovisor/args.go | 5 + tools/cosmovisor/args_test.go | 141 ++++++++++-------- .../cosmovisor/cmd/cosmovisor/add_upgrade.go | 9 +- tools/cosmovisor/process_test.go | 45 ++++++ tools/cosmovisor/scanner.go | 32 ++-- tools/cosmovisor/scanner_test.go | 18 ++- .../norecase/cosmovisor/genesis/bin/dummyd | 9 ++ .../cosmovisor/upgrades/Chain2/bin/dummyd | 6 + .../testdata/norecase/data/.gitkeep | 0 11 files changed, 187 insertions(+), 80 deletions(-) create mode 100755 tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd create mode 100755 tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd create mode 100644 tools/cosmovisor/testdata/norecase/data/.gitkeep diff --git a/tools/cosmovisor/CHANGELOG.md b/tools/cosmovisor/CHANGELOG.md index 7067ab11df..c8557e9833 100644 --- a/tools/cosmovisor/CHANGELOG.md +++ b/tools/cosmovisor/CHANGELOG.md @@ -45,6 +45,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Improvements +* [#16919](https://github.com/cosmos/cosmos-sdk/pull/16919) Add COSMOVISOR_DISABLE_RECASE to cosmovisor to disable automatic case change for plan name * [#14881](https://github.com/cosmos/cosmos-sdk/pull/14881) Refactor Cosmovisor to use `x/upgrade` validation logic. * [#14881](https://github.com/cosmos/cosmos-sdk/pull/14881) Refactor Cosmovisor to depend only on the `x/upgrade` module. * [#15362](https://github.com/cosmos/cosmos-sdk/pull/15362) Allow disabling Cosmovisor logs diff --git a/tools/cosmovisor/README.md b/tools/cosmovisor/README.md index 013a7cd466..46a9d1159d 100644 --- a/tools/cosmovisor/README.md +++ b/tools/cosmovisor/README.md @@ -98,6 +98,7 @@ Use of `cosmovisor` without one of the action arguments is deprecated. For backw * `COSMOVISOR_COLOR_LOGS` (defaults to `true`). If set to true, this will colorise Cosmovisor logs (but not the underlying process). * `COSMOVISOR_TIMEFORMAT_LOGS` (defaults to `kitchen`). If set to a value (`layout|ansic|unixdate|rubydate|rfc822|rfc822z|rfc850|rfc1123|rfc1123z|rfc3339|rfc3339nano|kitchen`), this will add timestamp prefix to Cosmovisor logs (but not the underlying process). * `COSMOVISOR_CUSTOM_PREUPGRADE` (defaults to ``). If set, this will run $DAEMON_HOME/cosmovisor/$COSMOVISOR_CUSTOM_PREUPGRADE prior to upgrade with the arguments [ upgrade.Name, upgrade.Height ]. Executes a custom script (separate and prior to the chain daemon pre-upgrade command) +* `COSMOVISOR_DISABLE_RECASE` (defaults to `false`). If set to true, the upgrade directory will expected to match the upgrade plan name without any case changes ### Folder Layout diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index 443580229e..84a202a045 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -33,6 +33,7 @@ const ( EnvColorLogs = "COSMOVISOR_COLOR_LOGS" EnvTimeFormatLogs = "COSMOVISOR_TIMEFORMAT_LOGS" EnvCustomPreupgrade = "COSMOVISOR_CUSTOM_PREUPGRADE" + EnvDisableRecase = "COSMOVISOR_DISABLE_RECASE" ) const ( @@ -58,6 +59,7 @@ type Config struct { ColorLogs bool TimeFormatLogs string CustomPreupgrade string + DisableRecase bool // currently running upgrade currentUpgrade upgradetypes.Plan @@ -178,6 +180,9 @@ func GetConfigFromEnv() (*Config, error) { if cfg.TimeFormatLogs, err = TimeFormatOptionFromEnv(EnvTimeFormatLogs, time.Kitchen); err != nil { errs = append(errs, err) } + if cfg.DisableRecase, err = BooleanOption(EnvDisableRecase, false); err != nil { + errs = append(errs, err) + } interval := os.Getenv(EnvInterval) if interval != "" { diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index 34522d260a..399e319253 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -38,6 +38,7 @@ type cosmovisorEnv struct { ColorLogs string TimeFormatLogs string CustomPreupgrade string + DisableRecase string } type envMap struct { @@ -62,6 +63,7 @@ func (c cosmovisorEnv) ToMap() map[string]envMap { EnvColorLogs: {val: c.ColorLogs, allowEmpty: false}, EnvTimeFormatLogs: {val: c.TimeFormatLogs, allowEmpty: true}, EnvCustomPreupgrade: {val: c.CustomPreupgrade, allowEmpty: true}, + EnvDisableRecase: {val: c.DisableRecase, allowEmpty: true}, } } @@ -96,6 +98,8 @@ func (c *cosmovisorEnv) Set(envVar, envVal string) { c.TimeFormatLogs = envVal case EnvCustomPreupgrade: c.CustomPreupgrade = envVal + case EnvDisableRecase: + c.DisableRecase = envVal default: panic(fmt.Errorf("Unknown environment variable [%s]. Ccannot set field to [%s]. ", envVar, envVal)) } @@ -456,6 +460,7 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { disableLogs, colorLogs bool, timeFormatLogs string, customPreUpgrade string, + disableRecase bool, ) *Config { return &Config{ Home: home, @@ -472,6 +477,7 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { ColorLogs: colorLogs, TimeFormatLogs: timeFormatLogs, CustomPreupgrade: customPreUpgrade, + DisableRecase: disableRecase, } } @@ -495,19 +501,21 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { Interval: "bad", PreupgradeMaxRetries: "bad", TimeFormatLogs: "bad", + CustomPreupgrade: "", + DisableRecase: "bad", }, expectedCfg: nil, - expectedErrCount: 11, + expectedErrCount: 12, }, { name: "all good", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "true"}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", true), expectedErrCount: 0, }, { name: "nothing set", - envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", "", "", "false", "false", "", ""}, + envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", "", "", "false", "false", "", "", ""}, expectedCfg: nil, expectedErrCount: 3, }, @@ -515,220 +523,231 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { // timeformat tests are done in the TestTimeFormat { name: "download bin bad", - envVals: cosmovisorEnv{absPath, "testname", "bad", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", ""}, + envVals: cosmovisorEnv{absPath, "testname", "bad", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "download bin not set", - envVals: cosmovisorEnv{absPath, "testname", "", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", ""}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, ""), + envVals: cosmovisorEnv{absPath, "testname", "", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "", false), expectedErrCount: 0, }, { name: "download bin true", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "download bin false", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "download ensure checksum true", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, false, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, false, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "restart upgrade bad", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "bad", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "bad", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart upgrade not set", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "restart upgrade true", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "true", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "true", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "restart upgrade true", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "skip unsafe backups bad", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "bad", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "bad", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "skip unsafe backups not set", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "skip unsafe backups true", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "skip unsafe backups false", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, false, true, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "poll interval bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "bad", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "bad", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "0", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "0", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "", "1", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 300, 1, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "", "1", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 300, 1, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "poll interval 600", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "600", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "600", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval 1s", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "1s", "1", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 1000, 1, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "1s", "1", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 1000, 1, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "poll interval -3m", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "-3m", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "-3m", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "bad", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "bad", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "0", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "0", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 0, false, absPath, 303, 1, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 0, false, absPath, 303, 1, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "restart delay 600", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600", "false", "", "300ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600", "false", "", "300ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay 1s", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "1s", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 1000, false, absPath, 303, 1, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "1s", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 1000, false, absPath, 303, 1, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "restart delay -3m", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "-3m", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "-3m", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "prepupgrade max retries bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "bad", "false", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "bad", "false", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "prepupgrade max retries 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "0", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "0", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "prepupgrade max retries not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "prepupgrade max retries 5", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "false", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 5, false, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "false", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 5, false, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "disable logs bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "bad", "true", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "bad", "true", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "disable logs good", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "disable logs color bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "true", "bad", "kitchen", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "true", "bad", "kitchen", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, { name: "disable logs color good", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, time.Kitchen, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, time.Kitchen, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "disable logs timestamp", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, "", "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, false, "", "preupgrade.sh", false), expectedErrCount: 0, }, { name: "enable rf3339 logs timestamp", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh"}, - expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, true, time.RFC3339, "preupgrade.sh"), + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", ""}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, true, time.RFC3339, "preupgrade.sh", false), expectedErrCount: 0, }, { name: "invalid logs timestamp format", - envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "invalid", "preupgrade.sh"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "invalid", "preupgrade.sh", ""}, expectedCfg: nil, expectedErrCount: 1, }, + { + name: "disable recase good", + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "true"}, + expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, true, true, time.RFC3339, "preupgrade.sh", true), + expectedErrCount: 0, + }, + { + name: "disable recase bad", + envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "bad"}, + expectedErrCount: 1, + }, } for _, tc := range tests { diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index e914b1d4bc..98ca072e98 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path" + "path/filepath" "strings" "github.com/spf13/cobra" @@ -37,9 +38,9 @@ func AddUpgrade(cmd *cobra.Command, args []string) error { logger := cfg.Logger(os.Stdout) - upgradeName := strings.ToLower(args[0]) - if len(upgradeName) == 0 { - return fmt.Errorf("upgrade name cannot be empty") + upgradeName := args[0] + if !cfg.DisableRecase { + upgradeName = strings.ToLower(args[0]) } executablePath := args[1] @@ -93,7 +94,7 @@ func AddUpgrade(cmd *cobra.Command, args []string) error { return err } - logger.Info(fmt.Sprintf("%s created, %s upgrade binary will switch at height %d", upgradetypes.UpgradeInfoFilename, upgradeName, upgradeHeight)) + logger.Info(fmt.Sprintf("%s created, %s upgrade binary will switch at height %d", filepath.Join(cfg.UpgradeInfoFilePath(), upgradetypes.UpgradeInfoFilename), upgradeName, upgradeHeight)) } return nil diff --git a/tools/cosmovisor/process_test.go b/tools/cosmovisor/process_test.go index 57da5ff7fa..f9b5137f39 100644 --- a/tools/cosmovisor/process_test.go +++ b/tools/cosmovisor/process_test.go @@ -74,6 +74,51 @@ func (s *processTestSuite) TestLaunchProcess() { require.Equal(cfg.UpgradeBin("chain2"), currentBin) } +// TestPlanDisableRecase will test upgrades without lower case plan names +func (s *processTestSuite) TestPlanDisableRecase() { + // binaries from testdata/validate directory + require := s.Require() + home := copyTestData(s.T(), "norecase") + cfg := &cosmovisor.Config{Home: home, Name: "dummyd", PollInterval: 20, UnsafeSkipBackup: true, DisableRecase: true} + logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor") + + // should run the genesis binary and produce expected output + stdout, stderr := newBuffer(), newBuffer() + currentBin, err := cfg.CurrentBin() + require.NoError(err) + require.Equal(cfg.GenesisBin(), currentBin) + + launcher, err := cosmovisor.NewLauncher(logger, cfg) + require.NoError(err) + + upgradeFile := cfg.UpgradeInfoFilePath() + + args := []string{"foo", "bar", "1234", upgradeFile} + doUpgrade, err := launcher.Run(args, stdout, stderr) + require.NoError(err) + require.True(doUpgrade) + require.Equal("", stderr.String()) + require.Equal(fmt.Sprintf("Genesis foo bar 1234 %s\nUPGRADE \"Chain2\" NEEDED at height: 49: {}\n", upgradeFile), stdout.String()) + + // ensure this is upgraded now and produces new output + currentBin, err = cfg.CurrentBin() + require.NoError(err) + + require.Equal(cfg.UpgradeBin("Chain2"), currentBin) + args = []string{"second", "run", "--verbose"} + stdout.Reset() + stderr.Reset() + + doUpgrade, err = launcher.Run(args, stdout, stderr) + require.NoError(err) + require.False(doUpgrade) + require.Equal("", stderr.String()) + require.Equal("Chain 2 is live!\nArgs: second run --verbose\nFinished successfully\n", stdout.String()) + + // ended without other upgrade + require.Equal(cfg.UpgradeBin("Chain2"), currentBin) +} + func (s *processTestSuite) TestLaunchProcessWithRestartDelay() { // binaries from testdata/validate directory require := s.Require() diff --git a/tools/cosmovisor/scanner.go b/tools/cosmovisor/scanner.go index c64445a9cc..f67692751d 100644 --- a/tools/cosmovisor/scanner.go +++ b/tools/cosmovisor/scanner.go @@ -25,8 +25,9 @@ type fileWatcher struct { cancel chan bool ticker *time.Ticker - needsUpdate bool - initialized bool + needsUpdate bool + initialized bool + disableRecase bool } func newUpgradeFileWatcher(cfg *Config, logger log.Logger) (*fileWatcher, error) { @@ -51,15 +52,16 @@ func newUpgradeFileWatcher(cfg *Config, logger log.Logger) (*fileWatcher, error) } return &fileWatcher{ - currentBin: bin, - filename: filenameAbs, - interval: cfg.PollInterval, - currentInfo: upgradetypes.Plan{}, - lastModTime: time.Time{}, - cancel: make(chan bool), - ticker: time.NewTicker(cfg.PollInterval), - needsUpdate: false, - initialized: false, + currentBin: bin, + filename: filenameAbs, + interval: cfg.PollInterval, + currentInfo: upgradetypes.Plan{}, + lastModTime: time.Time{}, + cancel: make(chan bool), + ticker: time.NewTicker(cfg.PollInterval), + needsUpdate: false, + initialized: false, + disableRecase: cfg.DisableRecase, }, nil } @@ -112,7 +114,7 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool { return false } - info, err := parseUpgradeInfoFile(fw.filename) + info, err := parseUpgradeInfoFile(fw.filename, fw.disableRecase) if err != nil { panic(fmt.Errorf("failed to parse upgrade info file: %w", err)) } @@ -180,7 +182,7 @@ func (fw *fileWatcher) checkHeight() (int64, error) { return strconv.ParseInt(resp.SyncInfo.LatestBlockHeight, 10, 64) } -func parseUpgradeInfoFile(filename string) (upgradetypes.Plan, error) { +func parseUpgradeInfoFile(filename string, disableRecase bool) (upgradetypes.Plan, error) { f, err := os.ReadFile(filename) if err != nil { return upgradetypes.Plan{}, err @@ -201,7 +203,9 @@ func parseUpgradeInfoFile(filename string) (upgradetypes.Plan, error) { } // normalize name to prevent operator error in upgrade name case sensitivity errors. - upgradePlan.Name = strings.ToLower(upgradePlan.Name) + if !disableRecase { + upgradePlan.Name = strings.ToLower(upgradePlan.Name) + } return upgradePlan, err } diff --git a/tools/cosmovisor/scanner_test.go b/tools/cosmovisor/scanner_test.go index 5e3ac93337..02ece86218 100644 --- a/tools/cosmovisor/scanner_test.go +++ b/tools/cosmovisor/scanner_test.go @@ -13,50 +13,66 @@ func TestParseUpgradeInfoFile(t *testing.T) { cases := []struct { filename string expectUpgrade upgradetypes.Plan + disableRecase bool expectErr bool }{ { filename: "f1-good.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{Name: "upgrade1", Info: "some info", Height: 123}, expectErr: false, }, { filename: "f2-normalized-name.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{Name: "upgrade2", Info: "some info", Height: 125}, expectErr: false, }, + { + filename: "f2-normalized-name.json", + disableRecase: true, + expectUpgrade: upgradetypes.Plan{Name: "Upgrade2", Info: "some info", Height: 125}, + expectErr: false, + }, { filename: "f2-bad-type.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "f2-bad-type-2.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "f3-empty.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "f4-empty-obj.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "f5-partial-obj-1.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "f5-partial-obj-2.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, { filename: "unknown.json", + disableRecase: false, expectUpgrade: upgradetypes.Plan{}, expectErr: true, }, @@ -66,7 +82,7 @@ func TestParseUpgradeInfoFile(t *testing.T) { tc := cases[i] t.Run(tc.filename, func(t *testing.T) { require := require.New(t) - ui, err := parseUpgradeInfoFile(filepath.Join(".", "testdata", "upgrade-files", tc.filename)) + ui, err := parseUpgradeInfoFile(filepath.Join(".", "testdata", "upgrade-files", tc.filename), tc.disableRecase) if tc.expectErr { require.Error(err) } else { diff --git a/tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd b/tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd new file mode 100755 index 0000000000..832cee8f62 --- /dev/null +++ b/tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd @@ -0,0 +1,9 @@ +#!/bin/sh + +echo Genesis $@ +sleep 1 +test -z $4 && exit 1001 +echo 'UPGRADE "Chain2" NEEDED at height: 49: {}' +echo '{"name":"Chain2","height":49,"info":""}' > $4 +sleep 2 +echo Never should be printed!!! diff --git a/tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd b/tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd new file mode 100755 index 0000000000..0022b84af2 --- /dev/null +++ b/tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd @@ -0,0 +1,6 @@ +#!/bin/sh + +echo Chain 2 is live! +echo Args: $@ +sleep 1 +echo Finished successfully diff --git a/tools/cosmovisor/testdata/norecase/data/.gitkeep b/tools/cosmovisor/testdata/norecase/data/.gitkeep new file mode 100644 index 0000000000..e69de29bb2