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 #20948

Closed
wants to merge 6 commits into from
Closed
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
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 @@ -209,13 +216,18 @@ func GetConfigFromEnv() (*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 @@ -341,6 +353,20 @@ func (cfg *Config) validate() []error {
errs = append(errs, fmt.Errorf("%s is not a directory", cfg.Root()))
}
}
// validate DataPath
switch {
case cfg.DataPath == "":
break
case !filepath.IsAbs(cfg.DataPath):
errs = append(errs, fmt.Errorf("%s must be an absolute path", cfg.DataPath))
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 @@ -539,6 +565,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 @@ -553,6 +580,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
193 changes: 120 additions & 73 deletions tools/cosmovisor/args_test.go

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions tools/cosmovisor/cmd/cosmovisor/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,10 @@ func getConfigForInitCmd() (*cosmovisor.Config, error) {
cfg.DataBackupPath = cfg.Home
}

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

if len(cfg.Name) == 0 {
errs = append(errs, fmt.Errorf("%s is not set", cosmovisor.EnvName))
}
Expand Down
4 changes: 2 additions & 2 deletions tools/cosmovisor/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,8 @@ func (l Launcher) doBackup() error {

l.logger.Info("starting to take backup of data directory", "backup start time", st)

// copy the $DAEMON_HOME/data to a backup dir
if err = copy.Copy(filepath.Join(l.cfg.Home, "data"), dst); err != nil {
// copy the data dir to a backup dir
if err = copy.Copy(l.cfg.DataPath, dst); err != nil {
return fmt.Errorf("error while taking data backup: %w", err)
}

Expand Down
68 changes: 62 additions & 6 deletions tools/cosmovisor/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func (s *processTestSuite) TestLaunchProcess() {
// binaries from testdata/validate directory
require := s.Require()
home := copyTestData(s.T(), "validate")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", PollInterval: 20, UnsafeSkipBackup: true}
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", DataPath: dataPath, PollInterval: 20, UnsafeSkipBackup: true}
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")

// should run the genesis binary and produce expected output
Expand Down Expand Up @@ -79,7 +80,8 @@ 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}
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", DataPath: dataPath, PollInterval: 20, UnsafeSkipBackup: true, DisableRecase: true}
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")

// should run the genesis binary and produce expected output
Expand Down Expand Up @@ -123,7 +125,8 @@ func (s *processTestSuite) TestLaunchProcessWithRestartDelay() {
// binaries from testdata/validate directory
require := s.Require()
home := copyTestData(s.T(), "validate")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", RestartDelay: 5 * time.Second, PollInterval: 20, UnsafeSkipBackup: true}
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", DataPath: dataPath, RestartDelay: 5 * time.Second, PollInterval: 20, UnsafeSkipBackup: true}
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")

// should run the genesis binary and produce expected output
Expand All @@ -149,12 +152,13 @@ func (s *processTestSuite) TestLaunchProcessWithRestartDelay() {
}
}

// TestPlanShutdownGrace will test upgrades without lower case plan names
// TestPlanShutdownGrace will wait for clean up before sending the kill signal
func (s *processTestSuite) TestPlanShutdownGrace() {
// binaries from testdata/validate directory
require := s.Require()
home := copyTestData(s.T(), "dontdie")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", PollInterval: 20, UnsafeSkipBackup: true, ShutdownGrace: 2 * time.Second}
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", DataPath: dataPath, PollInterval: 20, UnsafeSkipBackup: true, ShutdownGrace: 2 * time.Second}
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")

// should run the genesis binary and produce expected output
Expand Down Expand Up @@ -203,7 +207,8 @@ func (s *processTestSuite) TestLaunchProcessWithDownloads() {
// chain3-zip_dir - doesn't upgrade
require := s.Require()
home := copyTestData(s.T(), "download")
cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true, PollInterval: 100, UnsafeSkipBackup: true}
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{Home: home, Name: "autod", DataPath: dataPath, AllowDownloadBinaries: true, PollInterval: 100, UnsafeSkipBackup: true}
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmovisor")
upgradeFilename := cfg.UpgradeInfoFilePath()

Expand Down Expand Up @@ -266,9 +271,11 @@ func (s *processTestSuite) TestLaunchProcessWithDownloadsAndMissingPreupgrade()
// chain3-zip_dir - doesn't upgrade
require := s.Require()
home := copyTestData(s.T(), "download")
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{
Home: home,
Name: "autod",
DataPath: dataPath,
AllowDownloadBinaries: true,
PollInterval: 100,
UnsafeSkipBackup: true,
Expand Down Expand Up @@ -302,9 +309,11 @@ func (s *processTestSuite) TestLaunchProcessWithDownloadsAndPreupgrade() {
// chain3-zip_dir - doesn't upgrade
require := s.Require()
home := copyTestData(s.T(), "download")
dataPath := filepath.Join(home, "data")
cfg := &cosmovisor.Config{
Home: home,
Name: "autod",
DataPath: dataPath,
AllowDownloadBinaries: true,
PollInterval: 100,
UnsafeSkipBackup: true,
Expand Down Expand Up @@ -370,6 +379,53 @@ func (s *processTestSuite) TestLaunchProcessWithDownloadsAndPreupgrade() {
require.Equal(cfg.UpgradeBin("chain3"), currentBin)
}

// TestPlanCustomDataLocation will detect upgrade when chain data dir is not located in the default path
// the custom path should be provided as an absolute path to the configs
func (s *processTestSuite) TestPlanCustomDataLocation() {
// binaries from testdata/validate directory
require := s.Require()
home := copyTestData(s.T(), "custom-data-path")
dataPath := filepath.Join(home, "custom-location/data")
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", DataPath: dataPath, PollInterval: 20, UnsafeSkipBackup: 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)
}

// TestSkipUpgrade tests heights that are identified to be skipped and return if upgrade height matches the skip heights
func TestSkipUpgrade(t *testing.T) {
cases := []struct {
Expand Down
Original file line number Diff line number Diff line change
@@ -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!!!
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

echo chain 2 is live!
echo Args: $@
sleep 1
echo Finished successfully
Loading