diff --git a/tools/cosmovisor/CHANGELOG.md b/tools/cosmovisor/CHANGELOG.md index 67bc51a148..b28afab40d 100644 --- a/tools/cosmovisor/CHANGELOG.md +++ b/tools/cosmovisor/CHANGELOG.md @@ -44,6 +44,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [#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 + ## v1.4.0 2022-10-23 diff --git a/tools/cosmovisor/README.md b/tools/cosmovisor/README.md index 19f1706375..d591ba2624 100644 --- a/tools/cosmovisor/README.md +++ b/tools/cosmovisor/README.md @@ -99,7 +99,8 @@ All arguments passed to `cosmovisor run` will be passed to the application binar * `DAEMON_POLL_INTERVAL` (*optional*, default 300 milliseconds), is the interval length for polling the upgrade plan file. The value must be a duration (e.g. `1s`). * `DAEMON_DATA_BACKUP_DIR` option to set a custom backup directory. If not set, `DAEMON_HOME` is used. * `UNSAFE_SKIP_BACKUP` (defaults to `false`), if set to `true`, upgrades directly without performing a backup. Otherwise (`false`, default) backs up the data before trying the upgrade. The default value of false is useful and recommended in case of failures and when a backup needed to rollback. We recommend using the default backup option `UNSAFE_SKIP_BACKUP=false`. -* `DAEMON_PREUPGRADE_MAX_RETRIES` (defaults to `0`). The maximum number of times to call `pre-upgrade` in the application after exit status of `31`. After the maximum number of retries, cosmovisor fails the upgrade. +* `DAEMON_PREUPGRADE_MAX_RETRIES` (defaults to `0`). The maximum number of times to call `pre-upgrade` in the application after exit status of `31`. After the maximum number of retries, Cosmovisor fails the upgrade. +* `COSMOVISOR_DISABLE_LOGS` (defaults to `false`). If set to true, this will disable Cosmovisor logs (but not the underlying process) completely. This may be useful, for example, when a Cosmovisor subcommand you are executing returns a valid JSON you are then parsing, as logs added by Cosmovisor make this output not a valid JSON. ### Folder Layout diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index 45358412b3..8c61c1f1c1 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -28,6 +28,7 @@ const ( EnvDataBackupPath = "DAEMON_DATA_BACKUP_DIR" EnvInterval = "DAEMON_POLL_INTERVAL" EnvPreupgradeMaxRetries = "DAEMON_PREUPGRADE_MAX_RETRIES" + EnvDisableLogs = "COSMOVISOR_DISABLE_LOGS" ) const ( @@ -51,6 +52,7 @@ type Config struct { UnsafeSkipBackup bool DataBackupPath string PreupgradeMaxRetries int + DisableLogs bool // currently running upgrade currentUpgrade upgradetypes.Plan @@ -158,6 +160,9 @@ func GetConfigFromEnv() (*Config, error) { if cfg.UnsafeSkipBackup, err = booleanOption(EnvSkipBackup, false); err != nil { errs = append(errs, err) } + if cfg.DisableLogs, err = booleanOption(EnvDisableLogs, false); err != nil { + errs = append(errs, err) + } interval := os.Getenv(EnvInterval) if interval != "" { @@ -369,6 +374,7 @@ func (cfg Config) DetailString() string { {EnvSkipBackup, fmt.Sprintf("%t", cfg.UnsafeSkipBackup)}, {EnvDataBackupPath, cfg.DataBackupPath}, {EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreupgradeMaxRetries)}, + {EnvDisableLogs, fmt.Sprintf("%t", cfg.DisableLogs)}, } derivedEntries := []struct{ name, value string }{ diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index ffb9322945..b84e537c69 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -38,6 +38,7 @@ type cosmovisorEnv struct { DataBackupPath string Interval string PreupgradeMaxRetries string + DisableLogs string } // ToMap creates a map of the cosmovisorEnv where the keys are the env var names. @@ -52,6 +53,7 @@ func (c cosmovisorEnv) ToMap() map[string]string { EnvDataBackupPath: c.DataBackupPath, EnvInterval: c.Interval, EnvPreupgradeMaxRetries: c.PreupgradeMaxRetries, + EnvDisableLogs: c.DisableLogs, } } @@ -76,6 +78,8 @@ func (c *cosmovisorEnv) Set(envVar, envVal string) { c.Interval = envVal case EnvPreupgradeMaxRetries: c.PreupgradeMaxRetries = envVal + case EnvDisableLogs: + c.DisableLogs = envVal default: panic(fmt.Errorf("Unknown environment variable [%s]. Ccannot set field to [%s]. ", envVar, envVal)) } @@ -367,7 +371,7 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { absPath, perr := filepath.Abs(relPath) s.Require().NoError(perr) - newConfig := func(home, name string, downloadBin, restartUpgrade bool, restartDelay int, skipBackup bool, dataBackupPath string, interval int, preupgradeMaxRetries int) *Config { + newConfig := func(home, name string, downloadBin, restartUpgrade bool, restartDelay int, skipBackup bool, dataBackupPath string, interval int, preupgradeMaxRetries int, disableLogs bool) *Config { return &Config{ Home: home, Name: name, @@ -378,6 +382,7 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { UnsafeSkipBackup: skipBackup, DataBackupPath: dataBackupPath, PreupgradeMaxRetries: preupgradeMaxRetries, + DisableLogs: disableLogs, } } @@ -405,183 +410,195 @@ func (s *argsTestSuite) TestGetConfigFromEnv() { }, { name: "all good", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "nothing set", - envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", ""}, + envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", "", "false"}, expectedCfg: nil, expectedErrCount: 3, }, // Note: Home and Name tests are done in TestValidate { name: "download bin bad", - envVals: cosmovisorEnv{absPath, "testname", "bad", "false", "600ms", "true", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "bad", "false", "600ms", "true", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "download bin not set", - envVals: cosmovisorEnv{absPath, "testname", "", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "download bin true", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "download bin false", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "restart upgrade bad", - envVals: cosmovisorEnv{absPath, "testname", "true", "bad", "600ms", "true", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "true", "bad", "600ms", "true", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart upgrade not set", - envVals: cosmovisorEnv{absPath, "testname", "true", "", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "restart upgrade true", - envVals: cosmovisorEnv{absPath, "testname", "true", "true", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "true", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "restart upgrade true", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "skip unsafe backups bad", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "bad", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "bad", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "skip unsafe backups not set", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "skip unsafe backups true", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "skip unsafe backups false", - envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "false", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "false", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "poll interval bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "bad", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "bad", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "0", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "0", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 300, 1), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 300, 1, false), expectedErrCount: 0, }, { name: "poll interval 600", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "600", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "600", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "poll interval 1s", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "1s", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 1000, 1), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "1s", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 1000, 1, false), expectedErrCount: 0, }, { name: "poll interval -3m", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "-3m", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "-3m", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "bad", "false", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "bad", "false", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "0", "false", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "0", "false", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "", "false", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 0, false, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "", "false", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 0, false, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "restart delay 600", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600", "false", "", "300ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600", "false", "", "300ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "restart delay 1s", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "1s", "false", "", "303ms", "1"}, - expectedCfg: newConfig(absPath, "testname", false, false, 1000, false, absPath, 303, 1), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "1s", "false", "", "303ms", "1", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 1000, false, absPath, 303, 1, false), expectedErrCount: 0, }, { name: "restart delay -3m", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "-3m", "false", "", "303ms", "1"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "-3m", "false", "", "303ms", "1", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "prepupgrade max retries bad", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "bad"}, + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "bad", "false"}, expectedCfg: nil, expectedErrCount: 1, }, { name: "prepupgrade max retries 0", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "0"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "0", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, false), expectedErrCount: 0, }, { name: "prepupgrade max retries not set", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", ""}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, false), expectedErrCount: 0, }, { name: "prepupgrade max retries 5", - envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "5"}, - expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 5), + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "5", "false"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 5, false), + expectedErrCount: 0, + }, + { + name: "disable logs bad", + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "5", "bad"}, + expectedCfg: nil, + expectedErrCount: 1, + }, + { + name: "disable logs good", + envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "", "true"}, + expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, true), expectedErrCount: 0, }, } diff --git a/tools/cosmovisor/cmd/cosmovisor/run.go b/tools/cosmovisor/cmd/cosmovisor/run.go index 5c91c77c11..8a6839ba0b 100644 --- a/tools/cosmovisor/cmd/cosmovisor/run.go +++ b/tools/cosmovisor/cmd/cosmovisor/run.go @@ -3,6 +3,7 @@ package main import ( "cosmossdk.io/log" "cosmossdk.io/tools/cosmovisor" + "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -16,19 +17,23 @@ var runCmd = &cobra.Command{ SilenceUsage: true, DisableFlagParsing: true, RunE: func(cmd *cobra.Command, args []string) error { - logger := cmd.Context().Value(log.ContextKey).(log.Logger) - - return Run(logger, args) + return Run(cmd, args) }, } // Run runs the configured program with the given args and monitors it for upgrades. -func Run(logger log.Logger, args []string, options ...RunOption) error { +func Run(cmd *cobra.Command, args []string, options ...RunOption) error { cfg, err := cosmovisor.GetConfigFromEnv() if err != nil { return err } + logger := cmd.Context().Value(log.ContextKey).(log.Logger) + + if cfg.DisableLogs { + logger = log.NewCustomLogger(zerolog.Nop()) + } + runCfg := DefaultRunConfig for _, opt := range options { opt(&runCfg) diff --git a/tools/cosmovisor/cmd/cosmovisor/version.go b/tools/cosmovisor/cmd/cosmovisor/version.go index 722ccc5e93..4e023d0e3f 100644 --- a/tools/cosmovisor/cmd/cosmovisor/version.go +++ b/tools/cosmovisor/cmd/cosmovisor/version.go @@ -6,8 +6,6 @@ import ( "runtime/debug" "strings" - "cosmossdk.io/log" - "github.com/rs/zerolog" "github.com/spf13/cobra" ) @@ -24,13 +22,11 @@ var versionCmd = &cobra.Command{ Short: "Prints the version of Cosmovisor.", SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - logger := cmd.Context().Value(log.ContextKey).(log.Logger) - if val, err := cmd.Flags().GetString(OutputFlag); val == "json" && err == nil { - return printVersionJSON(logger, args) + return printVersionJSON(cmd, args) } - return printVersion(logger, args) + return printVersion(cmd, args) }, } @@ -43,25 +39,21 @@ func getVersion() string { return strings.TrimSpace(version.Main.Version) } -func printVersion(logger log.Logger, args []string) error { +func printVersion(cmd *cobra.Command, args []string) error { fmt.Printf("cosmovisor version: %s\n", getVersion()) - if err := Run(logger, append([]string{"version"}, args...)); err != nil { + if err := Run(cmd, append([]string{"version"}, args...)); err != nil { return fmt.Errorf("failed to run version command: %w", err) } return nil } -func printVersionJSON(logger log.Logger, args []string) error { +func printVersionJSON(cmd *cobra.Command, args []string) error { buf := new(strings.Builder) - // disable logger - zl := logger.Impl().(*zerolog.Logger) - logger = log.NewCustomLogger(zl.Level(zerolog.Disabled)) - if err := Run( - logger, + cmd, []string{"version", "--long", "--output", "json"}, StdOutRunOption(buf), ); err != nil {