From c347f6c9eea2bdac5489e68180d005aa66aad56a Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 10 Sep 2024 12:14:12 +1000 Subject: [PATCH 01/24] wip: batch upgrade --- .../cmd/cosmovisor/batch_upgrade.go | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go new file mode 100644 index 00000000000..5a6c7b281e8 --- /dev/null +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -0,0 +1,38 @@ +package main + +import ( + "fmt" + "strings" + + "github.com/spf13/cobra" + + "cosmossdk.io/tools/cosmovisor" +) + +func NewBatchAddUpgradeCmd() *cobra.Command { + addUpgrade := &cobra.Command{ + Use: "add-batch-upgrade : : .. :", + Short: "Add APP upgrades binary to cosmovisor", + SilenceUsage: true, + Args: cobra.ArbitraryArgs, + RunE: AddBatchUpgrade, + } + + addUpgrade.Flags().Bool(cosmovisor.FlagForce, false, "overwrite existing upgrade binary / upgrade-info.json file") + addUpgrade.Flags().Int64(cosmovisor.FlagUpgradeHeight, 0, "define a height at which to upgrade the binary automatically (without governance proposal)") + + return addUpgrade +} + +func AddBatchUpgrade(cmd *cobra.Command, args []string) error { + for i, as := range args { + a := strings.Split(as, ":") + if len(a) != 2 { + return fmt.Errorf("argument at position %d (%s) is invalid", i, as) + } + if err := AddUpgrade(cmd, a); err != nil { + return err + } + } + return nil +} From cd54cb2db61accc938476aefb852020293762919 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 14:49:14 +1000 Subject: [PATCH 02/24] add logic for batch upgrade cmd; logic for swapping upgrade files --- tools/cosmovisor/args.go | 4 + .../cosmovisor/cmd/cosmovisor/add_upgrade.go | 63 +++++++++------ .../cmd/cosmovisor/batch_upgrade.go | 61 ++++++++++++--- tools/cosmovisor/process.go | 76 +++++++++++++++++++ 4 files changed, 169 insertions(+), 35 deletions(-) diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index be94fbadaf6..7b188ad1bff 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -109,6 +109,10 @@ func (cfg *Config) UpgradeInfoFilePath() string { return filepath.Join(cfg.Home, "data", upgradetypes.UpgradeInfoFilename) } +func (cfg *Config) UpgradeInfoBatchFilePath() string { + return cfg.UpgradeInfoFilePath() + ".batch" +} + // SymLinkToGenesis creates a symbolic link from "./current" to the genesis directory. func (cfg *Config) SymLinkToGenesis() (string, error) { genesis := filepath.Join(cfg.Root(), genesisDir) diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index a197f33bc1d..4675e95f49d 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -19,7 +19,7 @@ func NewAddUpgradeCmd() *cobra.Command { Short: "Add APP upgrade binary to cosmovisor", SilenceUsage: true, Args: cobra.ExactArgs(2), - RunE: AddUpgrade, + RunE: AddUpgradeCmd, } addUpgrade.Flags().Bool(cosmovisor.FlagForce, false, "overwrite existing upgrade binary / upgrade-info.json file") @@ -28,26 +28,13 @@ func NewAddUpgradeCmd() *cobra.Command { return addUpgrade } -// AddUpgrade adds upgrade info to manifest -func AddUpgrade(cmd *cobra.Command, args []string) error { - 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 - } - +func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgradeName, executablePath, upgradeInfoPath string) error { logger := cfg.Logger(os.Stdout) - upgradeName := args[0] if !cfg.DisableRecase { - upgradeName = strings.ToLower(args[0]) + upgradeName = strings.ToLower(upgradeName) } - executablePath := args[1] if _, err := os.Stat(executablePath); err != nil { if os.IsNotExist(err) { return fmt.Errorf("invalid executable path: %w", err) @@ -68,11 +55,6 @@ func AddUpgrade(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to read binary: %w", err) } - force, err := cmd.Flags().GetBool(cosmovisor.FlagForce) - if err != nil { - return fmt.Errorf("failed to get force flag: %w", err) - } - if err := saveOrAbort(cfg.UpgradeBin(upgradeName), executableData, force); err != nil { return err } @@ -80,9 +62,7 @@ func AddUpgrade(cmd *cobra.Command, args []string) error { logger.Info(fmt.Sprintf("Using %s for %s upgrade", executablePath, upgradeName)) logger.Info(fmt.Sprintf("Upgrade binary located at %s", cfg.UpgradeBin(upgradeName))) - if upgradeHeight, err := cmd.Flags().GetInt64(cosmovisor.FlagUpgradeHeight); err != nil { - return fmt.Errorf("failed to get upgrade-height flag: %w", err) - } else if upgradeHeight > 0 { + if upgradeHeight > 0 { plan := upgradetypes.Plan{Name: upgradeName, Height: upgradeHeight} if err := plan.ValidateBasic(); err != nil { panic(fmt.Errorf("something is wrong with cosmovisor: %w", err)) @@ -104,6 +84,41 @@ func AddUpgrade(cmd *cobra.Command, args []string) error { return nil } +func GetConfig(cmd *cobra.Command) (*cosmovisor.Config, error) { + configPath, err := cmd.Flags().GetString(cosmovisor.FlagCosmovisorConfig) + if err != nil { + return nil, fmt.Errorf("failed to get config flag: %w", err) + } + + cfg, err := cosmovisor.GetConfigFromFile(configPath) + if err != nil { + return nil, err + } + return cfg, nil +} + +// AddUpgrade adds upgrade info to manifest +func AddUpgradeCmd(cmd *cobra.Command, args []string) error { + cfg, err := GetConfig(cmd) + if err != nil { + return err + } + + upgradeName, executablePath := args[0], args[1] + + force, err := cmd.Flags().GetBool(cosmovisor.FlagForce) + if err != nil { + return fmt.Errorf("failed to get force flag: %w", err) + } + + upgradeHeight, err := cmd.Flags().GetInt64(cosmovisor.FlagUpgradeHeight) + if err != nil { + return fmt.Errorf("failed to get upgrade-height flag: %w", err) + } + + return AddUpgrade(cfg, force, upgradeHeight, upgradeName, executablePath, cfg.UpgradeInfoFilePath()) +} + // saveOrAbort saves data to path or aborts if file exists and force is false func saveOrAbort(path string, data []byte, force bool) error { if _, err := os.Stat(path); err == nil { diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 5a6c7b281e8..d96cbdf4ef5 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -3,36 +3,75 @@ package main import ( "fmt" "strings" + "strconv" + "os" + "encoding/json" "github.com/spf13/cobra" - - "cosmossdk.io/tools/cosmovisor" ) func NewBatchAddUpgradeCmd() *cobra.Command { - addUpgrade := &cobra.Command{ - Use: "add-batch-upgrade : : .. :", + return &cobra.Command{ + Use: "add-batch-upgrade :: :: .. ::", Short: "Add APP upgrades binary to cosmovisor", SilenceUsage: true, Args: cobra.ArbitraryArgs, RunE: AddBatchUpgrade, } - - addUpgrade.Flags().Bool(cosmovisor.FlagForce, false, "overwrite existing upgrade binary / upgrade-info.json file") - addUpgrade.Flags().Int64(cosmovisor.FlagUpgradeHeight, 0, "define a height at which to upgrade the binary automatically (without governance proposal)") - - return addUpgrade } func AddBatchUpgrade(cmd *cobra.Command, args []string) error { + cfg, err := GetConfig(cmd) + if err != nil { + return err + } + upgradeInfoPaths := []string{} for i, as := range args { a := strings.Split(as, ":") - if len(a) != 2 { + if len(a) != 3 { return fmt.Errorf("argument at position %d (%s) is invalid", i, as) } - if err := AddUpgrade(cmd, a); err != nil { + upgradeName := args[0] + upgradePath := args[1] + upgradeHeight, err := strconv.ParseInt(args[2], 10, 64) + if err != nil { + return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, args[2]) + } + upgradeInfoPath := cfg.UpgradeInfoFilePath() + upgradeName + upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) + if err := AddUpgrade(cfg, true, upgradeHeight, upgradeName, upgradePath, upgradeInfoPath); err != nil { return err } } + + var allData []json.RawMessage + for _, uip := range upgradeInfoPaths { + fileData, err := os.ReadFile(uip) + if err != nil { + return fmt.Errorf("Error reading file %s: %v", uip, err) + } + + // Verify it's valid JSON + var jsonData json.RawMessage + if err := json.Unmarshal(fileData, &jsonData); err != nil { + return fmt.Errorf("Error parsing JSON from file %s: %v", uip, err) + } + + // Add to our slice + allData = append(allData, jsonData) + } + + // Marshal the combined data + batchData, err := json.MarshalIndent(allData, "", " ") + if err != nil { + return fmt.Errorf("Error marshaling combined JSON: %v", err) + } + + // Write to output file + err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0644) + if err != nil { + return fmt.Errorf("Error writing combined JSON to file: %v", err) + } + return nil } diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index ac759bde39f..635670292c7 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -13,11 +13,15 @@ import ( "strings" "syscall" "time" + "context" + "sort" "github.com/otiai10/copy" "cosmossdk.io/log" "cosmossdk.io/x/upgrade/plan" + "github.com/cometbft/cometbft/rpc/client/http" + cmttypes "github.com/cometbft/cometbft/types" upgradetypes "cosmossdk.io/x/upgrade/types" ) @@ -27,6 +31,7 @@ type Launcher struct { fw *fileWatcher } + func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { fw, err := newUpgradeFileWatcher(cfg) if err != nil { @@ -36,6 +41,72 @@ func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { return Launcher{logger: logger, cfg: cfg, fw: fw}, nil } + +func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) error { + // load batch file in memory + var uInfos []upgradetypes.Plan + upgradeInfoFile, err := os.ReadFile(cfg.UpgradeInfoBatchFilePath()) + if err != nil { + return fmt.Errorf("error while reading upgrade-info.json.batch: %w", err) + } + + if err = json.Unmarshal(upgradeInfoFile, &uInfos); err != nil { + return err + } + sort.Slice(uInfos, func(i, j int) bool { + return uInfos[i].Height < uInfos[j].Height + }) + + client, err := http.New(rpcAddress, "/websocket") + if err != nil { + return fmt.Errorf("failed to create CometBFT client: %w", err) + } + defer client.Stop() + + err = client.Start() + if err != nil { + return fmt.Errorf("failed to start CometBFT client: %w", err) + } + + eventCh, err := client.Subscribe(ctx, "cosmovisor-watcher", cmttypes.EventQueryNewBlock.String()) + if err != nil { + return fmt.Errorf("failed to subscribe to new blocks: %w", err) + } + // TODO: break if empty array + for { + select { + case e := <-eventCh: + nb, ok := e.Data.(cmttypes.EventDataNewBlock) + if !ok { + logger.Warn("batch watcher: unexpected event data type:", e.Data) + continue + } + h := nb.Block.Height + // read batch file in buffer, get lowest, compare, replace and delete + if h > uInfos[0].Height { + jsonBytes, err := json.Marshal(uInfos[0]) + if err != nil { + return fmt.Errorf("error marshaling JSON: %w", err) + } + if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o755); err != nil { + return fmt.Errorf("error writing upgrade-info.json: %w", err) + } + uInfos = uInfos[1:] + + jsonBytes, err = json.Marshal(uInfos) + if err != nil { + return fmt.Errorf("error marshaling JSON: %w", err) + } + if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o755); err != nil { + return fmt.Errorf("error writing upgrade-info.json.batch: %w", err) + } + } + case <-ctx.Done(): + return nil + } + } +} + // Run launches the app in a subprocess and returns when the subprocess (app) // exits (either when it dies, or *after* a successful upgrade.) and upgrade finished. // Returns true if the upgrade request was detected and the upgrade process started. @@ -58,10 +129,13 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) return false, fmt.Errorf("launching process %s %s failed: %w", bin, strings.Join(args, " "), err) } + ctx, cancel := context.WithCancel(context.Background()) + sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGQUIT, syscall.SIGTERM) go func() { sig := <-sigs + cancel() if err := cmd.Process.Signal(sig); err != nil { l.logger.Error("terminated", "error", err, "bin", bin) os.Exit(1) @@ -72,6 +146,8 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) return false, err } + go BatchWatcher(ctx, l.cfg, l.logger, "localhost") + if !IsSkipUpgradeHeight(args, l.fw.currentInfo) { l.cfg.WaitRestartDelay() From e38739d1844d6ffffa3cab15eb5558c1704ea246 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 15:02:04 +1000 Subject: [PATCH 03/24] fmt --- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 6 +++--- tools/cosmovisor/process.go | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index d96cbdf4ef5..7265044b4de 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -1,11 +1,11 @@ package main import ( + "encoding/json" "fmt" - "strings" - "strconv" "os" - "encoding/json" + "strconv" + "strings" "github.com/spf13/cobra" ) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 635670292c7..5de5f16c6d5 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -1,6 +1,7 @@ package cosmovisor import ( + "context" "encoding/json" "errors" "fmt" @@ -9,20 +10,19 @@ import ( "os/exec" "os/signal" "path/filepath" + "sort" "strconv" "strings" "syscall" "time" - "context" - "sort" "github.com/otiai10/copy" "cosmossdk.io/log" "cosmossdk.io/x/upgrade/plan" + upgradetypes "cosmossdk.io/x/upgrade/types" "github.com/cometbft/cometbft/rpc/client/http" cmttypes "github.com/cometbft/cometbft/types" - upgradetypes "cosmossdk.io/x/upgrade/types" ) type Launcher struct { @@ -31,7 +31,6 @@ type Launcher struct { fw *fileWatcher } - func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { fw, err := newUpgradeFileWatcher(cfg) if err != nil { @@ -41,7 +40,6 @@ func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { return Launcher{logger: logger, cfg: cfg, fw: fw}, nil } - func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) error { // load batch file in memory var uInfos []upgradetypes.Plan From 322ff7253968c4000e40685bd7bc3b04f6e0eef3 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 18:31:19 +1000 Subject: [PATCH 04/24] made some fixes; command now runs successfuly on my machine --- .../cosmovisor/cmd/cosmovisor/add_upgrade.go | 4 +- .../cmd/cosmovisor/batch_upgrade.go | 8 +- tools/cosmovisor/cmd/cosmovisor/root.go | 1 + tools/cosmovisor/process.go | 85 +++++++++++++++---- 4 files changed, 74 insertions(+), 24 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index 4675e95f49d..c046b635284 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -74,11 +74,11 @@ func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgrade return fmt.Errorf("failed to marshal upgrade plan: %w", err) } - if err := saveOrAbort(cfg.UpgradeInfoFilePath(), planData, force); err != nil { + if err := saveOrAbort(upgradeInfoPath, planData, force); err != nil { return err } - logger.Info(fmt.Sprintf("%s created, %s upgrade binary will switch at height %d", cfg.UpgradeInfoFilePath(), upgradeName, upgradeHeight)) + logger.Info(fmt.Sprintf("%s created, %s upgrade binary will switch at height %d", upgradeInfoPath, upgradeName, upgradeHeight)) } return nil diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 7265044b4de..dc21549de54 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -31,11 +31,11 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { if len(a) != 3 { return fmt.Errorf("argument at position %d (%s) is invalid", i, as) } - upgradeName := args[0] - upgradePath := args[1] - upgradeHeight, err := strconv.ParseInt(args[2], 10, 64) + upgradeName := a[0] + upgradePath := a[1] + upgradeHeight, err := strconv.ParseInt(a[2], 10, 64) if err != nil { - return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, args[2]) + return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, a[2]) } upgradeInfoPath := cfg.UpgradeInfoFilePath() + upgradeName upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) diff --git a/tools/cosmovisor/cmd/cosmovisor/root.go b/tools/cosmovisor/cmd/cosmovisor/root.go index d8c351f4c14..2ca4dfafb5f 100644 --- a/tools/cosmovisor/cmd/cosmovisor/root.go +++ b/tools/cosmovisor/cmd/cosmovisor/root.go @@ -20,6 +20,7 @@ func NewRootCmd() *cobra.Command { NewVersionCmd(), NewAddUpgradeCmd(), NewShowUpgradeInfoCmd(), + NewBatchAddUpgradeCmd(), ) rootCmd.PersistentFlags().StringP(cosmovisor.FlagCosmovisorConfig, "c", "", "path to cosmovisor config file") diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 5de5f16c6d5..81ae0636564 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -16,6 +16,7 @@ import ( "syscall" "time" + "github.com/fsnotify/fsnotify" "github.com/otiai10/copy" "cosmossdk.io/log" @@ -40,67 +41,115 @@ func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { return Launcher{logger: logger, cfg: cfg, fw: fw}, nil } -func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) error { - // load batch file in memory +func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { var uInfos []upgradetypes.Plan upgradeInfoFile, err := os.ReadFile(cfg.UpgradeInfoBatchFilePath()) - if err != nil { - return fmt.Errorf("error while reading upgrade-info.json.batch: %w", err) + if os.IsNotExist(err) { + return uInfos, nil + } else if err != nil { + return nil, fmt.Errorf("error while reading upgrade-info.json.batch: %w", err) } if err = json.Unmarshal(upgradeInfoFile, &uInfos); err != nil { - return err + return nil, err } sort.Slice(uInfos, func(i, j int) bool { return uInfos[i].Height < uInfos[j].Height }) + return uInfos, nil +} + +func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) { + // load batch file in memory + uInfos, err := loadBatchUpgradeFile(cfg) + if err != nil { + logger.Warn(fmt.Sprintf("failed to load batch upgrade file: %s", err)) + return + } + + watcher, err := fsnotify.NewWatcher() + if err != nil { + logger.Warn(fmt.Sprintf("failed to init watcher: %s", err)) + return + } + defer watcher.Close() + err = watcher.Add(filepath.Dir(cfg.UpgradeInfoBatchFilePath())) + if err != nil { + logger.Warn(fmt.Sprintf("failed to init watcher: %s", err)) + return + } + // sleep to allow the chain process to startup + time.Sleep(10 * time.Second) client, err := http.New(rpcAddress, "/websocket") if err != nil { - return fmt.Errorf("failed to create CometBFT client: %w", err) + logger.Warn(fmt.Sprintf("failed to create CometBFT client: %s", err)) + return } defer client.Stop() err = client.Start() if err != nil { - return fmt.Errorf("failed to start CometBFT client: %w", err) + logger.Warn(fmt.Sprintf("failed to start CometBFT client: %s", err)) + return } eventCh, err := client.Subscribe(ctx, "cosmovisor-watcher", cmttypes.EventQueryNewBlock.String()) if err != nil { - return fmt.Errorf("failed to subscribe to new blocks: %w", err) + logger.Warn(fmt.Sprintf("failed to subscribe to new blocks: %s", err)) + return } - // TODO: break if empty array + + var prevUpgradeHeight int64 = -1 + + logger.Info("starting the batch watcher loop") for { select { case e := <-eventCh: + if len(uInfos) == 0 { + continue + } nb, ok := e.Data.(cmttypes.EventDataNewBlock) if !ok { logger.Warn("batch watcher: unexpected event data type:", e.Data) continue } h := nb.Block.Height - // read batch file in buffer, get lowest, compare, replace and delete - if h > uInfos[0].Height { + upcomingUpgrade := uInfos[0].Height + // replace upgrade-info and upgrade-info batch file + if h > prevUpgradeHeight && h < upcomingUpgrade { jsonBytes, err := json.Marshal(uInfos[0]) if err != nil { - return fmt.Errorf("error marshaling JSON: %w", err) + logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) + return } if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o755); err != nil { - return fmt.Errorf("error writing upgrade-info.json: %w", err) + logger.Warn(fmt.Sprintf("error writing upgrade-info.json: %s", err)) + return } uInfos = uInfos[1:] jsonBytes, err = json.Marshal(uInfos) if err != nil { - return fmt.Errorf("error marshaling JSON: %w", err) + logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) + return } if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o755); err != nil { - return fmt.Errorf("error writing upgrade-info.json.batch: %w", err) + logger.Warn(fmt.Sprintf("error writing upgrade-info.json.batch: %s", err)) + return + } + prevUpgradeHeight = upcomingUpgrade + } + case event := <-watcher.Events: + if event.Op&(fsnotify.Write|fsnotify.Create) != 0 { + uInfos, err = loadBatchUpgradeFile(cfg) + if err != nil { + logger.Warn(fmt.Sprintf("failed to load batch upgrade file: %s", err)) + return } } case <-ctx.Done(): - return nil + return } } } @@ -140,12 +189,12 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) } }() + go BatchWatcher(ctx, l.cfg, l.logger, "http://localhost:26657") + if needsUpdate, err := l.WaitForUpgradeOrExit(cmd); err != nil || !needsUpdate { return false, err } - go BatchWatcher(ctx, l.cfg, l.logger, "localhost") - if !IsSkipUpgradeHeight(args, l.fw.currentInfo) { l.cfg.WaitRestartDelay() From b30a8d1481771f43cfe7e1aa71ccd79fd6ffcce7 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 18:40:16 +1000 Subject: [PATCH 05/24] lint --- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 8 ++++---- tools/cosmovisor/process.go | 10 +++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index dc21549de54..682e9cdb6a0 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -48,13 +48,13 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { for _, uip := range upgradeInfoPaths { fileData, err := os.ReadFile(uip) if err != nil { - return fmt.Errorf("Error reading file %s: %v", uip, err) + return fmt.Errorf("Error reading file %s: %w", uip, err) } // Verify it's valid JSON var jsonData json.RawMessage if err := json.Unmarshal(fileData, &jsonData); err != nil { - return fmt.Errorf("Error parsing JSON from file %s: %v", uip, err) + return fmt.Errorf("Error parsing JSON from file %s: %w", uip, err) } // Add to our slice @@ -64,13 +64,13 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { // Marshal the combined data batchData, err := json.MarshalIndent(allData, "", " ") if err != nil { - return fmt.Errorf("Error marshaling combined JSON: %v", err) + return fmt.Errorf("Error marshaling combined JSON: %w", err) } // Write to output file err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0644) if err != nil { - return fmt.Errorf("Error writing combined JSON to file: %v", err) + return fmt.Errorf("Error writing combined JSON to file: %w", err) } return nil diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 81ae0636564..e15df77d467 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -86,7 +86,11 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres logger.Warn(fmt.Sprintf("failed to create CometBFT client: %s", err)) return } - defer client.Stop() + defer func() { + if err := client.Stop(); err != nil { + logger.Warn(fmt.Sprintf("couldn't stop CometBFT client: %s", err)) + } + }() err = client.Start() if err != nil { @@ -123,7 +127,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) return } - if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o755); err != nil { + if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o666); err != nil { logger.Warn(fmt.Sprintf("error writing upgrade-info.json: %s", err)) return } @@ -134,7 +138,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) return } - if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o755); err != nil { + if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o666); err != nil { logger.Warn(fmt.Sprintf("error writing upgrade-info.json.batch: %s", err)) return } From 980201d68999215f3f31e8fe04672b62cea62df0 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 18:49:16 +1000 Subject: [PATCH 06/24] lint --- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 8 ++++---- tools/cosmovisor/process.go | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 682e9cdb6a0..6d46fa1e2f7 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -48,13 +48,13 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { for _, uip := range upgradeInfoPaths { fileData, err := os.ReadFile(uip) if err != nil { - return fmt.Errorf("Error reading file %s: %w", uip, err) + return fmt.Errorf("error reading file %s: %w", uip, err) } // Verify it's valid JSON var jsonData json.RawMessage if err := json.Unmarshal(fileData, &jsonData); err != nil { - return fmt.Errorf("Error parsing JSON from file %s: %w", uip, err) + return fmt.Errorf("error parsing JSON from file %s: %w", uip, err) } // Add to our slice @@ -64,13 +64,13 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { // Marshal the combined data batchData, err := json.MarshalIndent(allData, "", " ") if err != nil { - return fmt.Errorf("Error marshaling combined JSON: %w", err) + return fmt.Errorf("error marshaling combined JSON: %w", err) } // Write to output file err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0644) if err != nil { - return fmt.Errorf("Error writing combined JSON to file: %w", err) + return fmt.Errorf("error writing combined JSON to file: %w", err) } return nil diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index e15df77d467..b2a378faa53 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -127,7 +127,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) return } - if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o666); err != nil { + if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o600); err != nil { logger.Warn(fmt.Sprintf("error writing upgrade-info.json: %s", err)) return } @@ -138,7 +138,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) return } - if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o666); err != nil { + if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o600); err != nil { logger.Warn(fmt.Sprintf("error writing upgrade-info.json.batch: %s", err)) return } From bc0651dcaf655c9c22ca696f1e1612938d850354 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 18 Sep 2024 18:52:23 +1000 Subject: [PATCH 07/24] go mod tidy --- tools/cosmovisor/go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/cosmovisor/go.mod b/tools/cosmovisor/go.mod index 9ae2df793f3..565f733270d 100644 --- a/tools/cosmovisor/go.mod +++ b/tools/cosmovisor/go.mod @@ -5,6 +5,8 @@ go 1.23 require ( cosmossdk.io/log v1.4.1 cosmossdk.io/x/upgrade v0.1.4 + github.com/cometbft/cometbft v0.38.9 + github.com/fsnotify/fsnotify v1.7.0 github.com/otiai10/copy v1.14.0 github.com/pelletier/go-toml/v2 v2.2.3 github.com/spf13/cobra v1.8.1 @@ -45,7 +47,6 @@ require ( github.com/cockroachdb/pebble v1.1.0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect - github.com/cometbft/cometbft v0.38.9 // indirect github.com/cometbft/cometbft-db v0.12.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.2 // indirect @@ -68,7 +69,6 @@ require ( github.com/emicklei/dot v1.6.2 // indirect github.com/fatih/color v1.17.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/sentry-go v0.28.0 // indirect github.com/go-kit/kit v0.13.0 // indirect github.com/go-kit/log v0.2.1 // indirect From 68bc6f9c75c1d5fa91278c6f5091af5378557f58 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 19 Sep 2024 14:05:32 +1000 Subject: [PATCH 08/24] godoc; lint; changelog --- tools/cosmovisor/CHANGELOG.md | 3 +++ tools/cosmovisor/args.go | 1 + tools/cosmovisor/cmd/cosmovisor/add_upgrade.go | 4 +++- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 4 +++- tools/cosmovisor/process.go | 9 +++++++-- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/tools/cosmovisor/CHANGELOG.md b/tools/cosmovisor/CHANGELOG.md index ae0de6b86ff..327fe10a5b8 100644 --- a/tools/cosmovisor/CHANGELOG.md +++ b/tools/cosmovisor/CHANGELOG.md @@ -36,6 +36,9 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] +### Features +* [#21790](https://github.com/cosmos/cosmos-sdk/pull/21790) Add `add-batch-upgrade` command. + ### Improvements * [#21462](https://github.com/cosmos/cosmos-sdk/pull/21462) Pass `stdin` to binary. diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index 7b188ad1bff..d7ce4d12675 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -109,6 +109,7 @@ func (cfg *Config) UpgradeInfoFilePath() string { return filepath.Join(cfg.Home, "data", upgradetypes.UpgradeInfoFilename) } +// UpgradeInfoBatchFilePath is the same as UpgradeInfoFilePath but with a batch suffix. func (cfg *Config) UpgradeInfoBatchFilePath() string { return cfg.UpgradeInfoFilePath() + ".batch" } diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index c046b635284..d011beb8440 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -28,6 +28,7 @@ func NewAddUpgradeCmd() *cobra.Command { return addUpgrade } +// AddUpgrade adds upgrade info to manifest func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgradeName, executablePath, upgradeInfoPath string) error { logger := cfg.Logger(os.Stdout) @@ -84,6 +85,7 @@ func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgrade return nil } +// GetConfig returns a Config using passed-in flag func GetConfig(cmd *cobra.Command) (*cosmovisor.Config, error) { configPath, err := cmd.Flags().GetString(cosmovisor.FlagCosmovisorConfig) if err != nil { @@ -97,7 +99,7 @@ func GetConfig(cmd *cobra.Command) (*cosmovisor.Config, error) { return cfg, nil } -// AddUpgrade adds upgrade info to manifest +// AddUpgradeCmd parses input flags and adds upgrade info to manifest func AddUpgradeCmd(cmd *cobra.Command, args []string) error { cfg, err := GetConfig(cmd) if err != nil { diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 6d46fa1e2f7..72d570b662e 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -20,6 +20,8 @@ func NewBatchAddUpgradeCmd() *cobra.Command { } } +// AddBatchUpgrade takes in multiple specified upgrades and creates a single +// batch upgrade file out of them func AddBatchUpgrade(cmd *cobra.Command, args []string) error { cfg, err := GetConfig(cmd) if err != nil { @@ -68,7 +70,7 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { } // Write to output file - err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0644) + err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0o600) if err != nil { return fmt.Errorf("error writing combined JSON to file: %w", err) } diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index b2a378faa53..457ecc9db0b 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -16,14 +16,14 @@ import ( "syscall" "time" + "github.com/cometbft/cometbft/rpc/client/http" + cmttypes "github.com/cometbft/cometbft/types" "github.com/fsnotify/fsnotify" "github.com/otiai10/copy" "cosmossdk.io/log" "cosmossdk.io/x/upgrade/plan" upgradetypes "cosmossdk.io/x/upgrade/types" - "github.com/cometbft/cometbft/rpc/client/http" - cmttypes "github.com/cometbft/cometbft/types" ) type Launcher struct { @@ -41,6 +41,8 @@ func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) { return Launcher{logger: logger, cfg: cfg, fw: fw}, nil } +// loadBatchUpgradeFile loads the batch upgrade file into memory, sorted by +// their upgrade heights func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { var uInfos []upgradetypes.Plan upgradeInfoFile, err := os.ReadFile(cfg.UpgradeInfoBatchFilePath()) @@ -59,6 +61,9 @@ func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { return uInfos, nil } +// BatchWatcher starts a watcher loop that swaps upgrade manifests at the correct +// height, given the batch upgrade file. It watches the current state of the chain +// via the websocket API. func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) { // load batch file in memory uInfos, err := loadBatchUpgradeFile(cfg) From 34bb0c8b10f6e18f89ca7cc7b38012cb53a38bad Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 19 Sep 2024 20:17:11 +1000 Subject: [PATCH 09/24] ty coderabbitai --- tools/cosmovisor/CHANGELOG.md | 1 + tools/cosmovisor/args.go | 12 +++-- .../cmd/cosmovisor/batch_upgrade.go | 9 ++-- tools/cosmovisor/process.go | 51 ++++++++++++------- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/tools/cosmovisor/CHANGELOG.md b/tools/cosmovisor/CHANGELOG.md index 327fe10a5b8..1c6c3ea798d 100644 --- a/tools/cosmovisor/CHANGELOG.md +++ b/tools/cosmovisor/CHANGELOG.md @@ -37,6 +37,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## [Unreleased] ### Features + * [#21790](https://github.com/cosmos/cosmos-sdk/pull/21790) Add `add-batch-upgrade` command. ### Improvements diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index d7ce4d12675..bec741f0ffe 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -38,6 +38,7 @@ const ( EnvTimeFormatLogs = "COSMOVISOR_TIMEFORMAT_LOGS" EnvCustomPreupgrade = "COSMOVISOR_CUSTOM_PREUPGRADE" EnvDisableRecase = "COSMOVISOR_DISABLE_RECASE" + EnvCometBftRpcEndpoint = "COMETBFT_RPC_ENDPOINT" ) const ( @@ -68,6 +69,7 @@ type Config struct { 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"` + CometBftRpcEndpoint string `toml:"combetbft_rpc_endpoint" mapstructure:"combetbft_rpc_endpoint" default:"http://localhost:26657"` // currently running upgrade currentUpgrade upgradetypes.Plan @@ -212,10 +214,11 @@ func GetConfigFromFile(filePath string) (*Config, error) { func GetConfigFromEnv(skipValidate bool) (*Config, error) { var errs []error cfg := &Config{ - Home: os.Getenv(EnvHome), - Name: os.Getenv(EnvName), - DataBackupPath: os.Getenv(EnvDataBackupPath), - CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade), + Home: os.Getenv(EnvHome), + Name: os.Getenv(EnvName), + DataBackupPath: os.Getenv(EnvDataBackupPath), + CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade), + CometBftRpcEndpoint: os.Getenv(EnvCometBftRpcEndpoint), } if cfg.DataBackupPath == "" { @@ -553,6 +556,7 @@ func (cfg Config) DetailString() string { {EnvTimeFormatLogs, cfg.TimeFormatLogs}, {EnvCustomPreupgrade, cfg.CustomPreUpgrade}, {EnvDisableRecase, fmt.Sprintf("%t", cfg.DisableRecase)}, + {EnvCometBftRpcEndpoint, cfg.CometBftRpcEndpoint}, } derivedEntries := []struct{ name, value string }{ diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 72d570b662e..544c2581d59 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "os" + "path/filepath" "strconv" "strings" @@ -12,10 +13,10 @@ import ( func NewBatchAddUpgradeCmd() *cobra.Command { return &cobra.Command{ - Use: "add-batch-upgrade :: :: .. ::", + Use: "add-batch-upgrade :: .. ::", Short: "Add APP upgrades binary to cosmovisor", SilenceUsage: true, - Args: cobra.ArbitraryArgs, + Args: cobra.MinimumNArgs(1), RunE: AddBatchUpgrade, } } @@ -33,13 +34,13 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { if len(a) != 3 { return fmt.Errorf("argument at position %d (%s) is invalid", i, as) } - upgradeName := a[0] + upgradeName := filepath.Base(a[0]) upgradePath := a[1] upgradeHeight, err := strconv.ParseInt(a[2], 10, 64) if err != nil { return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, a[2]) } - upgradeInfoPath := cfg.UpgradeInfoFilePath() + upgradeName + upgradeInfoPath := filepath.Join(cfg.UpgradeInfoFilePath(), upgradeName) upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) if err := AddUpgrade(cfg, true, upgradeHeight, upgradeName, upgradePath, upgradeInfoPath); err != nil { return err diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 457ecc9db0b..163406704fb 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -6,6 +6,7 @@ import ( "errors" "fmt" "io" + "net/http" "os" "os/exec" "os/signal" @@ -16,7 +17,7 @@ import ( "syscall" "time" - "github.com/cometbft/cometbft/rpc/client/http" + cmthttp "github.com/cometbft/cometbft/rpc/client/http" cmttypes "github.com/cometbft/cometbft/types" "github.com/fsnotify/fsnotify" "github.com/otiai10/copy" @@ -49,7 +50,7 @@ func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { if os.IsNotExist(err) { return uInfos, nil } else if err != nil { - return nil, fmt.Errorf("error while reading upgrade-info.json.batch: %w", err) + return nil, fmt.Errorf("error while reading %s: %w", cfg.UpgradeInfoBatchFilePath(), err) } if err = json.Unmarshal(upgradeInfoFile, &uInfos); err != nil { @@ -64,11 +65,11 @@ func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { // BatchWatcher starts a watcher loop that swaps upgrade manifests at the correct // height, given the batch upgrade file. It watches the current state of the chain // via the websocket API. -func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddress string) { +func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger) { // load batch file in memory uInfos, err := loadBatchUpgradeFile(cfg) if err != nil { - logger.Warn(fmt.Sprintf("failed to load batch upgrade file: %s", err)) + logger.Warn("failed to load batch upgrade file", "error", err) return } @@ -80,32 +81,44 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres defer watcher.Close() err = watcher.Add(filepath.Dir(cfg.UpgradeInfoBatchFilePath())) if err != nil { - logger.Warn(fmt.Sprintf("failed to init watcher: %s", err)) + logger.Warn("failed to init watcher", "error", err) return } - // sleep to allow the chain process to startup - time.Sleep(10 * time.Second) - client, err := http.New(rpcAddress, "/websocket") + // Wait for the chain process to be ready +pollLoop: + for { + select { + case <-ctx.Done(): + return + default: + _, err := http.Get(cfg.CometBftRpcEndpoint) + if err == nil { + break pollLoop + } + time.Sleep(time.Second) + } + } + client, err := cmthttp.New(cfg.CometBftRpcEndpoint, "/websocket") if err != nil { - logger.Warn(fmt.Sprintf("failed to create CometBFT client: %s", err)) + logger.Warn("failed to create CometBFT client", "error", err) return } defer func() { if err := client.Stop(); err != nil { - logger.Warn(fmt.Sprintf("couldn't stop CometBFT client: %s", err)) + logger.Warn("couldn't stop CometBFT client", "errror", err) } }() err = client.Start() if err != nil { - logger.Warn(fmt.Sprintf("failed to start CometBFT client: %s", err)) + logger.Warn("failed to start CometBFT client", "error", err) return } eventCh, err := client.Subscribe(ctx, "cosmovisor-watcher", cmttypes.EventQueryNewBlock.String()) if err != nil { - logger.Warn(fmt.Sprintf("failed to subscribe to new blocks: %s", err)) + logger.Warn("failed to subscribe to new blocks", "error", err) return } @@ -120,7 +133,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres } nb, ok := e.Data.(cmttypes.EventDataNewBlock) if !ok { - logger.Warn("batch watcher: unexpected event data type:", e.Data) + logger.Warn("batch watcher: unexpected event data type", "eventData", e.Data) continue } h := nb.Block.Height @@ -129,22 +142,22 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres if h > prevUpgradeHeight && h < upcomingUpgrade { jsonBytes, err := json.Marshal(uInfos[0]) if err != nil { - logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) + logger.Warn("error marshaling JSON", "error", err) return } if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o600); err != nil { - logger.Warn(fmt.Sprintf("error writing upgrade-info.json: %s", err)) + logger.Warn("error writing upgrade-info.json", "error", err) return } uInfos = uInfos[1:] jsonBytes, err = json.Marshal(uInfos) if err != nil { - logger.Warn(fmt.Sprintf("error marshaling JSON: %s", err)) + logger.Warn("error marshaling JSON", "error", err) return } if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o600); err != nil { - logger.Warn(fmt.Sprintf("error writing upgrade-info.json.batch: %s", err)) + logger.Warn("error writing upgrade-info.json.batch", "error", err) return } prevUpgradeHeight = upcomingUpgrade @@ -153,7 +166,7 @@ func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger, rpcAddres if event.Op&(fsnotify.Write|fsnotify.Create) != 0 { uInfos, err = loadBatchUpgradeFile(cfg) if err != nil { - logger.Warn(fmt.Sprintf("failed to load batch upgrade file: %s", err)) + logger.Warn("failed to load batch upgrade file", "error", err) return } } @@ -198,7 +211,7 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) } }() - go BatchWatcher(ctx, l.cfg, l.logger, "http://localhost:26657") + go BatchWatcher(ctx, l.cfg, l.logger) if needsUpdate, err := l.WaitForUpgradeOrExit(cmd); err != nil || !needsUpdate { return false, err From 071c677db8f6cbd6c4d89f989eb47c1feb370590 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 19 Sep 2024 21:15:15 +1000 Subject: [PATCH 10/24] cometbftrpc default value --- tools/cosmovisor/args.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index bec741f0ffe..fb1b4a5f5b1 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -225,6 +225,10 @@ func GetConfigFromEnv(skipValidate bool) (*Config, error) { cfg.DataBackupPath = cfg.Home } + if cfg.CometBftRpcEndpoint == "" { + cfg.CometBftRpcEndpoint = "http://localhost:26657" + } + var err error if cfg.AllowDownloadBinaries, err = BooleanOption(EnvDownloadBin, false); err != nil { errs = append(errs, err) From 054d9ce14608549599277223e8625202880360ac Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Fri, 20 Sep 2024 13:18:28 +1000 Subject: [PATCH 11/24] fix test; better naming --- tools/cosmovisor/args_test.go | 1 + tools/cosmovisor/cmd/cosmovisor/add_upgrade.go | 10 +++++----- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 4 ++-- tools/cosmovisor/process.go | 6 +++--- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index f121a3989d9..e32e1658bb0 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -477,6 +477,7 @@ var newConfig = func( CustomPreUpgrade: customPreUpgrade, DisableRecase: disableRecase, ShutdownGrace: time.Duration(shutdownGrace), + CometBftRpcEndpoint: "http://localhost:26657", } } diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index d011beb8440..9c544aa60fc 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -28,8 +28,8 @@ func NewAddUpgradeCmd() *cobra.Command { return addUpgrade } -// AddUpgrade adds upgrade info to manifest -func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgradeName, executablePath, upgradeInfoPath string) error { +// addUpgrade adds upgrade info to manifest +func addUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgradeName, executablePath, upgradeInfoPath string) error { logger := cfg.Logger(os.Stdout) if !cfg.DisableRecase { @@ -86,7 +86,7 @@ func AddUpgrade(cfg *cosmovisor.Config, force bool, upgradeHeight int64, upgrade } // GetConfig returns a Config using passed-in flag -func GetConfig(cmd *cobra.Command) (*cosmovisor.Config, error) { +func getConfigFromCmd(cmd *cobra.Command) (*cosmovisor.Config, error) { configPath, err := cmd.Flags().GetString(cosmovisor.FlagCosmovisorConfig) if err != nil { return nil, fmt.Errorf("failed to get config flag: %w", err) @@ -101,7 +101,7 @@ func GetConfig(cmd *cobra.Command) (*cosmovisor.Config, error) { // AddUpgradeCmd parses input flags and adds upgrade info to manifest func AddUpgradeCmd(cmd *cobra.Command, args []string) error { - cfg, err := GetConfig(cmd) + cfg, err := getConfigFromCmd(cmd) if err != nil { return err } @@ -118,7 +118,7 @@ func AddUpgradeCmd(cmd *cobra.Command, args []string) error { return fmt.Errorf("failed to get upgrade-height flag: %w", err) } - return AddUpgrade(cfg, force, upgradeHeight, upgradeName, executablePath, cfg.UpgradeInfoFilePath()) + return addUpgrade(cfg, force, upgradeHeight, upgradeName, executablePath, cfg.UpgradeInfoFilePath()) } // saveOrAbort saves data to path or aborts if file exists and force is false diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 544c2581d59..a482953a3c4 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -24,7 +24,7 @@ func NewBatchAddUpgradeCmd() *cobra.Command { // AddBatchUpgrade takes in multiple specified upgrades and creates a single // batch upgrade file out of them func AddBatchUpgrade(cmd *cobra.Command, args []string) error { - cfg, err := GetConfig(cmd) + cfg, err := getConfigFromCmd(cmd) if err != nil { return err } @@ -42,7 +42,7 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { } upgradeInfoPath := filepath.Join(cfg.UpgradeInfoFilePath(), upgradeName) upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) - if err := AddUpgrade(cfg, true, upgradeHeight, upgradeName, upgradePath, upgradeInfoPath); err != nil { + if err := addUpgrade(cfg, true, upgradeHeight, upgradeName, upgradePath, upgradeInfoPath); err != nil { return err } } diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 163406704fb..4e7223d4e97 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -62,10 +62,10 @@ func loadBatchUpgradeFile(cfg *Config) ([]upgradetypes.Plan, error) { return uInfos, nil } -// BatchWatcher starts a watcher loop that swaps upgrade manifests at the correct +// BatchUpgradeWatcher starts a watcher loop that swaps upgrade manifests at the correct // height, given the batch upgrade file. It watches the current state of the chain // via the websocket API. -func BatchWatcher(ctx context.Context, cfg *Config, logger log.Logger) { +func BatchUpgradeWatcher(ctx context.Context, cfg *Config, logger log.Logger) { // load batch file in memory uInfos, err := loadBatchUpgradeFile(cfg) if err != nil { @@ -211,7 +211,7 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) } }() - go BatchWatcher(ctx, l.cfg, l.logger) + go BatchUpgradeWatcher(ctx, l.cfg, l.logger) if needsUpdate, err := l.WaitForUpgradeOrExit(cmd); err != nil || !needsUpdate { return false, err From a7e6459c0a07379a1052566f69e1e36ab565a5ba Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Fri, 27 Sep 2024 17:51:29 +1000 Subject: [PATCH 12/24] switch from cometbft websocket to cosmos grpc --- tools/cosmovisor/args.go | 20 +++--- tools/cosmovisor/args_test.go | 2 +- .../cmd/cosmovisor/batch_upgrade.go | 2 +- tools/cosmovisor/go.mod | 6 +- tools/cosmovisor/process.go | 71 +++++++++---------- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index fb1b4a5f5b1..4e62f376c64 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -38,7 +38,7 @@ const ( EnvTimeFormatLogs = "COSMOVISOR_TIMEFORMAT_LOGS" EnvCustomPreupgrade = "COSMOVISOR_CUSTOM_PREUPGRADE" EnvDisableRecase = "COSMOVISOR_DISABLE_RECASE" - EnvCometBftRpcEndpoint = "COMETBFT_RPC_ENDPOINT" + EnvCosmosGrpcEndpoint = "COSMOS_GRPC_ENDPOINT" ) const ( @@ -69,7 +69,7 @@ type Config struct { 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"` - CometBftRpcEndpoint string `toml:"combetbft_rpc_endpoint" mapstructure:"combetbft_rpc_endpoint" default:"http://localhost:26657"` + CosmosGrpcEndpoint string `toml:"cosmos_grpc_endpoint" mapstructure:"cosmos_grpc_endpoint" default:"localhost:9090"` // currently running upgrade currentUpgrade upgradetypes.Plan @@ -214,19 +214,19 @@ func GetConfigFromFile(filePath string) (*Config, error) { func GetConfigFromEnv(skipValidate bool) (*Config, error) { var errs []error cfg := &Config{ - Home: os.Getenv(EnvHome), - Name: os.Getenv(EnvName), - DataBackupPath: os.Getenv(EnvDataBackupPath), - CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade), - CometBftRpcEndpoint: os.Getenv(EnvCometBftRpcEndpoint), + Home: os.Getenv(EnvHome), + Name: os.Getenv(EnvName), + DataBackupPath: os.Getenv(EnvDataBackupPath), + CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade), + CosmosGrpcEndpoint: os.Getenv(EnvCosmosGrpcEndpoint), } if cfg.DataBackupPath == "" { cfg.DataBackupPath = cfg.Home } - if cfg.CometBftRpcEndpoint == "" { - cfg.CometBftRpcEndpoint = "http://localhost:26657" + if cfg.CosmosGrpcEndpoint == "" { + cfg.CosmosGrpcEndpoint = "localhost:9090" } var err error @@ -560,7 +560,7 @@ func (cfg Config) DetailString() string { {EnvTimeFormatLogs, cfg.TimeFormatLogs}, {EnvCustomPreupgrade, cfg.CustomPreUpgrade}, {EnvDisableRecase, fmt.Sprintf("%t", cfg.DisableRecase)}, - {EnvCometBftRpcEndpoint, cfg.CometBftRpcEndpoint}, + {EnvCosmosGrpcEndpoint, cfg.CosmosGrpcEndpoint}, } derivedEntries := []struct{ name, value string }{ diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index e32e1658bb0..156cb1ba19a 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -477,7 +477,7 @@ var newConfig = func( CustomPreUpgrade: customPreUpgrade, DisableRecase: disableRecase, ShutdownGrace: time.Duration(shutdownGrace), - CometBftRpcEndpoint: "http://localhost:26657", + CosmosGrpcEndpoint: "localhost:9090", } } diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index a482953a3c4..43eb3e32322 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -40,7 +40,7 @@ func AddBatchUpgrade(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, a[2]) } - upgradeInfoPath := filepath.Join(cfg.UpgradeInfoFilePath(), upgradeName) + upgradeInfoPath := cfg.UpgradeInfoFilePath() + "." + upgradeName upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) if err := addUpgrade(cfg, true, upgradeHeight, upgradeName, upgradePath, upgradeInfoPath); err != nil { return err diff --git a/tools/cosmovisor/go.mod b/tools/cosmovisor/go.mod index 565f733270d..654535f081f 100644 --- a/tools/cosmovisor/go.mod +++ b/tools/cosmovisor/go.mod @@ -5,13 +5,14 @@ go 1.23 require ( cosmossdk.io/log v1.4.1 cosmossdk.io/x/upgrade v0.1.4 - github.com/cometbft/cometbft v0.38.9 + github.com/cosmos/cosmos-sdk v0.50.7 github.com/fsnotify/fsnotify v1.7.0 github.com/otiai10/copy v1.14.0 github.com/pelletier/go-toml/v2 v2.2.3 github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 github.com/stretchr/testify v1.9.0 + google.golang.org/grpc v1.67.0 ) require ( @@ -47,11 +48,11 @@ require ( github.com/cockroachdb/pebble v1.1.0 // indirect github.com/cockroachdb/redact v1.1.5 // indirect github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect + github.com/cometbft/cometbft v0.38.9 // indirect github.com/cometbft/cometbft-db v0.12.0 // indirect github.com/cosmos/btcutil v1.0.5 // indirect github.com/cosmos/cosmos-db v1.0.2 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect - github.com/cosmos/cosmos-sdk v0.50.7 // indirect github.com/cosmos/go-bip39 v1.0.0 // indirect github.com/cosmos/gogogateway v1.2.0 // indirect github.com/cosmos/gogoproto v1.7.0 // indirect @@ -175,7 +176,6 @@ require ( google.golang.org/genproto v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240924160255-9d4c2d233b61 // indirect - google.golang.org/grpc v1.67.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 4e7223d4e97..b032e17f4b0 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "io" - "net/http" "os" "os/exec" "os/signal" @@ -17,14 +16,16 @@ import ( "syscall" "time" - cmthttp "github.com/cometbft/cometbft/rpc/client/http" - cmttypes "github.com/cometbft/cometbft/types" "github.com/fsnotify/fsnotify" "github.com/otiai10/copy" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" "cosmossdk.io/log" "cosmossdk.io/x/upgrade/plan" upgradetypes "cosmossdk.io/x/upgrade/types" + + cmtservice "github.com/cosmos/cosmos-sdk/client/grpc/cmtservice" ) type Launcher struct { @@ -85,6 +86,9 @@ func BatchUpgradeWatcher(ctx context.Context, cfg *Config, logger log.Logger) { return } + var conn *grpc.ClientConn + var grpcErr error + // Wait for the chain process to be ready pollLoop: for { @@ -92,51 +96,49 @@ pollLoop: case <-ctx.Done(): return default: - _, err := http.Get(cfg.CometBftRpcEndpoint) - if err == nil { + conn, grpcErr = grpc.NewClient(cfg.CosmosGrpcEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) + if grpcErr == nil { break pollLoop } time.Sleep(time.Second) } } - client, err := cmthttp.New(cfg.CometBftRpcEndpoint, "/websocket") - if err != nil { - logger.Warn("failed to create CometBFT client", "error", err) - return - } + defer func() { - if err := client.Stop(); err != nil { - logger.Warn("couldn't stop CometBFT client", "errror", err) + if err := conn.Close(); err != nil { + logger.Warn("couldn't stop gRPC client", "error", err) } }() - err = client.Start() - if err != nil { - logger.Warn("failed to start CometBFT client", "error", err) - return - } - - eventCh, err := client.Subscribe(ctx, "cosmovisor-watcher", cmttypes.EventQueryNewBlock.String()) - if err != nil { - logger.Warn("failed to subscribe to new blocks", "error", err) - return - } + client := cmtservice.NewServiceClient(conn) var prevUpgradeHeight int64 = -1 logger.Info("starting the batch watcher loop") for { select { - case e := <-eventCh: + case event := <-watcher.Events: + if event.Op&(fsnotify.Write|fsnotify.Create) != 0 { + uInfos, err = loadBatchUpgradeFile(cfg) + if err != nil { + logger.Warn("failed to load batch upgrade file", "error", err) + return + } + } + case <-ctx.Done(): + return + default: if len(uInfos) == 0 { continue } - nb, ok := e.Data.(cmttypes.EventDataNewBlock) - if !ok { - logger.Warn("batch watcher: unexpected event data type", "eventData", e.Data) + resp, err := client.GetLatestBlock(ctx, &cmtservice.GetLatestBlockRequest{}) + if err != nil { + logger.Warn("error getting latest block", "error", err) + time.Sleep(time.Second) continue } - h := nb.Block.Height + + h := resp.SdkBlock.Header.Height upcomingUpgrade := uInfos[0].Height // replace upgrade-info and upgrade-info batch file if h > prevUpgradeHeight && h < upcomingUpgrade { @@ -162,16 +164,9 @@ pollLoop: } prevUpgradeHeight = upcomingUpgrade } - case event := <-watcher.Events: - if event.Op&(fsnotify.Write|fsnotify.Create) != 0 { - uInfos, err = loadBatchUpgradeFile(cfg) - if err != nil { - logger.Warn("failed to load batch upgrade file", "error", err) - return - } - } - case <-ctx.Done(): - return + + // Add a small delay to avoid hammering the gRPC endpoint + time.Sleep(time.Second) } } } From 3f3c787b213383baab6b7b2a76c8f831081b1880 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Sun, 29 Sep 2024 01:01:00 +1000 Subject: [PATCH 13/24] cpu opti --- tools/cosmovisor/process.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index b032e17f4b0..6fe302291c0 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -129,6 +129,8 @@ pollLoop: return default: if len(uInfos) == 0 { + // prevent spending extra CPU cycles + time.Sleep(time.Second) continue } resp, err := client.GetLatestBlock(ctx, &cmtservice.GetLatestBlockRequest{}) From d4bdb57f9dc215d292f3382cd757c1fc14b80f2b Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Mon, 30 Sep 2024 14:36:46 +1000 Subject: [PATCH 14/24] ty coderabbitai --- tools/cosmovisor/cmd/cosmovisor/add_upgrade.go | 6 +++--- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 6 +++--- tools/cosmovisor/process.go | 13 +++++++++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go index 9c544aa60fc..3832efa05e8 100644 --- a/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/add_upgrade.go @@ -19,7 +19,7 @@ func NewAddUpgradeCmd() *cobra.Command { Short: "Add APP upgrade binary to cosmovisor", SilenceUsage: true, Args: cobra.ExactArgs(2), - RunE: AddUpgradeCmd, + RunE: addUpgradeCmd, } addUpgrade.Flags().Bool(cosmovisor.FlagForce, false, "overwrite existing upgrade binary / upgrade-info.json file") @@ -99,8 +99,8 @@ func getConfigFromCmd(cmd *cobra.Command) (*cosmovisor.Config, error) { return cfg, nil } -// AddUpgradeCmd parses input flags and adds upgrade info to manifest -func AddUpgradeCmd(cmd *cobra.Command, args []string) error { +// addUpgradeCmd parses input flags and adds upgrade info to manifest +func addUpgradeCmd(cmd *cobra.Command, args []string) error { cfg, err := getConfigFromCmd(cmd) if err != nil { return err diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 43eb3e32322..75b8e0f98f4 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -17,13 +17,13 @@ func NewBatchAddUpgradeCmd() *cobra.Command { Short: "Add APP upgrades binary to cosmovisor", SilenceUsage: true, Args: cobra.MinimumNArgs(1), - RunE: AddBatchUpgrade, + RunE: addBatchUpgrade, } } -// AddBatchUpgrade takes in multiple specified upgrades and creates a single +// addBatchUpgrade takes in multiple specified upgrades and creates a single // batch upgrade file out of them -func AddBatchUpgrade(cmd *cobra.Command, args []string) error { +func addBatchUpgrade(cmd *cobra.Command, args []string) error { cfg, err := getConfigFromCmd(cmd) if err != nil { return err diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 6fe302291c0..620e36223a8 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -13,6 +13,7 @@ import ( "sort" "strconv" "strings" + "sync" "syscall" "time" @@ -196,20 +197,25 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) } ctx, cancel := context.WithCancel(context.Background()) + var wg sync.WaitGroup + wg.Add(1) + go func() { + defer wg.Done() + BatchUpgradeWatcher(ctx, l.cfg, l.logger) + }() sigs := make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGQUIT, syscall.SIGTERM) go func() { sig := <-sigs cancel() + wg.Wait() if err := cmd.Process.Signal(sig); err != nil { l.logger.Error("terminated", "error", err, "bin", bin) os.Exit(1) } }() - go BatchUpgradeWatcher(ctx, l.cfg, l.logger) - if needsUpdate, err := l.WaitForUpgradeOrExit(cmd); err != nil || !needsUpdate { return false, err } @@ -236,6 +242,9 @@ func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) return true, nil } + cancel() + wg.Wait() + return false, nil } From 4ae231e99bbf10e40f680a066ebcf9429986c0b5 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 1 Oct 2024 14:26:05 +1000 Subject: [PATCH 15/24] improve ux; nit --- tools/cosmovisor/args.go | 12 ++-- tools/cosmovisor/args_test.go | 2 +- .../cmd/cosmovisor/batch_upgrade.go | 68 +++++++++++++++++-- tools/cosmovisor/process.go | 2 +- 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/tools/cosmovisor/args.go b/tools/cosmovisor/args.go index 4e62f376c64..00edb2e4f24 100644 --- a/tools/cosmovisor/args.go +++ b/tools/cosmovisor/args.go @@ -38,7 +38,7 @@ const ( EnvTimeFormatLogs = "COSMOVISOR_TIMEFORMAT_LOGS" EnvCustomPreupgrade = "COSMOVISOR_CUSTOM_PREUPGRADE" EnvDisableRecase = "COSMOVISOR_DISABLE_RECASE" - EnvCosmosGrpcEndpoint = "COSMOS_GRPC_ENDPOINT" + EnvDaemonGrpcEndpoint = "DAEMON_GRPC_ENDPOINT" ) const ( @@ -69,7 +69,7 @@ type Config struct { 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"` - CosmosGrpcEndpoint string `toml:"cosmos_grpc_endpoint" mapstructure:"cosmos_grpc_endpoint" default:"localhost:9090"` + DaemonGrpcEndpoint string `toml:"daemon_grpc_endpoint" mapstructure:"daemon_grpc_endpoint" default:"localhost:9090"` // currently running upgrade currentUpgrade upgradetypes.Plan @@ -218,15 +218,15 @@ func GetConfigFromEnv(skipValidate bool) (*Config, error) { Name: os.Getenv(EnvName), DataBackupPath: os.Getenv(EnvDataBackupPath), CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade), - CosmosGrpcEndpoint: os.Getenv(EnvCosmosGrpcEndpoint), + DaemonGrpcEndpoint: os.Getenv(EnvDaemonGrpcEndpoint), } if cfg.DataBackupPath == "" { cfg.DataBackupPath = cfg.Home } - if cfg.CosmosGrpcEndpoint == "" { - cfg.CosmosGrpcEndpoint = "localhost:9090" + if cfg.DaemonGrpcEndpoint == "" { + cfg.DaemonGrpcEndpoint = "localhost:9090" } var err error @@ -560,7 +560,7 @@ func (cfg Config) DetailString() string { {EnvTimeFormatLogs, cfg.TimeFormatLogs}, {EnvCustomPreupgrade, cfg.CustomPreUpgrade}, {EnvDisableRecase, fmt.Sprintf("%t", cfg.DisableRecase)}, - {EnvCosmosGrpcEndpoint, cfg.CosmosGrpcEndpoint}, + {EnvDaemonGrpcEndpoint, cfg.DaemonGrpcEndpoint}, } derivedEntries := []struct{ name, value string }{ diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index 156cb1ba19a..fd9e7b164a3 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -477,7 +477,7 @@ var newConfig = func( CustomPreUpgrade: customPreUpgrade, DisableRecase: disableRecase, ShutdownGrace: time.Duration(shutdownGrace), - CosmosGrpcEndpoint: "localhost:9090", + DaemonGrpcEndpoint: "localhost:9090", } } diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 75b8e0f98f4..b8de5e4e054 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -9,16 +9,41 @@ import ( "strings" "github.com/spf13/cobra" + + "cosmossdk.io/tools/cosmovisor" + upgradetypes "cosmossdk.io/x/upgrade/types" ) func NewBatchAddUpgradeCmd() *cobra.Command { - return &cobra.Command{ - Use: "add-batch-upgrade :: .. ::", - Short: "Add APP upgrades binary to cosmovisor", + cmd := &cobra.Command{ + Use: "add-batch-upgrade [flags]", + Short: "Add multiple upgrade binaries at specified heights to cosmovisor", + Long: `This command allows you to specify multiple upgrades at once at specific heights, copying or creating a batch upgrade file that's actively watched during 'cosmovisor run'. +You can provide upgrades in two ways: + +1. Using --upgrade-file: Specify a path to a batch upgrade file ie. a JSON array of upgrade-info objects. + The file is validated before it's copied over to the upgrade directory. + +2. Using --upgrade-list: Provide a comma-separated list of upgrades. + Each upgrade is defined by three colon-separated values: + a. upgrade-name: A unique identifier for the upgrade + b. path-to-exec: The file path to the upgrade's executable binary + c. upgrade-height: The block height at which the upgrade should occur + This creates a batch upgrade JSON file with the upgrade-info objects in the upgrade directory. + +Note: You must provide either --upgrade-file or --upgrade-list.`, + Example: `cosmovisor add-batch-upgrade --upgrade-list upgrade_v2:/path/to/v2/binary:1000000,upgrade_v3:/path/to/v3/binary:2000000 + +cosmovisor add-batch-upgrade --upgrade-file /path/to/batch_upgrade.json`, SilenceUsage: true, - Args: cobra.MinimumNArgs(1), + Args: cobra.NoArgs, RunE: addBatchUpgrade, } + + cmd.Flags().String("upgrade-file", "", "Path to a batch upgrade file which is a JSON array of upgrade-info objects") + cmd.Flags().StringSlice("upgrade-list", []string{}, "List of comma-separated upgrades in the format 'name:path/to/binary:height'") + + return cmd } // addBatchUpgrade takes in multiple specified upgrades and creates a single @@ -28,8 +53,28 @@ func addBatchUpgrade(cmd *cobra.Command, args []string) error { if err != nil { return err } + upgradeFile, err := cmd.Flags().GetString("upgrade-file") + if err == nil && upgradeFile != "" { + info, err := os.Stat(upgradeFile) + if err != nil { + return fmt.Errorf("error getting reading upgrade file %s: %w", upgradeFile, err) + } + if info.IsDir() { + return fmt.Errorf("upgrade file %s is a directory", upgradeFile) + } + return processUpgradeFile(cfg, upgradeFile) + } + upgradeList, err := cmd.Flags().GetStringSlice("upgrade-list") + if err != nil || len(upgradeList) == 0 { + return fmt.Errorf("either --upgrade-file or --upgrade-list must be provided") + } + return processUpgradeList(cfg, upgradeList) +} + +// processUpgradeList takes in a list of upgrades and creates a batch upgrade file +func processUpgradeList(cfg *cosmovisor.Config, upgradeList []string) error { upgradeInfoPaths := []string{} - for i, as := range args { + for i, as := range upgradeList { a := strings.Split(as, ":") if len(a) != 3 { return fmt.Errorf("argument at position %d (%s) is invalid", i, as) @@ -78,3 +123,16 @@ func addBatchUpgrade(cmd *cobra.Command, args []string) error { return nil } + +// processUpgradeFile takes in a batch upgrade file, validates it and copies it to the upgrade directory +func processUpgradeFile(cfg *cosmovisor.Config, upgradeFile string) error { + b, err := os.ReadFile(upgradeFile) + if err != nil { + return fmt.Errorf("error reading upgrade file %s: %w", upgradeFile, err) + } + var batch []upgradetypes.Plan + if err := json.Unmarshal(b, &batch); err != nil { + return fmt.Errorf("error unmarshalling upgrade file %s: %w", upgradeFile, err) + } + return copyFile(upgradeFile, cfg.UpgradeInfoBatchFilePath()) +} diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 620e36223a8..2d48017bbc3 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -97,7 +97,7 @@ pollLoop: case <-ctx.Done(): return default: - conn, grpcErr = grpc.NewClient(cfg.CosmosGrpcEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) + conn, grpcErr = grpc.NewClient(cfg.DaemonGrpcEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials())) if grpcErr == nil { break pollLoop } From bc9fb9b5ce7abaf87ad324ed165ad365092ff5a1 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 1 Oct 2024 14:46:54 +1000 Subject: [PATCH 16/24] ty coderabbitai --- tools/cosmovisor/process.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 2d48017bbc3..0c2ae442922 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -90,6 +90,14 @@ func BatchUpgradeWatcher(ctx context.Context, cfg *Config, logger log.Logger) { var conn *grpc.ClientConn var grpcErr error + defer func() { + if conn != nil { + if err := conn.Close(); err != nil { + logger.Warn("couldn't stop gRPC client", "error", err) + } + } + }() + // Wait for the chain process to be ready pollLoop: for { @@ -105,12 +113,6 @@ pollLoop: } } - defer func() { - if err := conn.Close(); err != nil { - logger.Warn("couldn't stop gRPC client", "error", err) - } - }() - client := cmtservice.NewServiceClient(conn) var prevUpgradeHeight int64 = -1 @@ -123,7 +125,7 @@ pollLoop: uInfos, err = loadBatchUpgradeFile(cfg) if err != nil { logger.Warn("failed to load batch upgrade file", "error", err) - return + continue } } case <-ctx.Done(): From 96dd220d3433b504c9e2281abd47ed75be225542 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 1 Oct 2024 15:07:48 +1000 Subject: [PATCH 17/24] minute things --- tools/cosmovisor/process.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 0c2ae442922..83d17af2856 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -72,18 +72,18 @@ func BatchUpgradeWatcher(ctx context.Context, cfg *Config, logger log.Logger) { uInfos, err := loadBatchUpgradeFile(cfg) if err != nil { logger.Warn("failed to load batch upgrade file", "error", err) - return + uInfos = []upgradetypes.Plan{} } watcher, err := fsnotify.NewWatcher() if err != nil { - logger.Warn(fmt.Sprintf("failed to init watcher: %s", err)) + logger.Warn("failed to init watcher", "error", err) return } defer watcher.Close() err = watcher.Add(filepath.Dir(cfg.UpgradeInfoBatchFilePath())) if err != nil { - logger.Warn("failed to init watcher", "error", err) + logger.Warn("watcher failed to add upgrade directory", "error", err) return } From 7f4309e5609a1cd8d145654986bc462d09cf2067 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 1 Oct 2024 15:26:13 +1000 Subject: [PATCH 18/24] ty coderabbitai --- tools/cosmovisor/process.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 83d17af2856..99e82a9e8e5 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -149,23 +149,23 @@ pollLoop: if h > prevUpgradeHeight && h < upcomingUpgrade { jsonBytes, err := json.Marshal(uInfos[0]) if err != nil { - logger.Warn("error marshaling JSON", "error", err) - return + logger.Warn("error marshaling JSON for upgrade-info.json", "error", err, "upgrade", uInfos[0]) + continue } if err := os.WriteFile(cfg.UpgradeInfoFilePath(), jsonBytes, 0o600); err != nil { logger.Warn("error writing upgrade-info.json", "error", err) - return + continue } uInfos = uInfos[1:] jsonBytes, err = json.Marshal(uInfos) if err != nil { - logger.Warn("error marshaling JSON", "error", err) - return + logger.Warn("error marshaling JSON for upgrade-info.json.batch", "error", err, "upgrades", uInfos) + continue } if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o600); err != nil { logger.Warn("error writing upgrade-info.json.batch", "error", err) - return + continue } prevUpgradeHeight = upcomingUpgrade } From 13be6564944b2d7e787502bfea480ff47a5dcf2c Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Tue, 1 Oct 2024 15:57:59 +1000 Subject: [PATCH 19/24] gracefully handle corrupted batch files --- tools/cosmovisor/process.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/cosmovisor/process.go b/tools/cosmovisor/process.go index 99e82a9e8e5..e7601fa61af 100644 --- a/tools/cosmovisor/process.go +++ b/tools/cosmovisor/process.go @@ -165,6 +165,12 @@ pollLoop: } if err := os.WriteFile(cfg.UpgradeInfoBatchFilePath(), jsonBytes, 0o600); err != nil { logger.Warn("error writing upgrade-info.json.batch", "error", err) + // remove the upgrade-info.json.batch file to avoid non-deterministic behavior + err := os.Remove(cfg.UpgradeInfoBatchFilePath()) + if err != nil && !os.IsNotExist(err) { + logger.Warn("error removing upgrade-info.json.batch", "error", err) + return + } continue } prevUpgradeHeight = upcomingUpgrade From 250eae96cd778ed0beecae340ce1629ec426aa49 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 2 Oct 2024 22:41:47 +1000 Subject: [PATCH 20/24] switch to using csv file --- .../cmd/cosmovisor/batch_upgrade.go | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index b8de5e4e054..3ad82600e06 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -1,6 +1,7 @@ package main import ( + "encoding/csv" "encoding/json" "fmt" "os" @@ -11,7 +12,6 @@ import ( "github.com/spf13/cobra" "cosmossdk.io/tools/cosmovisor" - upgradetypes "cosmossdk.io/x/upgrade/types" ) func NewBatchAddUpgradeCmd() *cobra.Command { @@ -21,8 +21,8 @@ func NewBatchAddUpgradeCmd() *cobra.Command { Long: `This command allows you to specify multiple upgrades at once at specific heights, copying or creating a batch upgrade file that's actively watched during 'cosmovisor run'. You can provide upgrades in two ways: -1. Using --upgrade-file: Specify a path to a batch upgrade file ie. a JSON array of upgrade-info objects. - The file is validated before it's copied over to the upgrade directory. +1. Using --upgrade-file: Specify a path to a headerless CSV batch upgrade file in the format: + upgrade-name,path-to-exec,upgrade-height 2. Using --upgrade-list: Provide a comma-separated list of upgrades. Each upgrade is defined by three colon-separated values: @@ -55,35 +55,31 @@ func addBatchUpgrade(cmd *cobra.Command, args []string) error { } upgradeFile, err := cmd.Flags().GetString("upgrade-file") if err == nil && upgradeFile != "" { - info, err := os.Stat(upgradeFile) - if err != nil { - return fmt.Errorf("error getting reading upgrade file %s: %w", upgradeFile, err) - } - if info.IsDir() { - return fmt.Errorf("upgrade file %s is a directory", upgradeFile) - } return processUpgradeFile(cfg, upgradeFile) } upgradeList, err := cmd.Flags().GetStringSlice("upgrade-list") if err != nil || len(upgradeList) == 0 { return fmt.Errorf("either --upgrade-file or --upgrade-list must be provided") } - return processUpgradeList(cfg, upgradeList) + var splitUpgrades [][]string + for _, upgrade := range upgradeList { + splitUpgrades = append(splitUpgrades, strings.Split(upgrade, ":")) + } + return processUpgradeList(cfg, splitUpgrades) } // processUpgradeList takes in a list of upgrades and creates a batch upgrade file -func processUpgradeList(cfg *cosmovisor.Config, upgradeList []string) error { +func processUpgradeList(cfg *cosmovisor.Config, upgradeList [][]string) error { upgradeInfoPaths := []string{} - for i, as := range upgradeList { - a := strings.Split(as, ":") - if len(a) != 3 { - return fmt.Errorf("argument at position %d (%s) is invalid", i, as) + for i, upgrade := range upgradeList { + if len(upgrade) != 3 { + return fmt.Errorf("argument at position %d (%s) is invalid", i, upgrade) } - upgradeName := filepath.Base(a[0]) - upgradePath := a[1] - upgradeHeight, err := strconv.ParseInt(a[2], 10, 64) + upgradeName := filepath.Base(upgrade[0]) + upgradePath := upgrade[1] + upgradeHeight, err := strconv.ParseInt(upgrade[2], 10, 64) if err != nil { - return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, a[2]) + return fmt.Errorf("upgrade height at position %d (%s) is invalid", i, upgrade[2]) } upgradeInfoPath := cfg.UpgradeInfoFilePath() + "." + upgradeName upgradeInfoPaths = append(upgradeInfoPaths, upgradeInfoPath) @@ -124,15 +120,23 @@ func processUpgradeList(cfg *cosmovisor.Config, upgradeList []string) error { return nil } -// processUpgradeFile takes in a batch upgrade file, validates it and copies it to the upgrade directory +// processUpgradeFile takes in a CSV batch upgrade file, parses it and calls processUpgradeList func processUpgradeFile(cfg *cosmovisor.Config, upgradeFile string) error { - b, err := os.ReadFile(upgradeFile) + file, err := os.Open(upgradeFile) if err != nil { - return fmt.Errorf("error reading upgrade file %s: %w", upgradeFile, err) + return fmt.Errorf("error opening upgrade CSV file %s: %w", upgradeFile, err) } - var batch []upgradetypes.Plan - if err := json.Unmarshal(b, &batch); err != nil { - return fmt.Errorf("error unmarshalling upgrade file %s: %w", upgradeFile, err) + defer file.Close() + + r := csv.NewReader(file) + r.FieldsPerRecord = 3 + r.TrimLeadingSpace = true + records, err := r.ReadAll() + if err != nil { + return fmt.Errorf("error parsing upgrade CSV file %s: %w", upgradeFile, err) + } + if err := processUpgradeList(cfg, records); err != nil { + return err } - return copyFile(upgradeFile, cfg.UpgradeInfoBatchFilePath()) + return nil } From 7e8112db1c071eaf673cfe3faa0c21df4c8a30f3 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Wed, 2 Oct 2024 22:55:09 +1000 Subject: [PATCH 21/24] mark flags with mutex --- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 3ad82600e06..2ca0ca6ea37 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -42,6 +42,7 @@ cosmovisor add-batch-upgrade --upgrade-file /path/to/batch_upgrade.json`, cmd.Flags().String("upgrade-file", "", "Path to a batch upgrade file which is a JSON array of upgrade-info objects") cmd.Flags().StringSlice("upgrade-list", []string{}, "List of comma-separated upgrades in the format 'name:path/to/binary:height'") + cmd.MarkFlagsMutuallyExclusive("upgrade-file", "upgrade-list") return cmd } From 57bf0a03fe89fc85ee24700e3df8c9c715777be5 Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 3 Oct 2024 00:26:17 +1000 Subject: [PATCH 22/24] nit --- tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go index 2ca0ca6ea37..a66f65b406a 100644 --- a/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go +++ b/tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go @@ -34,7 +34,7 @@ You can provide upgrades in two ways: Note: You must provide either --upgrade-file or --upgrade-list.`, Example: `cosmovisor add-batch-upgrade --upgrade-list upgrade_v2:/path/to/v2/binary:1000000,upgrade_v3:/path/to/v3/binary:2000000 -cosmovisor add-batch-upgrade --upgrade-file /path/to/batch_upgrade.json`, +cosmovisor add-batch-upgrade --upgrade-file /path/to/batch_upgrade.csv`, SilenceUsage: true, Args: cobra.NoArgs, RunE: addBatchUpgrade, From f91bfc38e5592e715ee88e86822a14498b72a32b Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 3 Oct 2024 00:53:02 +1000 Subject: [PATCH 23/24] fix tests --- tools/cosmovisor/args_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index f121a3989d9..7cdbd196396 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -477,6 +477,7 @@ var newConfig = func( CustomPreUpgrade: customPreUpgrade, DisableRecase: disableRecase, ShutdownGrace: time.Duration(shutdownGrace), + GRPCAddress: "localhost:9090", } } From 4f31076bdc20ef8eedc6f1bc440c1d95c05150dc Mon Sep 17 00:00:00 2001 From: psiphi5 Date: Thu, 10 Oct 2024 13:32:32 +1100 Subject: [PATCH 24/24] fix merge --- tools/cosmovisor/args_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/tools/cosmovisor/args_test.go b/tools/cosmovisor/args_test.go index f805288c9da..12c28b8e795 100644 --- a/tools/cosmovisor/args_test.go +++ b/tools/cosmovisor/args_test.go @@ -479,7 +479,6 @@ var newConfig = func( CustomPreUpgrade: customPreUpgrade, DisableRecase: disableRecase, ShutdownGrace: time.Duration(shutdownGrace), - GRPCAddress: "localhost:9090", } }