Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tools/cosmovisor): Introduce a new optional config variable for setting custom path to application data directory #21971

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
13 changes: 12 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
Member

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,14 +128,24 @@ 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
├── config
├── data
└── cosmovisor
```
Otherwise, if you decide to set `$DAEMON_HOME` to an arbitrary location, such as `$HOME/.simapp_manager`, and use the `--home` flag of the application as `$HOME/.simapp`, you must explicitly set the `DAEMON_DATA_DIR` to the application's data directory path, `$HOME/.simapp/data`. Your directory structure will look like this:

```text
$HOME
├── .simapp
│ ├── config
│ └── data
└── .simapp_manager
└── cosmovisor
```

## Usage

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
Loading