Skip to content
Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tools/cosmovisor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
### Improvements

* [#21462](https://github.com/cosmos/cosmos-sdk/pull/21462) Pass `stdin` to binary.
* [#21971](https://github.com/cosmos/cosmos-sdk/pull/21971) Support Custom path to application data directory

### Features

Expand Down
3 changes: 2 additions & 1 deletion tools/cosmovisor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ All arguments passed to `cosmovisor run` will be passed to the application binar

* `DAEMON_HOME` is the location where the `cosmovisor/` directory is kept that contains the genesis binary, the upgrade binaries, and any additional auxiliary files associated with each binary (e.g. `$HOME/.gaiad`, `$HOME/.regend`, `$HOME/.simd`, etc.).
* `DAEMON_NAME` is the name of the binary itself (e.g. `gaiad`, `regend`, `simd`, etc.).
* `DAEMON_DATA_DIR` option to set an absolute path to the `data` directory. this path is used to detect upgrade info file and backups. If not set, `$DAEMON_HOME/data` is used.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the backup changes in this PR, but where are the data folder changes for the node?
How do you ensure the node saves its data to DAEMON_DATA_DIR?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Node data is written to the location specified by the underlying application's --home flag. Currently, the DAEMON_HOME for Cosmovisor must always be the same as the --home location. This PR decouples the two, allowing the Cosmovisor DAEMON_HOME to be set to any arbitrary location, as long as DAEMON_DATA_DIR points to the --home/data directory.
I've added an example to the README file.

* `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*), if set to `true`, will enable auto-downloading of new binaries (for security reasons, this is intended for full nodes rather than validators). By default, `cosmovisor` will not auto-download new binaries.
* `DAEMON_DOWNLOAD_MUST_HAVE_CHECKSUM` (*optional*, default = `false`), if `true` cosmovisor will require that a checksum is provided in the upgrade plan for the binary to be downloaded. If `false`, cosmovisor will not require a checksum to be provided, but still check the checksum if one is provided.
* `DAEMON_RESTART_AFTER_UPGRADE` (*optional*, default = `true`), if `true`, restarts the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. Otherwise (`false`), `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note restart is only after the upgrade and does not auto-restart the subprocess after an error occurs.
Expand Down Expand Up @@ -127,7 +128,7 @@ All arguments passed to `cosmovisor run` will be passed to the application binar

The `cosmovisor/` directory includes a subdirectory for each version of the application (i.e. `genesis` or `upgrades/<name>`). Within each subdirectory is the application binary (i.e. `bin/$DAEMON_NAME`) and any additional auxiliary files associated with each binary. `current` is a symbolic link to the currently active directory (i.e. `genesis` or `upgrades/<name>`). The `name` variable in `upgrades/<name>` is the lowercased URI-encoded name of the upgrade as specified in the upgrade module plan. Note that the upgrade name path are normalized to be lowercased: for instance, `MyUpgrade` is normalized to `myupgrade`, and its path is `upgrades/myupgrade`.

Please note that `$DAEMON_HOME/cosmovisor` only stores the *application binaries*. The `cosmovisor` binary itself can be stored in any typical location (e.g. `/usr/local/bin`). The application will continue to store its data in the default data directory (e.g. `$HOME/.simapp`) or the data directory specified with the `--home` flag. `$DAEMON_HOME` is dependent of the data directory and must be set to the same directory as the data directory, you will end up with a configuration like the following:
Please note that `$DAEMON_HOME/cosmovisor` only stores the *application binaries*. The `cosmovisor` binary itself can be stored in any typical location (e.g. `/usr/local/bin`). The application will continue to store its data in the default data directory (e.g. `$HOME/.simapp`) or the data directory specified with the `--home` flag. `$DAEMON_HOME` is independent of the data directory and can be set to any location and the actual data directory path can be set through `DAEMON_DATA_DIR` config. If you set `$DAEMON_HOME` to the same directory as the data directory, you will end up with a configuration like the following:

```text
.simapp
Expand Down
30 changes: 29 additions & 1 deletion tools/cosmovisor/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const (
EnvShutdownGrace = "DAEMON_SHUTDOWN_GRACE"
EnvSkipBackup = "UNSAFE_SKIP_BACKUP"
EnvDataBackupPath = "DAEMON_DATA_BACKUP_DIR"
EnvDataPath = "DAEMON_DATA_DIR"
EnvInterval = "DAEMON_POLL_INTERVAL"
EnvPreupgradeMaxRetries = "DAEMON_PREUPGRADE_MAX_RETRIES"
EnvDisableLogs = "COSMOVISOR_DISABLE_LOGS"
Expand All @@ -45,6 +46,7 @@ const (
genesisDir = "genesis"
upgradesDir = "upgrades"
currentLink = "current"
dataDir = "data"

cfgFileName = "config"
cfgExtension = "toml"
Expand All @@ -62,6 +64,7 @@ type Config struct {
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"`
DataPath string `toml:"daemon_data_dir" mapstructure:"daemon_data_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"`
Expand Down Expand Up @@ -106,7 +109,11 @@ func (cfg *Config) BaseUpgradeDir() string {

// UpgradeInfoFilePath is the expected upgrade-info filename created by `x/upgrade/keeper`.
func (cfg *Config) UpgradeInfoFilePath() string {
return filepath.Join(cfg.Home, "data", upgradetypes.UpgradeInfoFilename)
return filepath.Join(cfg.DataPath, upgradetypes.UpgradeInfoFilename)
}

func (cfg *Config) DefaultDataDirPath() string {
return filepath.Join(cfg.Home, dataDir)
}

// SymLinkToGenesis creates a symbolic link from "./current" to the genesis directory.
Expand Down Expand Up @@ -210,13 +217,18 @@ func GetConfigFromEnv(skipValidate bool) (*Config, error) {
Home: os.Getenv(EnvHome),
Name: os.Getenv(EnvName),
DataBackupPath: os.Getenv(EnvDataBackupPath),
DataPath: os.Getenv(EnvDataPath),
CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade),
}

if cfg.DataBackupPath == "" {
cfg.DataBackupPath = cfg.Home
}

if cfg.DataPath == "" {
cfg.DataPath = cfg.DefaultDataDirPath()
}

var err error
if cfg.AllowDownloadBinaries, err = BooleanOption(EnvDownloadBin, false); err != nil {
errs = append(errs, err)
Expand Down Expand Up @@ -344,6 +356,20 @@ func (cfg *Config) validate() []error {
errs = append(errs, fmt.Errorf("%s is not a directory", cfg.Root()))
}
}
// validate DataPath
switch {
case cfg.DataPath == "":
errs = append(errs, fmt.Errorf("%s is not set", EnvDataPath))
case !filepath.IsAbs(cfg.DataPath):
errs = append(errs, fmt.Errorf("%s must be an absolute path", EnvDataPath))
default:
switch info, err := os.Stat(cfg.DataPath); {
case err != nil:
errs = append(errs, fmt.Errorf("%q must be a valid directory: %w", cfg.DataPath, err))
case !info.IsDir():
errs = append(errs, fmt.Errorf("%q must be a valid directory", cfg.DataPath))
}
}

// check the DataBackupPath
if cfg.UnsafeSkipBackup {
Expand Down Expand Up @@ -542,6 +568,7 @@ func (cfg Config) DetailString() string {
{EnvInterval, cfg.PollInterval.String()},
{EnvSkipBackup, fmt.Sprintf("%t", cfg.UnsafeSkipBackup)},
{EnvDataBackupPath, cfg.DataBackupPath},
{EnvDataPath, cfg.DataPath},
{EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreUpgradeMaxRetries)},
{EnvDisableLogs, fmt.Sprintf("%t", cfg.DisableLogs)},
{EnvColorLogs, fmt.Sprintf("%t", cfg.ColorLogs)},
Expand All @@ -556,6 +583,7 @@ func (cfg Config) DetailString() string {
{"Genesis Bin", cfg.GenesisBin()},
{"Monitored File", cfg.UpgradeInfoFilePath()},
{"Data Backup Dir", cfg.DataBackupPath},
{"Data Dir", cfg.DataPath},
}

var sb strings.Builder
Expand Down
Loading