feat(cosmovisor): load cosmovisor configuration from toml file (#19995)
This commit is contained in:
parent
90cbb022d5
commit
465410c75b
@ -38,6 +38,18 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
* [#20062](https://github.com/cosmos/cosmos-sdk/pull/20062) Fixed cosmovisor add-upgrade permissions
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
* [#19764](https://github.com/cosmos/cosmos-sdk/issues/19764) Use config file for cosmovisor configuration.
|
||||
|
||||
## Improvements
|
||||
|
||||
* [#19995](https://github.com/cosmos/cosmos-sdk/pull/19995):
|
||||
* `init command` writes the configuration to the config file only at the default path `DAEMON_HOME/cosmovisor/config.toml`.
|
||||
* Provide `--cosmovisor-config` flag with value as args to provide the path to the configuration file in the `run` command. `run --cosmovisor-config <path> (other cmds with flags) ...`.
|
||||
* Add `--cosmovisor-config` flag to provide `config.toml` path to the configuration file in root command used by `add-upgrade` and `config` subcommands.
|
||||
* `config command` now displays the configuration from the config file if it is provided. If the config file is not provided, it will display the configuration from the environment variables.
|
||||
|
||||
## v1.5.0 - 2023-07-17
|
||||
|
||||
## Features
|
||||
|
||||
@ -12,6 +12,9 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
@ -42,26 +45,29 @@ const (
|
||||
genesisDir = "genesis"
|
||||
upgradesDir = "upgrades"
|
||||
currentLink = "current"
|
||||
|
||||
cfgFileName = "config"
|
||||
cfgExtension = "toml"
|
||||
)
|
||||
|
||||
// Config is the information passed in to control the daemon
|
||||
type Config struct {
|
||||
Home string
|
||||
Name string
|
||||
AllowDownloadBinaries bool
|
||||
DownloadMustHaveChecksum bool
|
||||
RestartAfterUpgrade bool
|
||||
RestartDelay time.Duration
|
||||
ShutdownGrace time.Duration
|
||||
PollInterval time.Duration
|
||||
UnsafeSkipBackup bool
|
||||
DataBackupPath string
|
||||
PreupgradeMaxRetries int
|
||||
DisableLogs bool
|
||||
ColorLogs bool
|
||||
TimeFormatLogs string
|
||||
CustomPreupgrade string
|
||||
DisableRecase bool
|
||||
Home string `toml:"daemon_home" mapstructure:"daemon_home"`
|
||||
Name string `toml:"daemon_name" mapstructure:"daemon_name"`
|
||||
AllowDownloadBinaries bool `toml:"daemon_allow_download_binaries" mapstructure:"daemon_allow_download_binaries" default:"false"`
|
||||
DownloadMustHaveChecksum bool `toml:"daemon_download_must_have_checksum" mapstructure:"daemon_download_must_have_checksum" default:"false"`
|
||||
RestartAfterUpgrade bool `toml:"daemon_restart_after_upgrade" mapstructure:"daemon_restart_after_upgrade" default:"true"`
|
||||
RestartDelay time.Duration `toml:"daemon_restart_delay" mapstructure:"daemon_restart_delay"`
|
||||
ShutdownGrace time.Duration `toml:"daemon_shutdown_grace" mapstructure:"daemon_shutdown_grace"`
|
||||
PollInterval time.Duration `toml:"daemon_poll_interval" mapstructure:"daemon_poll_interval" default:"300ms"`
|
||||
UnsafeSkipBackup bool `toml:"unsafe_skip_backup" mapstructure:"unsafe_skip_backup" default:"false"`
|
||||
DataBackupPath string `toml:"daemon_data_backup_dir" mapstructure:"daemon_data_backup_dir"`
|
||||
PreUpgradeMaxRetries int `toml:"daemon_preupgrade_max_retries" mapstructure:"daemon_preupgrade_max_retries" default:"0"`
|
||||
DisableLogs bool `toml:"cosmovisor_disable_logs" mapstructure:"cosmovisor_disable_logs" default:"false"`
|
||||
ColorLogs bool `toml:"cosmovisor_color_logs" mapstructure:"cosmovisor_color_logs" default:"true"`
|
||||
TimeFormatLogs string `toml:"cosmovisor_timeformat_logs" mapstructure:"cosmovisor_timeformat_logs" default:"kitchen"`
|
||||
CustomPreUpgrade string `toml:"cosmovisor_custom_preupgrade" mapstructure:"cosmovisor_custom_preupgrade" default:""`
|
||||
DisableRecase bool `toml:"cosmovisor_disable_recase" mapstructure:"cosmovisor_disable_recase" default:"false"`
|
||||
|
||||
// currently running upgrade
|
||||
currentUpgrade upgradetypes.Plan
|
||||
@ -72,6 +78,11 @@ func (cfg *Config) Root() string {
|
||||
return filepath.Join(cfg.Home, rootName)
|
||||
}
|
||||
|
||||
// DefaultCfgPath returns the default path to the configuration file.
|
||||
func (cfg *Config) DefaultCfgPath() string {
|
||||
return filepath.Join(cfg.Root(), cfgFileName+"."+cfgExtension)
|
||||
}
|
||||
|
||||
// GenesisBin is the path to the genesis binary - must be in place to start manager
|
||||
func (cfg *Config) GenesisBin() string {
|
||||
return filepath.Join(cfg.Root(), genesisDir, "bin", cfg.Name)
|
||||
@ -145,6 +156,51 @@ func (cfg *Config) CurrentBin() (string, error) {
|
||||
return binpath, nil
|
||||
}
|
||||
|
||||
// GetConfigFromFile will read the configuration from the file at the given path.
|
||||
// If the file path is not provided, it will try to read the configuration from the ENV variables.
|
||||
// If a file path is provided and ENV variables are set, they will override the values in the file.
|
||||
func GetConfigFromFile(filePath string) (*Config, error) {
|
||||
if filePath == "" {
|
||||
return GetConfigFromEnv()
|
||||
}
|
||||
|
||||
// ensure the file exist
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return nil, fmt.Errorf("config not found: at %s : %w", filePath, err)
|
||||
}
|
||||
|
||||
// read the configuration from the file
|
||||
viper.SetConfigFile(filePath)
|
||||
// load the env variables
|
||||
// if the env variable is set, it will override the value provided by the config
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
cfg := &Config{}
|
||||
if err := viper.Unmarshal(cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
errs []error
|
||||
)
|
||||
|
||||
if cfg.TimeFormatLogs, err = getTimeFormatOption(cfg.TimeFormatLogs); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
errs = append(errs, cfg.validate()...)
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// GetConfigFromEnv will read the environmental variables into a config
|
||||
// and then validate it is reasonable
|
||||
func GetConfigFromEnv() (*Config, error) {
|
||||
@ -153,7 +209,7 @@ func GetConfigFromEnv() (*Config, error) {
|
||||
Home: os.Getenv(EnvHome),
|
||||
Name: os.Getenv(EnvName),
|
||||
DataBackupPath: os.Getenv(EnvDataBackupPath),
|
||||
CustomPreupgrade: os.Getenv(EnvCustomPreupgrade),
|
||||
CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade),
|
||||
}
|
||||
|
||||
if cfg.DataBackupPath == "" {
|
||||
@ -220,8 +276,8 @@ func GetConfigFromEnv() (*Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
envPreupgradeMaxRetriesVal := os.Getenv(EnvPreupgradeMaxRetries)
|
||||
if cfg.PreupgradeMaxRetries, err = strconv.Atoi(envPreupgradeMaxRetriesVal); err != nil && envPreupgradeMaxRetriesVal != "" {
|
||||
envPreUpgradeMaxRetriesVal := os.Getenv(EnvPreupgradeMaxRetries)
|
||||
if cfg.PreUpgradeMaxRetries, err = strconv.Atoi(envPreUpgradeMaxRetriesVal); err != nil && envPreUpgradeMaxRetriesVal != "" {
|
||||
errs = append(errs, fmt.Errorf("%s could not be parsed to int: %w", EnvPreupgradeMaxRetries, err))
|
||||
}
|
||||
|
||||
@ -355,6 +411,7 @@ func (cfg *Config) SetCurrentUpgrade(u upgradetypes.Plan) (rerr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpgradeInfo returns the current upgrade info
|
||||
func (cfg *Config) UpgradeInfo() (upgradetypes.Plan, error) {
|
||||
if cfg.currentUpgrade.Name != "" {
|
||||
return cfg.currentUpgrade, nil
|
||||
@ -381,7 +438,7 @@ returnError:
|
||||
return cfg.currentUpgrade, fmt.Errorf("failed to read %q: %w", filename, err)
|
||||
}
|
||||
|
||||
// checks and validates env option
|
||||
// BooleanOption checks and validate env option
|
||||
func BooleanOption(name string, defaultVal bool) (bool, error) {
|
||||
p := strings.ToLower(os.Getenv(name))
|
||||
switch p {
|
||||
@ -395,12 +452,17 @@ func BooleanOption(name string, defaultVal bool) (bool, error) {
|
||||
return false, fmt.Errorf("env variable %q must have a boolean value (\"true\" or \"false\"), got %q", name, p)
|
||||
}
|
||||
|
||||
// checks and validates env option
|
||||
// TimeFormatOptionFromEnv checks and validates the time format option
|
||||
func TimeFormatOptionFromEnv(env, defaultVal string) (string, error) {
|
||||
val, set := os.LookupEnv(env)
|
||||
if !set {
|
||||
return defaultVal, nil
|
||||
}
|
||||
|
||||
return getTimeFormatOption(val)
|
||||
}
|
||||
|
||||
func getTimeFormatOption(val string) (string, error) {
|
||||
switch val {
|
||||
case "layout":
|
||||
return time.Layout, nil
|
||||
@ -432,6 +494,38 @@ func TimeFormatOptionFromEnv(env, defaultVal string) (string, error) {
|
||||
return "", fmt.Errorf("env variable %q must have a timeformat value (\"layout|ansic|unixdate|rubydate|rfc822|rfc822z|rfc850|rfc1123|rfc1123z|rfc3339|rfc3339nano|kitchen\"), got %q", EnvTimeFormatLogs, val)
|
||||
}
|
||||
|
||||
// ValueToTimeFormatOption converts the time format option to the env value
|
||||
func ValueToTimeFormatOption(format string) string {
|
||||
switch format {
|
||||
case time.Layout:
|
||||
return "layout"
|
||||
case time.ANSIC:
|
||||
return "ansic"
|
||||
case time.UnixDate:
|
||||
return "unixdate"
|
||||
case time.RubyDate:
|
||||
return "rubydate"
|
||||
case time.RFC822:
|
||||
return "rfc822"
|
||||
case time.RFC822Z:
|
||||
return "rfc822z"
|
||||
case time.RFC850:
|
||||
return "rfc850"
|
||||
case time.RFC1123:
|
||||
return "rfc1123"
|
||||
case time.RFC1123Z:
|
||||
return "rfc1123z"
|
||||
case time.RFC3339:
|
||||
return "rfc3339"
|
||||
case time.RFC3339Nano:
|
||||
return "rfc3339nano"
|
||||
case time.Kitchen:
|
||||
return "kitchen"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// DetailString returns a multi-line string with details about this config.
|
||||
func (cfg Config) DetailString() string {
|
||||
configEntries := []struct{ name, value string }{
|
||||
@ -445,11 +539,11 @@ func (cfg Config) DetailString() string {
|
||||
{EnvInterval, cfg.PollInterval.String()},
|
||||
{EnvSkipBackup, fmt.Sprintf("%t", cfg.UnsafeSkipBackup)},
|
||||
{EnvDataBackupPath, cfg.DataBackupPath},
|
||||
{EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreupgradeMaxRetries)},
|
||||
{EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreUpgradeMaxRetries)},
|
||||
{EnvDisableLogs, fmt.Sprintf("%t", cfg.DisableLogs)},
|
||||
{EnvColorLogs, fmt.Sprintf("%t", cfg.ColorLogs)},
|
||||
{EnvTimeFormatLogs, cfg.TimeFormatLogs},
|
||||
{EnvCustomPreupgrade, cfg.CustomPreupgrade},
|
||||
{EnvCustomPreupgrade, cfg.CustomPreUpgrade},
|
||||
{EnvDisableRecase, fmt.Sprintf("%t", cfg.DisableRecase)},
|
||||
}
|
||||
|
||||
@ -479,3 +573,48 @@ func (cfg Config) DetailString() string {
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Export exports the configuration to a file at the given path.
|
||||
func (cfg Config) Export() (string, error) {
|
||||
// always use the default path
|
||||
path := filepath.Clean(cfg.DefaultCfgPath())
|
||||
|
||||
// check if config file already exists ask user if they want to overwrite it
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
// ask user if they want to overwrite the file
|
||||
if !askForConfirmation(fmt.Sprintf("file %s already exists, do you want to overwrite it?", path)) {
|
||||
cfg.Logger(os.Stdout).Info("file already exists, not overriding")
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
// create the file
|
||||
file, err := os.Create(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create configuration file: %w", err)
|
||||
}
|
||||
|
||||
// convert the time value to its format option
|
||||
cfg.TimeFormatLogs = ValueToTimeFormatOption(cfg.TimeFormatLogs)
|
||||
|
||||
defer file.Close()
|
||||
|
||||
// write the configuration to the file
|
||||
err = toml.NewEncoder(file).Encode(cfg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encode configuration: %w", err)
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func askForConfirmation(str string) bool {
|
||||
var response string
|
||||
fmt.Printf("%s [y/n]: ", str)
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.ToLower(response) == "y"
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -412,7 +413,7 @@ func (s *argsTestSuite) TestDetailString() {
|
||||
PollInterval: pollInterval,
|
||||
UnsafeSkipBackup: unsafeSkipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreupgradeMaxRetries: preupgradeMaxRetries,
|
||||
PreUpgradeMaxRetries: preupgradeMaxRetries,
|
||||
}
|
||||
|
||||
expectedPieces := []string{
|
||||
@ -444,6 +445,41 @@ func (s *argsTestSuite) TestDetailString() {
|
||||
}
|
||||
}
|
||||
|
||||
var newConfig = func(
|
||||
home, name string,
|
||||
downloadBin bool,
|
||||
downloadMustHaveChecksum bool,
|
||||
restartUpgrade bool,
|
||||
restartDelay int,
|
||||
skipBackup bool,
|
||||
dataBackupPath string,
|
||||
interval, preupgradeMaxRetries int,
|
||||
disableLogs, colorLogs bool,
|
||||
timeFormatLogs string,
|
||||
customPreUpgrade string,
|
||||
disableRecase bool,
|
||||
shutdownGrace int,
|
||||
) *Config {
|
||||
return &Config{
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: downloadBin,
|
||||
DownloadMustHaveChecksum: downloadMustHaveChecksum,
|
||||
RestartAfterUpgrade: restartUpgrade,
|
||||
RestartDelay: time.Millisecond * time.Duration(restartDelay),
|
||||
PollInterval: time.Millisecond * time.Duration(interval),
|
||||
UnsafeSkipBackup: skipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreUpgradeMaxRetries: preupgradeMaxRetries,
|
||||
DisableLogs: disableLogs,
|
||||
ColorLogs: colorLogs,
|
||||
TimeFormatLogs: timeFormatLogs,
|
||||
CustomPreUpgrade: customPreUpgrade,
|
||||
DisableRecase: disableRecase,
|
||||
ShutdownGrace: time.Duration(shutdownGrace),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
initialEnv := s.clearEnv()
|
||||
defer s.setEnv(nil, initialEnv)
|
||||
@ -452,41 +488,6 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
absPath, perr := filepath.Abs(relPath)
|
||||
s.Require().NoError(perr)
|
||||
|
||||
newConfig := func(
|
||||
home, name string,
|
||||
downloadBin bool,
|
||||
downloadMustHaveChecksum bool,
|
||||
restartUpgrade bool,
|
||||
restartDelay int,
|
||||
skipBackup bool,
|
||||
dataBackupPath string,
|
||||
interval, preupgradeMaxRetries int,
|
||||
disableLogs, colorLogs bool,
|
||||
timeFormatLogs string,
|
||||
customPreUpgrade string,
|
||||
disableRecase bool,
|
||||
shutdownGrace int,
|
||||
) *Config {
|
||||
return &Config{
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: downloadBin,
|
||||
DownloadMustHaveChecksum: downloadMustHaveChecksum,
|
||||
RestartAfterUpgrade: restartUpgrade,
|
||||
RestartDelay: time.Millisecond * time.Duration(restartDelay),
|
||||
PollInterval: time.Millisecond * time.Duration(interval),
|
||||
UnsafeSkipBackup: skipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreupgradeMaxRetries: preupgradeMaxRetries,
|
||||
DisableLogs: disableLogs,
|
||||
ColorLogs: colorLogs,
|
||||
TimeFormatLogs: timeFormatLogs,
|
||||
CustomPreupgrade: customPreUpgrade,
|
||||
DisableRecase: disableRecase,
|
||||
ShutdownGrace: time.Duration(shutdownGrace),
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
envVals cosmovisorEnv
|
||||
@ -783,6 +784,95 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) setUpDir() string {
|
||||
s.T().Helper()
|
||||
|
||||
home := s.T().TempDir()
|
||||
err := os.MkdirAll(filepath.Join(home, rootName), 0o755)
|
||||
s.Require().NoError(err)
|
||||
return home
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) setupConfig(home string) string {
|
||||
s.T().Helper()
|
||||
|
||||
cfg := newConfig(home, "test", true, true, true, 406, false, home, 8, 0, false, true, "kitchen", "", true, 10000000000)
|
||||
path := filepath.Join(home, rootName, "config.toml")
|
||||
f, err := os.Create(path)
|
||||
s.Require().NoError(err)
|
||||
|
||||
enc := toml.NewEncoder(f)
|
||||
s.Require().NoError(enc.Encode(&cfg))
|
||||
|
||||
err = f.Close()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestConfigFromFile() {
|
||||
home := s.setUpDir()
|
||||
// create a config file
|
||||
cfgFilePath := s.setupConfig(home)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedCfg func() *Config
|
||||
filePath string
|
||||
expectedError string
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
name: "valid config",
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "test", true, true, true, 406, false, home, 8, 0, false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
filePath: cfgFilePath,
|
||||
expectedError: "",
|
||||
malleate: func() {},
|
||||
},
|
||||
{
|
||||
name: "env variable will override config file fields",
|
||||
filePath: cfgFilePath,
|
||||
expectedError: "",
|
||||
malleate: func() {
|
||||
// set env variable different from the config file
|
||||
os.Setenv(EnvName, "env-name")
|
||||
},
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "env-name", true, true, true, 406, false, home, 8, 0, false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "empty config file path will load config from ENV variables",
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "test", true, true, true, 406, false, home, 8, 0, false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
filePath: "",
|
||||
expectedError: "",
|
||||
malleate: func() {
|
||||
s.setEnv(s.T(), &cosmovisorEnv{home, "test", "true", "true", "true", "406ms", "false", home, "8ms", "0", "false", "true", "kitchen", "", "true", "10s"})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
tc.malleate()
|
||||
actualCfg, err := GetConfigFromFile(tc.filePath)
|
||||
if tc.expectedError != "" {
|
||||
s.Require().NoError(err)
|
||||
s.Require().Contains(err.Error(), tc.expectedError)
|
||||
return
|
||||
}
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().EqualValues(tc.expectedCfg(), actualCfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
var sink interface{}
|
||||
|
||||
func BenchmarkDetailString(b *testing.B) {
|
||||
@ -791,7 +881,7 @@ func BenchmarkDetailString(b *testing.B) {
|
||||
AllowDownloadBinaries: true,
|
||||
UnsafeSkipBackup: true,
|
||||
PollInterval: 450 * time.Second,
|
||||
PreupgradeMaxRetries: 1e7,
|
||||
PreUpgradeMaxRetries: 1e7,
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
|
||||
@ -30,7 +30,12 @@ func NewAddUpgradeCmd() *cobra.Command {
|
||||
|
||||
// AddUpgrade adds upgrade info to manifest
|
||||
func AddUpgrade(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
configPath, err := cmd.Flags().GetString(cosmovisor.FlagCosmovisorConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get config flag: %w", err)
|
||||
}
|
||||
|
||||
cfg, err := cosmovisor.GetConfigFromFile(configPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -7,11 +7,13 @@ import (
|
||||
)
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Display cosmovisor config (prints environment variables used by cosmovisor).",
|
||||
Use: "config",
|
||||
Short: "Display cosmovisor config.",
|
||||
Long: `Display cosmovisor config. If a config file is provided, it will display the config from the file,
|
||||
otherwise it will display the config from the environment variables.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
cfg, err := cosmovisor.GetConfigFromFile(cmd.Flag(cosmovisor.FlagCosmovisorConfig).Value.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -15,14 +15,20 @@ import (
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
)
|
||||
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init <path to executable>",
|
||||
Short: "Initialize a cosmovisor daemon home directory.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return InitializeCosmovisor(nil, args)
|
||||
},
|
||||
func NewIntCmd() *cobra.Command {
|
||||
initCmd := &cobra.Command{
|
||||
Use: "init <path to executable>",
|
||||
Short: "Initialize a cosmovisor daemon home directory.",
|
||||
Long: `Initialize a cosmovisor daemon home directory with the provided executable.
|
||||
Configuration file is initialized at the default path (<-home->/cosmovisor/config.toml).`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return InitializeCosmovisor(nil, args)
|
||||
},
|
||||
}
|
||||
|
||||
return initCmd
|
||||
}
|
||||
|
||||
// InitializeCosmovisor initializes the cosmovisor directories, current link, and initial executable.
|
||||
@ -88,12 +94,20 @@ func InitializeCosmovisor(logger log.Logger, args []string) error {
|
||||
}
|
||||
logger.Info(fmt.Sprintf("the current symlink points to: %q", cur))
|
||||
|
||||
filePath, err := cfg.Export()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to export configuration: %w", err)
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("config file present at: %s", filePath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConfigForInitCmd gets just the configuration elements needed to initialize cosmovisor.
|
||||
func getConfigForInitCmd() (*cosmovisor.Config, error) {
|
||||
var errs []error
|
||||
|
||||
// Note: Not using GetConfigFromEnv here because that checks that the directories already exist.
|
||||
// We also don't care about the rest of the configuration stuff in here.
|
||||
cfg := &cosmovisor.Config{
|
||||
@ -105,19 +119,27 @@ func getConfigForInitCmd() (*cosmovisor.Config, error) {
|
||||
if cfg.ColorLogs, err = cosmovisor.BooleanOption(cosmovisor.EnvColorLogs, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if cfg.TimeFormatLogs, err = cosmovisor.TimeFormatOptionFromEnv(cosmovisor.EnvTimeFormatLogs, time.Kitchen); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
// if backup is not set, use the home directory
|
||||
if cfg.DataBackupPath == "" {
|
||||
cfg.DataBackupPath = cfg.Home
|
||||
}
|
||||
|
||||
if len(cfg.Name) == 0 {
|
||||
errs = append(errs, fmt.Errorf("%s is not set", cosmovisor.EnvName))
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(cfg.Home) == 0:
|
||||
errs = append(errs, fmt.Errorf("%s is not set", cosmovisor.EnvHome))
|
||||
case !filepath.IsAbs(cfg.Home):
|
||||
errs = append(errs, fmt.Errorf("%s must be an absolute path", cosmovisor.EnvHome))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return cfg, errors.Join(errs...)
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -18,7 +20,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
notset = " is not set"
|
||||
notset = " is not set"
|
||||
cosmovisorDirName = "cosmovisor"
|
||||
|
||||
cfgFileWithExt = "config.toml"
|
||||
)
|
||||
|
||||
type InitTestSuite struct {
|
||||
@ -79,6 +84,7 @@ func (s *InitTestSuite) clearEnv() *cosmovisorInitEnv {
|
||||
for envVar := range rv.ToMap() {
|
||||
rv.Set(envVar, os.Getenv(envVar))
|
||||
s.Require().NoError(os.Unsetenv(envVar))
|
||||
viper.Reset()
|
||||
}
|
||||
return &rv
|
||||
}
|
||||
@ -111,6 +117,26 @@ func (s *InitTestSuite) setEnv(t *testing.T, env *cosmovisorInitEnv) { //nolint:
|
||||
}
|
||||
}
|
||||
|
||||
// readStdInpFromFile reads the provided data as if it were a standard input.
|
||||
func (s *InitTestSuite) readStdInpFromFile(data []byte) {
|
||||
// Create a temporary file and write the test input into it
|
||||
tmpfile, err := os.CreateTemp("", "test")
|
||||
if err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
// write the test input into the temporary file
|
||||
if _, err := tmpfile.Write(data); err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.Seek(0, 0); err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
os.Stdin = tmpfile
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.Reader = BufferedPipe{}
|
||||
_ io.Writer = BufferedPipe{}
|
||||
@ -247,7 +273,7 @@ func (p *BufferedPipe) panicIfStarted(msg string) {
|
||||
func (s *InitTestSuite) NewCapturingLogger() (*BufferedPipe, log.Logger) {
|
||||
bufferedStdOut, err := StartNewBufferedPipe("stdout", os.Stdout)
|
||||
s.Require().NoError(err, "creating stdout buffered pipe")
|
||||
logger := log.NewLogger(bufferedStdOut, log.ColorOption(false), log.TimeFormatOption(time.RFC3339Nano)).With(log.ModuleKey, "cosmovisor")
|
||||
logger := log.NewLogger(bufferedStdOut, log.ColorOption(false), log.TimeFormatOption(time.RFC3339Nano)).With(log.ModuleKey, cosmovisorDirName)
|
||||
return &bufferedStdOut, logger
|
||||
}
|
||||
|
||||
@ -360,7 +386,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "pear",
|
||||
}
|
||||
genDir := filepath.Join(env.Home, "cosmovisor", "genesis")
|
||||
genDir := filepath.Join(env.Home, cosmovisorDirName, "genesis")
|
||||
genBin := filepath.Join(genDir, "bin")
|
||||
require.NoError(t, os.MkdirAll(genDir, 0o755), "creating genesis directory")
|
||||
require.NoError(t, copyFile(hwExe, genBin), "copying exe to genesis/bin")
|
||||
@ -380,7 +406,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
}
|
||||
// Create the genesis bin executable path fully as a directory (instead of a file).
|
||||
// That should get through all the other stuff, but error when EnsureBinary is called.
|
||||
genBinExe := filepath.Join(env.Home, "cosmovisor", "genesis", "bin", env.Name)
|
||||
genBinExe := filepath.Join(env.Home, cosmovisorDirName, "genesis", "bin", env.Name)
|
||||
require.NoError(t, os.MkdirAll(genBinExe, 0o755))
|
||||
expErr := fmt.Sprintf("%s is not a regular file", env.Name)
|
||||
// Check the log messages just to make sure it's erroring where expecting.
|
||||
@ -416,7 +442,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "orange",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
require.NoError(t, os.MkdirAll(rootDir, 0o755))
|
||||
curLn := filepath.Join(rootDir, "current")
|
||||
genDir := filepath.Join(rootDir, "genesis")
|
||||
@ -465,8 +491,8 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "blank",
|
||||
}
|
||||
curLn := filepath.Join(env.Home, "cosmovisor", "current")
|
||||
genBinDir := filepath.Join(env.Home, "cosmovisor", "genesis", "bin")
|
||||
curLn := filepath.Join(env.Home, cosmovisorDirName, "current")
|
||||
genBinDir := filepath.Join(env.Home, cosmovisorDirName, "genesis", "bin")
|
||||
genBinExe := filepath.Join(genBinDir, env.Name)
|
||||
expInLog := []string{
|
||||
"checking on the genesis/bin directory",
|
||||
@ -476,6 +502,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
fmt.Sprintf("making sure %q is executable", genBinExe),
|
||||
"checking on the current symlink and creating it if needed",
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinExe),
|
||||
fmt.Sprintf("config file present at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
s.setEnv(s.T(), env)
|
||||
@ -508,7 +535,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "nocur",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
genBinDir := filepath.Join(rootDir, "genesis", "bin")
|
||||
genBinDirExe := filepath.Join(genBinDir, env.Name)
|
||||
require.NoError(t, os.MkdirAll(genBinDir, 0o755), "making genesis bin dir")
|
||||
@ -528,6 +555,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
fmt.Sprintf("the %q file already exists", genBinDirExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinDirExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinDirExe),
|
||||
fmt.Sprintf("config file present at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
s.setEnv(t, env)
|
||||
@ -548,7 +576,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "emptygen",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
genBinDir := filepath.Join(rootDir, "genesis", "bin")
|
||||
genBinExe := filepath.Join(genBinDir, env.Name)
|
||||
require.NoError(t, os.MkdirAll(genBinDir, 0o755), "making genesis bin dir")
|
||||
@ -560,6 +588,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
fmt.Sprintf("copying executable into place: %q", genBinExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinExe),
|
||||
fmt.Sprintf("config file present at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
s.setEnv(t, env)
|
||||
@ -573,4 +602,111 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
assert.Contains(t, bufferStr, exp)
|
||||
}
|
||||
})
|
||||
|
||||
s.T().Run("ask to override (y/n) the existing config file", func(t *testing.T) {
|
||||
})
|
||||
|
||||
s.T().Run("init command exports configs to default path", func(t *testing.T) {
|
||||
testDir := s.T().TempDir()
|
||||
env := &cosmovisorInitEnv{
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "emptygen",
|
||||
}
|
||||
|
||||
s.setEnv(t, env)
|
||||
buffer, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
err := InitializeCosmovisor(logger, []string{hwExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
bufferBz := buffer.Collect()
|
||||
bufferStr := string(bufferBz)
|
||||
assert.Contains(t, bufferStr, fmt.Sprintf("config file present at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *InitTestSuite) TestInitializeCosmovisorWithOverrideCfg() {
|
||||
initEnv := s.clearEnv()
|
||||
defer s.setEnv(nil, initEnv)
|
||||
|
||||
tmpExe := s.CreateHelloWorld(0o755)
|
||||
testDir := s.T().TempDir()
|
||||
homePath := filepath.Join(testDir, "backup")
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
cfg *cosmovisor.Config
|
||||
override bool
|
||||
}{
|
||||
{
|
||||
name: "yes override",
|
||||
input: "y\n",
|
||||
cfg: &cosmovisor.Config{
|
||||
Home: homePath,
|
||||
Name: "old_test",
|
||||
DataBackupPath: homePath,
|
||||
},
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
name: "no override",
|
||||
input: "n\n",
|
||||
cfg: &cosmovisor.Config{
|
||||
Home: homePath,
|
||||
Name: "old_test",
|
||||
DataBackupPath: homePath,
|
||||
},
|
||||
override: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
// create a root cosmovisor directory
|
||||
require.NoError(t, os.MkdirAll(tc.cfg.Root(), 0o755), "making root dir")
|
||||
|
||||
// create a config file in the default location
|
||||
file, err := os.Create(tc.cfg.DefaultCfgPath())
|
||||
require.NoError(t, err)
|
||||
|
||||
// write the config to the file
|
||||
err = toml.NewEncoder(file).Encode(tc.cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
s.readStdInpFromFile([]byte(tc.input))
|
||||
|
||||
_, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
|
||||
// override the daemon name in environment file
|
||||
// if override is true (y), then the name should be updated in the config file
|
||||
// otherwise (n), the name should not be updated in the config file
|
||||
s.setEnv(t, &cosmovisorInitEnv{
|
||||
Home: tc.cfg.Home,
|
||||
Name: "update_name",
|
||||
})
|
||||
|
||||
err = InitializeCosmovisor(logger, []string{tmpExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
|
||||
cfg := &cosmovisor.Config{}
|
||||
// read the config file
|
||||
cfgFile, err := os.Open(tc.cfg.DefaultCfgPath())
|
||||
require.NoError(t, err)
|
||||
defer cfgFile.Close()
|
||||
|
||||
err = toml.NewDecoder(cfgFile).Decode(cfg)
|
||||
require.NoError(t, err)
|
||||
if tc.override {
|
||||
// check if the name is updated
|
||||
// basically, override the existing config file
|
||||
assert.Equal(t, "update_name", cfg.Name)
|
||||
} else {
|
||||
// daemon name should not be updated
|
||||
assert.Equal(t, tc.cfg.Name, cfg.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
func NewRootCmd() *cobra.Command {
|
||||
@ -12,12 +14,13 @@ func NewRootCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(
|
||||
initCmd,
|
||||
NewIntCmd(),
|
||||
runCmd,
|
||||
configCmd,
|
||||
NewVersionCmd(),
|
||||
NewAddUpgradeCmd(),
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().StringP(cosmovisor.FlagCosmovisorConfig, "c", "", "path to cosmovisor config file")
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
@ -1,24 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run an APP command.",
|
||||
Use: "run",
|
||||
Short: "Run an APP command.",
|
||||
Long: `Run an APP command. This command is intended to be used by the cosmovisor binary.
|
||||
Provide cosmovisor config file path in command args or set env variables to load configuration.
|
||||
`,
|
||||
SilenceUsage: true,
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return run(args)
|
||||
cfgPath, args, err := parseCosmovisorConfig(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse cosmovisor config: %w", err)
|
||||
}
|
||||
|
||||
return run(cfgPath, args)
|
||||
},
|
||||
}
|
||||
|
||||
// run runs the configured program with the given args and monitors it for upgrades.
|
||||
func run(args []string, options ...RunOption) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
func run(cfgPath string, args []string, options ...RunOption) error {
|
||||
cfg, err := cosmovisor.GetConfigFromFile(cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -47,3 +58,24 @@ func run(args []string, options ...RunOption) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func parseCosmovisorConfig(args []string) (string, []string, error) {
|
||||
var configFilePath string
|
||||
for i, arg := range args {
|
||||
// Check if the argument is the config flag
|
||||
if strings.EqualFold(arg, fmt.Sprintf("--%s", cosmovisor.FlagCosmovisorConfig)) ||
|
||||
strings.EqualFold(arg, fmt.Sprintf("-%s", cosmovisor.FlagCosmovisorConfig)) {
|
||||
// Check if there is an argument after the flag which should be the config file path
|
||||
if i+1 >= len(args) {
|
||||
return "", nil, fmt.Errorf("--%s requires an argument", cosmovisor.FlagCosmovisorConfig)
|
||||
}
|
||||
|
||||
configFilePath = args[i+1]
|
||||
// Remove the flag and its value from the arguments
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return configFilePath, args, nil
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ func printVersion(cmd *cobra.Command, args []string, noAppVersion bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := run(append([]string{"version"}, args...)); err != nil {
|
||||
if err := run("", append([]string{"version"}, args...)); err != nil {
|
||||
return fmt.Errorf("failed to run version command: %w", err)
|
||||
}
|
||||
|
||||
@ -62,6 +62,7 @@ func printVersionJSON(cmd *cobra.Command, args []string, noAppVersion bool) erro
|
||||
|
||||
buf := new(strings.Builder)
|
||||
if err := run(
|
||||
"",
|
||||
[]string{"version", "--long", "--output", "json"},
|
||||
StdOutRunOption(buf),
|
||||
); err != nil {
|
||||
|
||||
@ -6,4 +6,5 @@ const (
|
||||
FlagCosmovisorOnly = "cosmovisor-only"
|
||||
FlagForce = "force"
|
||||
FlagUpgradeHeight = "upgrade-height"
|
||||
FlagCosmovisorConfig = "cosmovisor-config"
|
||||
)
|
||||
|
||||
@ -201,7 +201,7 @@ func (l Launcher) doBackup() error {
|
||||
|
||||
// doCustomPreUpgrade executes the custom preupgrade script if provided.
|
||||
func (l Launcher) doCustomPreUpgrade() error {
|
||||
if l.cfg.CustomPreupgrade == "" {
|
||||
if l.cfg.CustomPreUpgrade == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -221,7 +221,7 @@ func (l Launcher) doCustomPreUpgrade() error {
|
||||
}
|
||||
|
||||
// check if preupgradeFile is executable file
|
||||
preupgradeFile := filepath.Join(l.cfg.Home, "cosmovisor", l.cfg.CustomPreupgrade)
|
||||
preupgradeFile := filepath.Join(l.cfg.Home, "cosmovisor", l.cfg.CustomPreUpgrade)
|
||||
l.logger.Info("looking for COSMOVISOR_CUSTOM_PREUPGRADE file", "file", preupgradeFile)
|
||||
info, err := os.Stat(preupgradeFile)
|
||||
if err != nil {
|
||||
@ -264,8 +264,8 @@ func (l Launcher) doCustomPreUpgrade() error {
|
||||
func (l *Launcher) doPreUpgrade() error {
|
||||
counter := 0
|
||||
for {
|
||||
if counter > l.cfg.PreupgradeMaxRetries {
|
||||
return fmt.Errorf("pre-upgrade command failed. reached max attempt of retries - %d", l.cfg.PreupgradeMaxRetries)
|
||||
if counter > l.cfg.PreUpgradeMaxRetries {
|
||||
return fmt.Errorf("pre-upgrade command failed. reached max attempt of retries - %d", l.cfg.PreUpgradeMaxRetries)
|
||||
}
|
||||
|
||||
if err := l.executePreUpgradeCmd(); err != nil {
|
||||
|
||||
@ -272,7 +272,7 @@ func (s *processTestSuite) TestLaunchProcessWithDownloadsAndMissingPreupgrade()
|
||||
AllowDownloadBinaries: true,
|
||||
PollInterval: 100,
|
||||
UnsafeSkipBackup: true,
|
||||
CustomPreupgrade: "missing.sh",
|
||||
CustomPreUpgrade: "missing.sh",
|
||||
}
|
||||
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmovisor")
|
||||
upgradeFilename := cfg.UpgradeInfoFilePath()
|
||||
@ -308,7 +308,7 @@ func (s *processTestSuite) TestLaunchProcessWithDownloadsAndPreupgrade() {
|
||||
AllowDownloadBinaries: true,
|
||||
PollInterval: 100,
|
||||
UnsafeSkipBackup: true,
|
||||
CustomPreupgrade: "preupgrade.sh",
|
||||
CustomPreUpgrade: "preupgrade.sh",
|
||||
}
|
||||
buf := newBuffer() // inspect output using buf.String()
|
||||
logger := log.NewLogger(buf).With(log.ModuleKey, "cosmovisor")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user