feat: adding pre-upgrade command call in Cosmovisor (#10056)
Cosmosvisor calls pre-upgrade command on the application before upgrade
This commit is contained in:
parent
78b151dd97
commit
429255275e
|
@ -10,6 +10,7 @@ import (
|
|||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
@ -64,6 +65,13 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
|
||||
if !SkipUpgrade(args, l.fw.currentInfo) {
|
||||
err = doPreUpgrade(l.cfg)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
return true, DoUpgrade(l.cfg, l.fw.currentInfo)
|
||||
}
|
||||
|
||||
|
@ -143,3 +151,65 @@ func doBackup(cfg *Config) error {
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doPreUpgrade runs the pre-upgrade command defined by the application
|
||||
func doPreUpgrade(cfg *Config) error {
|
||||
bin, err := cfg.CurrentBin()
|
||||
preUpgradeCmd := exec.Command(bin, "pre-upgrade")
|
||||
|
||||
_, err = preUpgradeCmd.Output()
|
||||
|
||||
if err != nil {
|
||||
if err.(*exec.ExitError).ProcessState.ExitCode() == 1 {
|
||||
fmt.Println("pre-upgrade command does not exist. continuing the upgrade.")
|
||||
return nil
|
||||
}
|
||||
if err.(*exec.ExitError).ProcessState.ExitCode() == 30 {
|
||||
return fmt.Errorf("pre-upgrade command failed : %w", err)
|
||||
}
|
||||
if err.(*exec.ExitError).ProcessState.ExitCode() == 31 {
|
||||
fmt.Println("pre-upgrade command failed. retrying.")
|
||||
return doPreUpgrade(cfg)
|
||||
}
|
||||
}
|
||||
fmt.Println("pre-upgrade successful. continuing the upgrade.")
|
||||
return nil
|
||||
}
|
||||
|
||||
// skipUpgrade checks if pre-upgrade script must be run. If the height in the upgrade plan matches any of the heights provided in --safe-skip-upgrade, the script is not run
|
||||
func SkipUpgrade(args []string, upgradeInfo UpgradeInfo) bool {
|
||||
skipUpgradeHeights := UpgradeSkipHeights(args)
|
||||
for _, h := range skipUpgradeHeights {
|
||||
if h == int(upgradeInfo.Height) {
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UpgradeSkipHeights gets all the heights provided when
|
||||
// simd start --unsafe-skip-upgrades <height1> <optional_height_2> ... <optional_height_N>
|
||||
func UpgradeSkipHeights(args []string) []int {
|
||||
var heights []int
|
||||
for i, arg := range args {
|
||||
if arg == "--unsafe-skip-upgrades" {
|
||||
j := i + 1
|
||||
|
||||
for j < len(args) {
|
||||
tArg := args[j]
|
||||
if strings.HasPrefix(tArg, "-") {
|
||||
break
|
||||
}
|
||||
h, err := strconv.Atoi(tArg)
|
||||
if err == nil {
|
||||
heights = append(heights, h)
|
||||
}
|
||||
j++
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
return heights
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/cosmovisor"
|
||||
|
@ -127,3 +128,78 @@ func (s *processTestSuite) TestLaunchProcessWithDownloads() {
|
|||
require.NoError(err)
|
||||
require.Equal(cfg.UpgradeBin("chain3"), 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 {
|
||||
args []string
|
||||
upgradeInfo cosmovisor.UpgradeInfo
|
||||
expectRes bool
|
||||
}{{
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades"},
|
||||
upgradeInfo: cosmovisor.UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 123},
|
||||
expectRes: false,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "--abcd"},
|
||||
upgradeInfo: cosmovisor.UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 123},
|
||||
expectRes: false,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "--abcd"},
|
||||
upgradeInfo: cosmovisor.UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 11},
|
||||
expectRes: false,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "20", "--abcd"},
|
||||
upgradeInfo: cosmovisor.UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 20},
|
||||
expectRes: true,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "20", "--abcd", "34"},
|
||||
upgradeInfo: cosmovisor.UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 34},
|
||||
expectRes: false,
|
||||
}}
|
||||
|
||||
for i := range cases {
|
||||
tc := cases[i]
|
||||
require := require.New(t)
|
||||
h := cosmovisor.SkipUpgrade(tc.args, tc.upgradeInfo)
|
||||
require.Equal(h, tc.expectRes)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpgradeSkipHeights tests if correct skip upgrade heights are identified from the cli args
|
||||
func TestUpgradeSkipHeights(t *testing.T) {
|
||||
cases := []struct {
|
||||
args []string
|
||||
expectRes []int
|
||||
}{{
|
||||
args: []string{},
|
||||
expectRes: nil,
|
||||
}, {
|
||||
args: []string{"appb", "start"},
|
||||
expectRes: nil,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades"},
|
||||
expectRes: nil,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "--abcd"},
|
||||
expectRes: nil,
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "--abcd"},
|
||||
expectRes: []int{10},
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "20", "--abcd"},
|
||||
expectRes: []int{10, 20},
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "20", "--abcd", "34"},
|
||||
expectRes: []int{10, 20},
|
||||
}, {
|
||||
args: []string{"appb", "start", "--unsafe-skip-upgrades", "10", "as", "20", "--abcd"},
|
||||
expectRes: []int{10, 20},
|
||||
}}
|
||||
|
||||
for i := range cases {
|
||||
tc := cases[i]
|
||||
require := require.New(t)
|
||||
h := cosmovisor.UpgradeSkipHeights(tc.args)
|
||||
require.Equal(h, tc.expectRes)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ parent:
|
|||
This folder contains all the migration guides to update your app and modules to Cosmos v0.40 Stargate.
|
||||
|
||||
1. [App and Modules Migration](./app_and_modules.md)
|
||||
1. [Pre Upgrade](./pre-upgrade.md)
|
||||
1. [Chain Upgrade Guide to v0.40](./chain-upgrade-guide-040.md)
|
||||
1. [REST Endpoints Migration](./rest.md)
|
||||
1. [Keyring Migration](./keyring.md)
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
# Pre-Upgrade Handling
|
||||
|
||||
Cosmovisor supports custom pre-upgrade handling. Use pre-upgrade handling when you need to implement application config changes that are required in the newer version before you perform the upgrade.
|
||||
|
||||
Using Cosmovisor pre-upgrade handling is optional. If pre-upgrade handling is not implemented, the upgrade continues.
|
||||
|
||||
For example, make the required new-version changes to `app.toml` settings during the pre-upgrade handling. The pre-upgrade handling process means that the file does not have to be manually updated after the upgrade.
|
||||
|
||||
Before the application binary is upgraded, Cosmovisor calls a `pre-upgrade` command that can be implemented by the application.
|
||||
|
||||
The `pre-upgrade` command does not take in any command-line arguments and is expected to terminate with the following exit codes:
|
||||
|
||||
|
||||
| Exit status code | How it is handled in Cosmosvisor |
|
||||
|------------------|---------------------------------------------------------------------------------------------------------------------|
|
||||
| `0` | Assumes `pre-upgrade` command executed successfully and continues the upgrade. |
|
||||
| `1` | Default exit code when `pre-upgrade` command has not been implemented. |
|
||||
| `30` | `pre-upgrade` command was executed but failed. This fails the entire upgrade. |
|
||||
| `31` | `pre-upgrade` command was executed but failed. But the command is retried until exit code `1` or `30` are returned. |
|
||||
|
||||
|
||||
## Sample
|
||||
|
||||
Here is a sample structure of the `pre-upgrade` command:
|
||||
|
||||
```go
|
||||
func preUpgradeCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "pre-upgrade",
|
||||
Short: "Pre-upgrade command",
|
||||
Long: "Pre-upgrade command to implement custom pre-upgrade handling",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
err := HandlePreUpgrade()
|
||||
|
||||
if err != nil {
|
||||
os.Exit(30)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Ensure that the pre-upgrade command has been registered in the application:
|
||||
```go
|
||||
rootCmd.AddCommand(
|
||||
// ..
|
||||
preUpgradeCommand(),
|
||||
// ..
|
||||
)
|
||||
```
|
Loading…
Reference in New Issue