From 75a4c1e9ea90aeebba3f2f4ea4f00433713d0167 Mon Sep 17 00:00:00 2001 From: Riccardo Montagnin Date: Fri, 5 Aug 2022 08:49:14 +0200 Subject: [PATCH] build(deps): bump github.com/cosmos/cosmos-sdk to v0.45.7 --- client/keys/add_test.go | 3 +- codec/legacy/codec.go | 5 +- cosmovisor/.gitignore | 1 - cosmovisor/README.md | 23 +- cosmovisor/args.go | 113 ++-------- cosmovisor/buffer_test.go | 34 --- cosmovisor/cmd/cosmovisor/main.go | 14 +- cosmovisor/go.mod | 6 +- cosmovisor/go.sum | 12 +- cosmovisor/process.go | 201 +++++++++--------- cosmovisor/process_test.go | 121 +++++------ cosmovisor/scanner_test.go | 97 ++++----- .../download/cosmovisor/genesis/bin/autod | 13 +- cosmovisor/testdata/download/data/.gitkeep | 0 cosmovisor/testdata/repo/chain2-zip_bin/autod | 13 -- .../testdata/repo/chain2-zip_bin/autod.zip | Bin 547 -> 0 bytes .../testdata/repo/chain3-zip_dir/autod.zip | Bin 362 -> 0 bytes .../testdata/repo/chain3-zip_dir/bin/autod | 4 - .../testdata/repo/ref_to_chain3-zip_dir.json | 5 - cosmovisor/testdata/repo/ref_zipped | 5 + cosmovisor/testdata/repo/zip_binary/autod | 8 + cosmovisor/testdata/repo/zip_binary/autod.zip | Bin 0 -> 511 bytes .../testdata/repo/zip_directory/autod.zip | Bin 0 -> 362 bytes .../testdata/repo/zip_directory/bin/autod | 4 + .../testdata/upgrade-files/f1-good.json | 1 - .../testdata/upgrade-files/f2-bad-type-2.json | 1 - .../testdata/upgrade-files/f2-bad-type.json | 1 - .../testdata/upgrade-files/f3-empty.json | 1 - .../testdata/upgrade-files/f4-empty-obj.json | 1 - .../upgrade-files/f5-partial-obj-1.json | 1 - .../upgrade-files/f5-partial-obj-2.json | 1 - .../validate/cosmovisor/genesis/bin/dummyd | 2 - cosmovisor/testdata/validate/data/.gitkeep | 0 cosmovisor/upgrade.go | 39 +++- cosmovisor/upgrade_test.go | 63 +++--- simapp/simd/cmd/root.go | 3 +- simapp/simd/cmd/testnet_test.go | 7 +- store/cachemulti/store_test.go | 3 +- store/types/gas.go | 2 +- types/handler_test.go | 2 +- x/distribution/types/proposal.go | 1 + x/gov/types/codec.go | 10 + x/params/types/proposal/proposal.go | 1 + x/upgrade/types/proposal.go | 2 + 44 files changed, 345 insertions(+), 479 deletions(-) delete mode 100644 cosmovisor/.gitignore delete mode 100644 cosmovisor/buffer_test.go delete mode 100644 cosmovisor/testdata/download/data/.gitkeep delete mode 100755 cosmovisor/testdata/repo/chain2-zip_bin/autod delete mode 100644 cosmovisor/testdata/repo/chain2-zip_bin/autod.zip delete mode 100644 cosmovisor/testdata/repo/chain3-zip_dir/autod.zip delete mode 100755 cosmovisor/testdata/repo/chain3-zip_dir/bin/autod delete mode 100644 cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json create mode 100644 cosmovisor/testdata/repo/ref_zipped create mode 100755 cosmovisor/testdata/repo/zip_binary/autod create mode 100644 cosmovisor/testdata/repo/zip_binary/autod.zip create mode 100644 cosmovisor/testdata/repo/zip_directory/autod.zip create mode 100755 cosmovisor/testdata/repo/zip_directory/bin/autod delete mode 100644 cosmovisor/testdata/upgrade-files/f1-good.json delete mode 100644 cosmovisor/testdata/upgrade-files/f2-bad-type-2.json delete mode 100644 cosmovisor/testdata/upgrade-files/f2-bad-type.json delete mode 100644 cosmovisor/testdata/upgrade-files/f3-empty.json delete mode 100644 cosmovisor/testdata/upgrade-files/f4-empty-obj.json delete mode 100644 cosmovisor/testdata/upgrade-files/f5-partial-obj-1.json delete mode 100644 cosmovisor/testdata/upgrade-files/f5-partial-obj-2.json delete mode 100644 cosmovisor/testdata/validate/data/.gitkeep diff --git a/client/keys/add_test.go b/client/keys/add_test.go index 6eab3a00deb3..88168bcdf9de 100644 --- a/client/keys/add_test.go +++ b/client/keys/add_test.go @@ -11,8 +11,6 @@ import ( "github.com/tendermint/tendermint/libs/cli" - bip39 "github.com/cosmos/go-bip39" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/crypto/hd" @@ -21,6 +19,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil" "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + bip39 "github.com/cosmos/go-bip39" ) func Test_runAddCmdBasic(t *testing.T) { diff --git a/codec/legacy/codec.go b/codec/legacy/codec.go index 0bc2e74993b8..a4a963b6d211 100644 --- a/codec/legacy/codec.go +++ b/codec/legacy/codec.go @@ -4,19 +4,18 @@ import ( "github.com/cosmos/cosmos-sdk/codec" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - sdk "github.com/cosmos/cosmos-sdk/types" ) // Cdc defines a global generic sealed Amino codec to be used throughout sdk. It // has all Tendermint crypto and evidence types registered. // // TODO: Deprecated - remove this global. -var Cdc = codec.NewLegacyAmino() +var Cdc *codec.LegacyAmino func init() { + Cdc = codec.NewLegacyAmino() cryptocodec.RegisterCrypto(Cdc) codec.RegisterEvidences(Cdc) - sdk.RegisterLegacyAminoCodec(Cdc) } // PrivKeyFromBytes unmarshals private key bytes and returns a PrivKey diff --git a/cosmovisor/.gitignore b/cosmovisor/.gitignore deleted file mode 100644 index fe1602e647d3..000000000000 --- a/cosmovisor/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/cosmovisor diff --git a/cosmovisor/README.md b/cosmovisor/README.md index 4e9b7a4b2716..e263966a49a0 100644 --- a/cosmovisor/README.md +++ b/cosmovisor/README.md @@ -4,10 +4,6 @@ *Note: If new versions of the application are not set up to run in-place store migrations, migrations will need to be run manually before restarting `cosmovisor` with the new binary. For this reason, we recommend applications adopt in-place store migrations.* -## Contributing - -Release branches has the following format `release/cosmovisor/vA.B.x`, where A and B are a number (eg: `release/cosmovisor/v0.1.x`). Releases are tagged using the following format: `cosmovisor/vA.B.C`. - ## Installation To install `cosmovisor`, run the following command: @@ -26,8 +22,6 @@ All arguments passed to `cosmovisor` will be passed to the application binary (a * `DAEMON_NAME` is the name of the binary itself (e.g. `gaiad`, `regend`, `simd`, etc.). * `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*), if set to `true`, will enable auto-downloading of new binaries (for security reasons, this is intended for full nodes rather than validators). By default, `cosmovisor` will not auto-download new binaries. * `DAEMON_RESTART_AFTER_UPGRADE` (*optional*), if set to `true`, will restart the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. By default, `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note that `cosmovisor` will not auto-restart the subprocess if there was an error. -* `DAEMON_POLL_INTERVAL` is the interval length in milliseconds for polling the upgrade plan file. Default: 300. -* `UNSAFE_SKIP_BACKUP` (defaults to `false`), if set to `false`, will backup the data before trying the upgrade. Otherwise it will upgrade directly without doing any backup. This is useful (and recommended) in case of failures and when needed to rollback. It is advised to use backup option, i.e., `UNSAFE_SKIP_BACKUP=false` ## Folder Layout @@ -41,9 +35,8 @@ All arguments passed to `cosmovisor` will be passed to the application binary (a │   └── $DAEMON_NAME └── upgrades └── - ├── bin - │   └── $DAEMON_NAME - └── upgrade-info.json + └── bin + └── $DAEMON_NAME ``` The `cosmovisor/` directory incudes a subdirectory for each version of the application (i.e. `genesis` or `upgrades/`). Within each subdirectory is the application binary (i.e. `bin/$DAEMON_NAME`) and any additional auxiliary files associated with each binary. `current` is a symbolic link to the currently active directory (i.e. `genesis` or `upgrades/`). The `name` variable in `upgrades/` is the URI-encoded name of the upgrade as specified in the upgrade module plan. @@ -73,18 +66,6 @@ In order to support downloadable binaries, a tarball for each upgrade binary wil The `DAEMON` specific code and operations (e.g. tendermint config, the application db, syncing blocks, etc.) all work as expected. The application binaries' directives such as command-line flags and environment variables also work as expected. - -### Detecting Upgrades - -`cosmovisor` is polling the `$DAEMON_HOME/data/upgrade-info.json` file for new upgrade instructions. The file is created by the x/upgrade module in `BeginBlocker` when an upgrade is detected and the blockchain reaches the upgrade height. -The following heuristic is applied to detect the upgrade: -+ When starting, `cosmovisor` doesn't know much about currently running upgrade, except the binary which is `current/bin/`. It tries to read the `current/update-info.json` file to get information about the current upgrade name. -+ If neither `cosmovisor/current/upgrade-info.json` nor `data/upgrade-info.json` exist, then `cosmovisor` will wait for `data/upgrade-info.json` file to trigger an upgrade. -+ If `cosmovisor/current/upgrade-info.json` doesn't exist but `data/upgrade-info.json` exists, then `cosmovisor` assumes that whatever is in `data/upgrade-info.json` is a valid upgrade request. In this case `cosmovisor` tries immediately to make an upgrade according to the `name` attribute in `data/upgrade-info.json`. -+ Otherwise, `cosmovisor` waits for changes in `upgrade-info.json`. As soon as a new upgrade name is recorded in the file, `cosmovisor` will trigger an upgrade mechanism. - -When the upgrade mechanism is triggered, `cosmovisor` will start by auto-downloading a new binary (if `DAEMON_ALLOW_DOWNLOAD_BINARIES` is enabled) into `cosmovisor//bin` (where `` is the `upgrade-info.json:name` attribute). `cosmovisor` will then update the `current` symbolic link to point to the new directory and save `data/upgrade-info.json` to `cosmovisor/current/upgrade-info.json`. - ## Auto-Download Generally, `cosmovisor` requires that the system administrator place all relevant binaries on disk before the upgrade happens. However, for people who don't need such control and want an easier setup (maybe they are syncing a non-validating fullnode and want to do little maintenance), there is another option. diff --git a/cosmovisor/args.go b/cosmovisor/args.go index e6dcf2c849e3..2b8dbeb5cc7a 100644 --- a/cosmovisor/args.go +++ b/cosmovisor/args.go @@ -1,39 +1,29 @@ package cosmovisor import ( - "encoding/json" + "bufio" "errors" "fmt" - "io/ioutil" "net/url" "os" "path/filepath" "strconv" - "time" ) const ( - rootName = "cosmovisor" - genesisDir = "genesis" - upgradesDir = "upgrades" - currentLink = "current" - upgradeFilename = "upgrade-info.json" + rootName = "cosmovisor" + genesisDir = "genesis" + upgradesDir = "upgrades" + currentLink = "current" ) -// must be the same as x/upgrade/types.UpgradeInfoFilename -const defaultFilename = "upgrade-info.json" - // Config is the information passed in to control the daemon type Config struct { Home string Name string AllowDownloadBinaries bool RestartAfterUpgrade bool - PollInterval time.Duration - UnsafeSkipBackup bool - - // currently running upgrade - currentUpgrade UpgradeInfo + LogBufferSize int } // Root returns the root directory where all info lives @@ -54,15 +44,10 @@ func (cfg *Config) UpgradeBin(upgradeName string) string { // UpgradeDir is the directory named upgrade func (cfg *Config) UpgradeDir(upgradeName string) string { safeName := url.PathEscape(upgradeName) - return filepath.Join(cfg.Home, rootName, upgradesDir, safeName) -} - -// UpgradeInfoFile is the expected upgrade-info filename created by `x/upgrade/keeper`. -func (cfg *Config) UpgradeInfoFilePath() string { - return filepath.Join(cfg.Home, "data", defaultFilename) + return filepath.Join(cfg.Root(), upgradesDir, safeName) } -// SymLinkToGenesis creates a symbolic link from "./current" to the genesis directory. +// Symlink to genesis func (cfg *Config) SymLinkToGenesis() (string, error) { genesis := filepath.Join(cfg.Root(), genesisDir) link := filepath.Join(cfg.Root(), currentLink) @@ -98,8 +83,7 @@ func (cfg *Config) CurrentBin() (string, error) { } // and return the binary - binpath := filepath.Join(dest, "bin", cfg.Name) - return binpath, nil + return filepath.Join(dest, "bin", cfg.Name), nil } // GetConfigFromEnv will read the environmental variables into a config @@ -118,22 +102,21 @@ func GetConfigFromEnv() (*Config, error) { cfg.RestartAfterUpgrade = true } - interval := os.Getenv("DAEMON_POLL_INTERVAL") - if interval != "" { - i, err := strconv.ParseUint(interval, 10, 32) + logBufferSizeStr := os.Getenv("DAEMON_LOG_BUFFER_SIZE") + if logBufferSizeStr != "" { + logBufferSize, err := strconv.Atoi(logBufferSizeStr) if err != nil { return nil, err } - cfg.PollInterval = time.Millisecond * time.Duration(i) + cfg.LogBufferSize = logBufferSize * 1024 } else { - cfg.PollInterval = 300 * time.Millisecond + cfg.LogBufferSize = bufio.MaxScanTokenSize } - cfg.UnsafeSkipBackup = os.Getenv("UNSAFE_SKIP_BACKUP") == "true" - if err := cfg.validate(); err != nil { return nil, err } + return cfg, nil } @@ -165,69 +148,3 @@ func (cfg *Config) validate() error { return nil } - -// SetCurrentUpgrade sets the named upgrade to be the current link, returns error if this binary doesn't exist -func (cfg *Config) SetCurrentUpgrade(u UpgradeInfo) error { - // ensure named upgrade exists - bin := cfg.UpgradeBin(u.Name) - - if err := EnsureBinary(bin); err != nil { - return err - } - - // set a symbolic link - link := filepath.Join(cfg.Root(), currentLink) - safeName := url.PathEscape(u.Name) - upgrade := filepath.Join(cfg.Root(), upgradesDir, safeName) - - // remove link if it exists - if _, err := os.Stat(link); err == nil { - os.Remove(link) - } - - // point to the new directory - if err := os.Symlink(upgrade, link); err != nil { - return fmt.Errorf("creating current symlink: %w", err) - } - - cfg.currentUpgrade = u - f, err := os.Create(filepath.Join(upgrade, upgradeFilename)) - if err != nil { - return err - } - bz, err := json.Marshal(u) - if err != nil { - return err - } - if _, err := f.Write(bz); err != nil { - return err - } - return f.Close() -} - -func (cfg *Config) UpgradeInfo() UpgradeInfo { - if cfg.currentUpgrade.Name != "" { - return cfg.currentUpgrade - } - - filename := filepath.Join(cfg.Root(), currentLink, upgradeFilename) - _, err := os.Lstat(filename) - var u UpgradeInfo - var bz []byte - if err != nil { // no current directory - goto returnError - } - if bz, err = ioutil.ReadFile(filename); err != nil { - goto returnError - } - if err = json.Unmarshal(bz, &u); err != nil { - goto returnError - } - cfg.currentUpgrade = u - return cfg.currentUpgrade - -returnError: - fmt.Println("[cosmovisor], error reading", filename, err) - cfg.currentUpgrade.Name = "_" - return cfg.currentUpgrade -} diff --git a/cosmovisor/buffer_test.go b/cosmovisor/buffer_test.go deleted file mode 100644 index 04dd2bb4c517..000000000000 --- a/cosmovisor/buffer_test.go +++ /dev/null @@ -1,34 +0,0 @@ -package cosmovisor_test - -import ( - "bytes" - "sync" -) - -// buffer is a thread safe bytes buffer -type buffer struct { - b bytes.Buffer - m sync.Mutex -} - -func NewBuffer() *buffer { - return &buffer{} -} - -func (b *buffer) Write(bz []byte) (int, error) { - b.m.Lock() - defer b.m.Unlock() - return b.b.Write(bz) -} - -func (b *buffer) String() string { - b.m.Lock() - defer b.m.Unlock() - return b.b.String() -} - -func (b *buffer) Reset() { - b.m.Lock() - defer b.m.Unlock() - b.b.Reset() -} diff --git a/cosmovisor/cmd/cosmovisor/main.go b/cosmovisor/cmd/cosmovisor/main.go index f956a2e088f0..a165acab38f6 100644 --- a/cosmovisor/cmd/cosmovisor/main.go +++ b/cosmovisor/cmd/cosmovisor/main.go @@ -9,7 +9,7 @@ import ( func main() { if err := Run(os.Args[1:]); err != nil { - fmt.Fprintf(os.Stderr, "[cosmovisor] %+v\n", err) + fmt.Fprintf(os.Stderr, "%+v\n", err) os.Exit(1) } } @@ -20,19 +20,11 @@ func Run(args []string) error { if err != nil { return err } - launcher, err := cosmovisor.NewLauncher(cfg) - if err != nil { - return err - } - doUpgrade, err := launcher.Run(args, os.Stdout, os.Stderr) + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) // if RestartAfterUpgrade, we launch after a successful upgrade (only condition LaunchProcess returns nil) for cfg.RestartAfterUpgrade && err == nil && doUpgrade { - fmt.Println("[cosmovisor] upgrade detected, relaunching the app ", cfg.Name) - doUpgrade, err = launcher.Run(args, os.Stdout, os.Stderr) - } - if doUpgrade && err == nil { - fmt.Println("[cosmovisor] upgrade detected, DAEMON_RESTART_AFTER_UPGRADE is off. Verify new upgrade and start cosmovisor again.") + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, os.Stdout, os.Stderr) } return err } diff --git a/cosmovisor/go.mod b/cosmovisor/go.mod index 70d184cba36e..fe6be0cdd4e0 100644 --- a/cosmovisor/go.mod +++ b/cosmovisor/go.mod @@ -1,9 +1,9 @@ module github.com/cosmos/cosmos-sdk/cosmovisor -go 1.15 +go 1.14 require ( github.com/hashicorp/go-getter v1.4.1 - github.com/otiai10/copy v1.4.2 - github.com/stretchr/testify v1.7.0 + github.com/otiai10/copy v1.2.0 + github.com/stretchr/testify v1.6.1 ) diff --git a/cosmovisor/go.sum b/cosmovisor/go.sum index 5d9538f4f96b..d4e8919a2680 100644 --- a/cosmovisor/go.sum +++ b/cosmovisor/go.sum @@ -61,20 +61,20 @@ github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnG github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/otiai10/copy v1.4.2 h1:RTiz2sol3eoXPLF4o+YWqEybwfUa/Q2Nkc4ZIUs3fwI= -github.com/otiai10/copy v1.4.2/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= +github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= +github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0 h1:TJIWdbX0B+kpNagQrjgq8bCMrbhiuX73M2XwgtDMoOI= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= -github.com/otiai10/mint v1.3.2 h1:VYWnrP5fXmz1MXvjuUvcBrXSjGE6xjON+axB/UrpO3E= -github.com/otiai10/mint v1.3.2/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/otiai10/mint v1.3.1 h1:BCmzIS3n71sGfHB5NMNDB3lHYPz8fWSkCAErHed//qc= +github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/ulikunitz/xz v0.5.5 h1:pFrO0lVpTBXLpYw+pnLj6TbvHuyjXMfjGeCwSqCVwok= github.com/ulikunitz/xz v0.5.5/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= diff --git a/cosmovisor/process.go b/cosmovisor/process.go index 98e0b9ef5ee6..6a67f65e162e 100644 --- a/cosmovisor/process.go +++ b/cosmovisor/process.go @@ -1,50 +1,57 @@ package cosmovisor import ( - "encoding/json" + "bufio" "fmt" "io" - "io/ioutil" "log" "os" "os/exec" "os/signal" - "path/filepath" "strings" + "sync" "syscall" - "time" - - "github.com/otiai10/copy" ) -type Launcher struct { - cfg *Config - fw *fileWatcher -} - -func NewLauncher(cfg *Config) (Launcher, error) { - fw, err := newUpgradeFileWatcher(cfg.UpgradeInfoFilePath(), cfg.PollInterval) - return Launcher{cfg, fw}, err -} - -// 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. -func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) { - bin, err := l.cfg.CurrentBin() +// LaunchProcess runs a subprocess and returns when the subprocess exits, +// either when it dies, or *after* a successful upgrade. +func LaunchProcess(cfg *Config, args []string, stdout, stderr io.Writer) (bool, error) { + bin, err := cfg.CurrentBin() if err != nil { return false, fmt.Errorf("error creating symlink to genesis: %w", err) } if err := EnsureBinary(bin); err != nil { - return false, fmt.Errorf("current binary is invalid: %w", err) + return false, fmt.Errorf("current binary invalid: %w", err) } - fmt.Println("[cosmovisor] running ", bin, args) + cmd := exec.Command(bin, args...) - cmd.Stdout = stdout - cmd.Stderr = stderr + outpipe, err := cmd.StdoutPipe() + if err != nil { + return false, err + } + + errpipe, err := cmd.StderrPipe() + if err != nil { + return false, err + } + + scanOut := bufio.NewScanner(io.TeeReader(outpipe, stdout)) + scanErr := bufio.NewScanner(io.TeeReader(errpipe, stderr)) + // set scanner's buffer size to cfg.LogBufferSize, and ensure larger than bufio.MaxScanTokenSize otherwise fallback to bufio.MaxScanTokenSize + var maxCapacity int + if cfg.LogBufferSize < bufio.MaxScanTokenSize { + maxCapacity = bufio.MaxScanTokenSize + } else { + maxCapacity = cfg.LogBufferSize + } + bufOut := make([]byte, maxCapacity) + bufErr := make([]byte, maxCapacity) + scanOut.Buffer(bufOut, maxCapacity) + scanErr.Buffer(bufErr, maxCapacity) + if err := cmd.Start(); err != nil { - return false, fmt.Errorf("launching process %s %s failed: %w", bin, strings.Join(args, " "), err) + return false, fmt.Errorf("launching process %s %s: %w", bin, strings.Join(args, " "), err) } sigs := make(chan os.Signal, 1) @@ -52,93 +59,95 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) { go func() { sig := <-sigs if err := cmd.Process.Signal(sig); err != nil { - log.Fatal(bin, "terminated. Error:", err) + log.Fatal(err) } }() - needsUpdate, err := l.WaitForUpgradeOrExit(cmd) - if err != nil || !needsUpdate { + // three ways to exit - command ends, find regexp in scanOut, find regexp in scanErr + upgradeInfo, err := WaitForUpgradeOrExit(cmd, scanOut, scanErr) + if err != nil { return false, err } - if err := doBackup(l.cfg); err != nil { - return false, err + + if upgradeInfo != nil { + return true, DoUpgrade(cfg, upgradeInfo) } - return true, DoUpgrade(l.cfg, l.fw.currentInfo) + return false, nil } -// WaitForUpgradeOrExit checks upgrade plan file created by the app. -// When it returns, the process (app) is finished. -// -// It returns (true, nil) if an upgrade should be initiated (and we killed the process) -// It returns (false, err) if the process died by itself, or there was an issue reading the upgrade-info file. -// It returns (false, nil) if the process exited normally without triggering an upgrade. This is very unlikely -// to happened with "start" but may happened with short-lived commands like `gaiad export ...` -func (l Launcher) WaitForUpgradeOrExit(cmd *exec.Cmd) (bool, error) { - currentUpgrade := l.cfg.UpgradeInfo() - var cmdDone = make(chan error) - go func() { - cmdDone <- cmd.Wait() - }() - - select { - case <-l.fw.MonitorUpdate(currentUpgrade): - // upgrade - kill the process and restart - _ = cmd.Process.Kill() - case err := <-cmdDone: - l.fw.Stop() - // no error -> command exits normally (eg. short command like `gaiad version`) - if err == nil { - return false, nil - } - // the app x/upgrade causes a panic and the app can die before the filwatcher finds the - // update, so we need to recheck update-info file. - if !l.fw.CheckUpdate(currentUpgrade) { - return false, err - } - } - return true, nil +// WaitResult is used to wrap feedback on cmd state with some mutex logic. +// This is needed as multiple go-routines can affect this - two read pipes that can trigger upgrade +// As well as the command, which can fail +type WaitResult struct { + // both err and info may be updated from several go-routines + // access is wrapped by mutex and should only be done through methods + err error + info *UpgradeInfo + mutex sync.Mutex } -func doBackup(cfg *Config) error { - // take backup if `UNSAFE_SKIP_BACKUP` is not set. - if !cfg.UnsafeSkipBackup { - // check if upgrade-info.json is not empty. - var uInfo UpgradeInfo - upgradeInfoFile, err := ioutil.ReadFile(filepath.Join(cfg.Home, "data", "upgrade-info.json")) - if err != nil { - return fmt.Errorf("error while reading upgrade-info.json: %w", err) - } - - err = json.Unmarshal(upgradeInfoFile, &uInfo) - if err != nil { - return err - } - - if uInfo.Name == "" { - return fmt.Errorf("upgrade-info.json is empty") - } +// AsResult reads the data protected by mutex to avoid race conditions +func (u *WaitResult) AsResult() (*UpgradeInfo, error) { + u.mutex.Lock() + defer u.mutex.Unlock() + return u.info, u.err +} - // a destination directory, Format YYYY-MM-DD - st := time.Now() - stStr := fmt.Sprintf("%d-%d-%d", st.Year(), st.Month(), st.Day()) - dst := filepath.Join(cfg.Home, fmt.Sprintf("data"+"-backup-%s", stStr)) +// SetError will set with the first error using a mutex +// don't set it once info is set, that means we chose to kill the process +func (u *WaitResult) SetError(myErr error) { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.info == nil && myErr != nil { + u.err = myErr + } +} - fmt.Printf("starting to take backup of data directory at time %s", st) +// SetUpgrade sets first non-nil upgrade info, ensure error is then nil +// pass in a command to shutdown on successful upgrade +func (u *WaitResult) SetUpgrade(up *UpgradeInfo) { + u.mutex.Lock() + defer u.mutex.Unlock() + if u.info == nil && up != nil { + u.info = up + u.err = nil + } +} - // copy the $DAEMON_HOME/data to a backup dir - err = copy.Copy(filepath.Join(cfg.Home, "data"), dst) +// WaitForUpgradeOrExit listens to both output streams of the process, as well as the process state itself +// When it returns, the process is finished and all streams have closed. +// +// It returns (info, nil) if an upgrade should be initiated (and we killed the process) +// It returns (nil, err) if the process died by itself, or there was an issue reading the pipes +// It returns (nil, nil) if the process exited normally without triggering an upgrade. This is very unlikely +// to happened with "start" but may happened with short-lived commands like `gaiad export ...` +func WaitForUpgradeOrExit(cmd *exec.Cmd, scanOut, scanErr *bufio.Scanner) (*UpgradeInfo, error) { + var res WaitResult + waitScan := func(scan *bufio.Scanner) { + upgrade, err := WaitForUpdate(scan) if err != nil { - return fmt.Errorf("error while taking data backup: %w", err) + res.SetError(err) + } else if upgrade != nil { + res.SetUpgrade(upgrade) + // now we need to kill the process + _ = cmd.Process.Kill() } - - // backup is done, lets check endtime to calculate total time taken for backup process - et := time.Now() - timeTaken := et.Sub(st) - fmt.Printf("backup saved at location: %s, completed at time: %s\n"+ - "time taken to complete the backup: %s", dst, et, timeTaken) } - return nil + // wait for the scanners, which can trigger upgrade and kill cmd + go waitScan(scanOut) + go waitScan(scanErr) + + // if the command exits normally (eg. short command like `gaiad version`), just return (nil, nil) + // we often get broken read pipes if it runs too fast. + // if we had upgrade info, we would have killed it, and thus got a non-nil error code + err := cmd.Wait() + if err == nil { + return nil, nil + } + // this will set the error code if it wasn't killed due to upgrade + res.SetError(err) + return res.AsResult() } diff --git a/cosmovisor/process_test.go b/cosmovisor/process_test.go index 407954a308d4..3b9a553ed638 100644 --- a/cosmovisor/process_test.go +++ b/cosmovisor/process_test.go @@ -4,7 +4,7 @@ package cosmovisor_test import ( - "fmt" + "bytes" "testing" "github.com/stretchr/testify/suite" @@ -23,108 +23,93 @@ func TestProcessTestSuite(t *testing.T) { // TestLaunchProcess will try running the script a few times and watch upgrades work properly // and args are passed through 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} + cfg := &cosmovisor.Config{Home: home, Name: "dummyd"} // should run the genesis binary and produce expected output - var stdout, stderr = NewBuffer(), NewBuffer() + var stdout, stderr bytes.Buffer currentBin, err := cfg.CurrentBin() - require.NoError(err) - require.Equal(cfg.GenesisBin(), currentBin) + s.Require().NoError(err) - launcher, err := cosmovisor.NewLauncher(cfg) - require.NoError(err) + s.Require().Equal(cfg.GenesisBin(), currentBin) - 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()) + args := []string{"foo", "bar", "1234"} + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Genesis foo bar 1234\nUPGRADE \"chain2\" NEEDED at height: 49: {}\n", stdout.String()) // ensure this is upgraded now and produces new output currentBin, err = cfg.CurrentBin() - require.NoError(err) - - require.Equal(cfg.UpgradeBin("chain2"), currentBin) + s.Require().NoError(err) + s.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()) + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().False(doUpgrade) + s.Require().Equal("", stderr.String()) + s.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) + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) } // TestLaunchProcess will try running the script a few times and watch upgrades work properly // and args are passed through func (s *processTestSuite) TestLaunchProcessWithDownloads() { - // test case upgrade path (binaries from testdata/download directory): - // genesis -> chain2-zip_bin - // chain2-zip_bin -> ref_to_chain3-zip_dir.json = (json for the next download instructions) -> chain3-zip_dir - // chain3-zip_dir - doesn't upgrade - require := s.Require() + // this is a fun path + // genesis -> "chain2" = zip_binary + // zip_binary -> "chain3" = ref_zipped -> zip_directory + // zip_directory no upgrade home := copyTestData(s.T(), "download") - cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true, PollInterval: 100, UnsafeSkipBackup: true} - upgradeFilename := cfg.UpgradeInfoFilePath() + cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true} // should run the genesis binary and produce expected output + var stdout, stderr bytes.Buffer currentBin, err := cfg.CurrentBin() - require.NoError(err) - require.Equal(cfg.GenesisBin(), currentBin) - - launcher, err := cosmovisor.NewLauncher(cfg) - require.NoError(err) + s.Require().NoError(err) - var stdout, stderr = NewBuffer(), NewBuffer() - args := []string{"some", "args", upgradeFilename} - doUpgrade, err := launcher.Run(args, stdout, stderr) + s.Require().Equal(cfg.GenesisBin(), currentBin) + args := []string{"some", "args"} + doUpgrade, err := cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Preparing auto-download some args\n"+`ERROR: UPGRADE "chain2" NEEDED at height: 49: {"binaries":{"linux/amd64":"https://github.com/cosmos/cosmos-sdk/raw/51249cb93130810033408934454841c98423ed4b/cosmovisor/testdata/repo/zip_binary/autod.zip?checksum=sha256:dc48829b4126ae95bc0db316c66d4e9da5f3db95e212665b6080638cca77e998"}} module=main`+"\n", stdout.String()) - require.NoError(err) - require.True(doUpgrade) - require.Equal("", stderr.String()) - require.Equal("Genesis autod. Args: some args "+upgradeFilename+"\n"+`ERROR: UPGRADE "chain2" NEEDED at height: 49: zip_binary`+"\n", stdout.String()) + // ensure this is upgraded now and produces new output currentBin, err = cfg.CurrentBin() - require.NoError(err) - require.Equal(cfg.UpgradeBin("chain2"), currentBin) - - // start chain2 + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin) + args = []string{"run", "--fast"} stdout.Reset() stderr.Reset() - args = []string{"run", "--fast", upgradeFilename} - doUpgrade, err = launcher.Run(args, stdout, stderr) - require.NoError(err) + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().True(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Chain 2 from zipped binary link to referral\nArgs: run --fast\n"+`ERROR: UPGRADE "chain3" NEEDED at height: 936: https://github.com/cosmos/cosmos-sdk/raw/0eae1a50612b8bf803336d35055896fbddaa1ddd/cosmovisor/testdata/repo/ref_zipped?checksum=sha256:0a428575de718ed3cf0771c9687eefaf6f19359977eca4d94a0abd0e11ef8e64 module=main`+"\n", stdout.String()) - require.Equal("", stderr.String()) - require.Equal("Chain 2 from zipped binary\nArgs: run --fast "+upgradeFilename+"\n"+`ERROR: UPGRADE "chain3" NEEDED at height: 936: ref_to_chain3-zip_dir.json module=main`+"\n", stdout.String()) // ended with one more upgrade - require.True(doUpgrade) currentBin, err = cfg.CurrentBin() - require.NoError(err) - require.Equal(cfg.UpgradeBin("chain3"), currentBin) - - // run the last chain - args = []string{"end", "--halt", upgradeFilename} + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain3"), currentBin) + // make sure this is the proper binary now.... + args = []string{"end", "--halt"} stdout.Reset() stderr.Reset() - doUpgrade, err = launcher.Run(args, stdout, stderr) - require.NoError(err) - require.False(doUpgrade) - require.Equal("", stderr.String()) - require.Equal("Chain 3 from zipped directory\nArgs: end --halt "+upgradeFilename+"\n", stdout.String()) + doUpgrade, err = cosmovisor.LaunchProcess(cfg, args, &stdout, &stderr) + s.Require().NoError(err) + s.Require().False(doUpgrade) + s.Require().Equal("", stderr.String()) + s.Require().Equal("Chain 2 from zipped directory\nArgs: end --halt\n", stdout.String()) // and this doesn't upgrade currentBin, err = cfg.CurrentBin() - require.NoError(err) - require.Equal(cfg.UpgradeBin("chain3"), currentBin) + s.Require().NoError(err) + s.Require().Equal(cfg.UpgradeBin("chain3"), currentBin) } diff --git a/cosmovisor/scanner_test.go b/cosmovisor/scanner_test.go index cabaf8304479..9e42410c7be4 100644 --- a/cosmovisor/scanner_test.go +++ b/cosmovisor/scanner_test.go @@ -1,62 +1,63 @@ -package cosmovisor +package cosmovisor_test import ( - "path/filepath" + "bufio" + "io" "testing" + "github.com/cosmos/cosmos-sdk/cosmovisor" + "github.com/stretchr/testify/require" ) -func TestParseUpgradeInfoFile(t *testing.T) { - cases := []struct { - filename string - expectUpgrade UpgradeInfo +func TestWaitForInfo(t *testing.T) { + cases := map[string]struct { + write []string + expectUpgrade *cosmovisor.UpgradeInfo expectErr bool - }{{ - filename: "f1-good.json", - expectUpgrade: UpgradeInfo{Name: "upgrade1", Info: "some info", Height: 123}, - expectErr: false, - }, { - filename: "f2-bad-type.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "f2-bad-type-2.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "f3-empty.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "f4-empty-obj.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "f5-partial-obj-1.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "f5-partial-obj-2.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }, { - filename: "unknown.json", - expectUpgrade: UpgradeInfo{}, - expectErr: true, - }} + }{ + "no match": { + write: []string{"some", "random\ninfo\n"}, + }, + "match name with no info": { + write: []string{"first line\n", `UPGRADE "myname" NEEDED at height: 123: `, "\nnext line\n"}, + expectUpgrade: &cosmovisor.UpgradeInfo{ + Name: "myname", + Info: "", + }, + }, + "match name with info": { + write: []string{"first line\n", `UPGRADE "take2" NEEDED at height: 123: DownloadData here!`, "\nnext line\n"}, + expectUpgrade: &cosmovisor.UpgradeInfo{ + Name: "take2", + Info: "DownloadData", + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + r, w := io.Pipe() + scan := bufio.NewScanner(r) + + // write all info in separate routine + go func() { + for _, line := range tc.write { + n, err := w.Write([]byte(line)) + require.NoError(t, err) + require.Equal(t, len(line), n) + } + w.Close() + }() - for i := range cases { - tc := cases[i] - t.Run(tc.filename, func(t *testing.T) { - require := require.New(t) - ui, err := parseUpgradeInfoFile(filepath.Join(".", "testdata", "upgrade-files", tc.filename)) + // now scan the info + info, err := cosmovisor.WaitForUpdate(scan) if tc.expectErr { - require.Error(err) - } else { - require.NoError(err) - require.Equal(tc.expectUpgrade, ui) + require.Error(t, err) + return } + require.NoError(t, err) + require.Equal(t, tc.expectUpgrade, info) }) } } diff --git a/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod b/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod index 816b3c3b634f..113cce7f797f 100755 --- a/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod +++ b/cosmovisor/testdata/download/cosmovisor/genesis/bin/autod @@ -1,12 +1,7 @@ #!/bin/sh -echo Genesis autod. Args: $@ -sleep 0.1 -echo 'ERROR: UPGRADE "chain2" NEEDED at height: 49: zip_binary' - -# create upgrade info -# this info contains directly information about binaries (in chain2->chain3 update we test with info containing a link to the file with an address for the new chain binary) -echo '{"name":"chain2","height":49,"info":"{\"binaries\":{\"linux/amd64\":\"https://github.com/cosmos/cosmos-sdk/raw/robert/cosmvisor-file-watch/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:960cad22d1684e4baf3e8781334c28e16d0e329497762aaa3b5c11e2184086bb\"}}"}' > $3 - -sleep 0.1 +echo Preparing auto-download $@ +sleep 1 +echo 'ERROR: UPGRADE "chain2" NEEDED at height: 49: {"binaries":{"linux/amd64":"https://github.com/cosmos/cosmos-sdk/raw/51249cb93130810033408934454841c98423ed4b/cosmovisor/testdata/repo/zip_binary/autod.zip?checksum=sha256:dc48829b4126ae95bc0db316c66d4e9da5f3db95e212665b6080638cca77e998"}} module=main' +sleep 4 echo Never should be printed!!! diff --git a/cosmovisor/testdata/download/data/.gitkeep b/cosmovisor/testdata/download/data/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/cosmovisor/testdata/repo/chain2-zip_bin/autod b/cosmovisor/testdata/repo/chain2-zip_bin/autod deleted file mode 100755 index eefc6c495d3b..000000000000 --- a/cosmovisor/testdata/repo/chain2-zip_bin/autod +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh - -echo Chain 2 from zipped binary -echo Args: $@ -# note that we just have a url (follow the ref), not a full link -echo 'ERROR: UPGRADE "chain3" NEEDED at height: 936: ref_to_chain3-zip_dir.json module=main' - -# this update info doesn't contain binaries, instead it is a reference for further download instructions. -# echo '{"name":"chain3","height":936,"info":"{\"binaries\":{\"linux/amd64\":\"https://github.com/cosmos/cosmos-sdk/raw/robert/cosmvisor-file-watch/cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json\"}}"}' > $3 -echo '{"name":"chain3","height":936,"info":"https://github.com/cosmos/cosmos-sdk/raw/robert/cosmvisor-file-watch/cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json"}' > $3 - -sleep 1 -echo 'Do not print' diff --git a/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip b/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip deleted file mode 100644 index 4364f996c8693f71aa84113f2d3bb2b42c1bc9e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 547 zcmWIWW@Zs#U|`^2D9C*mbgV7ndov>g!viJ;237_chQ!j6{FKlTP6p=3XWu1!a(bIk zTEWf0$nt`jfdNb$o#dE($3UR<{XfxQ)dwtP;Y*~M3{_N8eDiZ7U*slUy!CjhP-OCw z!aHVf>fcw)eR9p&&yw?O^{k)w7EIQ!dfwz`c6!4m7XE8H7~&UpUO5zhy)>d(!sn0i z%kPErxh(dc_EQj0-eAhZd?AaQx%`Z3vXMet#A-?wIUiSBgE??2p zzy4bOHB;T+Kil@jPhGI8@L$lu@0((#YEO73cK2QpftF9pLmtSWr|K;g!{KCV!r)G`aZkNJ)7X6X06J_bjJOM(@rXv6@2vCyXbNF>qXpi z(*H9Acr!A|G2@Cu31HkZFaV>J;cX*`1&vWwNQ|PzW`H*<8%Pl&5PAdYLm+(&03BiG Awg3PC diff --git a/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip b/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip deleted file mode 100644 index dffb1aba466836f40362cf3f95b6e6eb5e857563..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 362 zcmWIWW@h1H0D;`}cR^qVlwe_yVMxl%(+>^dWMJ++{4U{~Tt-4^1vdjD%L`@(1~3r- z)GY#3&jHpQCN$xb6_95G#GFvwiKQj^Dfo;C4!h!i>da;Ra}OSQ`JOuwUU1Ii%$Z<= zFc0lh=R;SV($Vnn4fHsvTrz`)9&z>rv4lAjV9!pp#3Fh?sMhD$5B85mi<0@VPu zGBD^)@b*7!Akgyur>J*#$OH!Uv(`bZqP;s9R~?beeN!s?>d3^mTbknif9-f9m-g)P zyEFL_m%p7^si3fqXOC7+bwtyaWzGA`Bs>nJIQ-_`_4fM%-iS#ly05$wa?95>-JYJn zxA!AU+`@L{gXdq^XxIoS^n@LrYZ5j4@si7(1^){+`OlKsYPVQ_hwI+EbvfEUe%0Jw zXKs4t$IAs3Z0}3=t~56|D7w}9skX-BHB3+cKF~T=wl+NZ^AX?UI|}C(UjO>y$h=6K zO%o<4`5e}XGILe#Ht2iob0l-dGS`_W-8hbTa1@@DjY`+~{A7;I>j(Y-Z+4D9caO|>28Jmp>;t?RnM9Zo j@q#P|iWeAo+X!M26-xo$tZX2~j6fI+q^AL`VPF6N5PZVr literal 0 HcmV?d00001 diff --git a/cosmovisor/testdata/repo/zip_directory/autod.zip b/cosmovisor/testdata/repo/zip_directory/autod.zip new file mode 100644 index 0000000000000000000000000000000000000000..225cd4672a782d26c9c32e56c9ba3492b901fb85 GIT binary patch literal 362 zcmWIWW@h1H0D<&obAK=cO0Y1>4%1JGB7({>xgv%;?fFk21b?_%nS@*A^@mc z1gM^aL7-B{f6IG`dMhB$28cPKx)V!F@>9S@_&Zm{x?>s<9CpS3)S1ir=N>%r@;!GV zyx^S2nKQu#K_1$t&WElzrK92D8|ZOT$IDmmr2eWFjY1tS{cS-3g%6k*0=yZS $4 sleep 2 echo Never should be printed!!! diff --git a/cosmovisor/testdata/validate/data/.gitkeep b/cosmovisor/testdata/validate/data/.gitkeep deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/cosmovisor/upgrade.go b/cosmovisor/upgrade.go index 2495d3096909..beba31c6536f 100644 --- a/cosmovisor/upgrade.go +++ b/cosmovisor/upgrade.go @@ -17,12 +17,12 @@ import ( // DoUpgrade will be called after the log message has been parsed and the process has terminated. // We can now make any changes to the underlying directory without interference and leave it // in a state, so we can make a proper restart -func DoUpgrade(cfg *Config, info UpgradeInfo) error { +func DoUpgrade(cfg *Config, info *UpgradeInfo) error { // Simplest case is to switch the link err := EnsureBinary(cfg.UpgradeBin(info.Name)) if err == nil { // we have the binary - do it - return cfg.SetCurrentUpgrade(info) + return cfg.SetCurrentUpgrade(info.Name) } // if auto-download is disabled, we fail if !cfg.AllowDownloadBinaries { @@ -36,7 +36,7 @@ func DoUpgrade(cfg *Config, info UpgradeInfo) error { // If not there, then we try to download it... maybe if err := DownloadBinary(cfg, info); err != nil { - return fmt.Errorf("cannot download binary. %w", err) + return fmt.Errorf("cannot download binary: %w", err) } // and then set the binary again @@ -44,11 +44,11 @@ func DoUpgrade(cfg *Config, info UpgradeInfo) error { return fmt.Errorf("downloaded binary doesn't check out: %w", err) } - return cfg.SetCurrentUpgrade(info) + return cfg.SetCurrentUpgrade(info.Name) } // DownloadBinary will grab the binary and place it in the proper directory -func DownloadBinary(cfg *Config, info UpgradeInfo) error { +func DownloadBinary(cfg *Config, info *UpgradeInfo) error { url, err := GetDownloadURL(info) if err != nil { return err @@ -101,7 +101,7 @@ type UpgradeConfig struct { } // GetDownloadURL will check if there is an arch-dependent binary specified in Info -func GetDownloadURL(info UpgradeInfo) (string, error) { +func GetDownloadURL(info *UpgradeInfo) (string, error) { doc := strings.TrimSpace(info.Info) // if this is a url, then we download that and try to get a new doc with the real info if _, err := url.Parse(doc); err == nil { @@ -146,6 +146,33 @@ func OSArch() string { return fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) } +// SetCurrentUpgrade sets the named upgrade to be the current link, returns error if this binary doesn't exist +func (cfg *Config) SetCurrentUpgrade(upgradeName string) error { + // ensure named upgrade exists + bin := cfg.UpgradeBin(upgradeName) + + if err := EnsureBinary(bin); err != nil { + return err + } + + // set a symbolic link + link := filepath.Join(cfg.Root(), currentLink) + safeName := url.PathEscape(upgradeName) + upgrade := filepath.Join(cfg.Root(), upgradesDir, safeName) + + // remove link if it exists + if _, err := os.Stat(link); err == nil { + os.Remove(link) + } + + // point to the new directory + if err := os.Symlink(upgrade, link); err != nil { + return fmt.Errorf("creating current symlink: %w", err) + } + + return nil +} + // EnsureBinary ensures the file exists and is executable, or returns an error func EnsureBinary(path string) error { info, err := os.Stat(path) diff --git a/cosmovisor/upgrade_test.go b/cosmovisor/upgrade_test.go index d8f881194275..371db1ef3c93 100644 --- a/cosmovisor/upgrade_test.go +++ b/cosmovisor/upgrade_test.go @@ -37,7 +37,7 @@ func (s *upgradeTestSuite) TestCurrentBin() { // ensure we cannot set this to an invalid value for _, name := range []string{"missing", "nobin", "noexec"} { - s.Require().Error(cfg.SetCurrentUpgrade(cosmovisor.UpgradeInfo{Name: name}), name) + s.Require().Error(cfg.SetCurrentUpgrade(name), name) currentBin, err := cfg.CurrentBin() s.Require().NoError(err) @@ -46,15 +46,15 @@ func (s *upgradeTestSuite) TestCurrentBin() { } // try a few times to make sure this can be reproduced - for _, name := range []string{"chain2", "chain3", "chain2"} { + for _, upgrade := range []string{"chain2", "chain3", "chain2"} { // now set it to a valid upgrade and make sure CurrentBin is now set properly - err = cfg.SetCurrentUpgrade(cosmovisor.UpgradeInfo{Name: name}) + err = cfg.SetCurrentUpgrade(upgrade) s.Require().NoError(err) // we should see current point to the new upgrade dir currentBin, err := cfg.CurrentBin() s.Require().NoError(err) - s.Require().Equal(cfg.UpgradeBin(name), currentBin) + s.Require().Equal(cfg.UpgradeBin(upgrade), currentBin) } } @@ -67,7 +67,7 @@ func (s *upgradeTestSuite) TestCurrentAlwaysSymlinkToDirectory() { s.Require().Equal(cfg.GenesisBin(), currentBin) s.assertCurrentLink(cfg, "genesis") - err = cfg.SetCurrentUpgrade(cosmovisor.UpgradeInfo{Name: "chain2"}) + err = cfg.SetCurrentUpgrade("chain2") s.Require().NoError(err) currentBin, err = cfg.CurrentBin() s.Require().NoError(err) @@ -100,7 +100,7 @@ func (s *upgradeTestSuite) TestDoUpgradeNoDownloadUrl() { // do upgrade ignores bad files for _, name := range []string{"missing", "nobin", "noexec"} { - info := cosmovisor.UpgradeInfo{Name: name} + info := &cosmovisor.UpgradeInfo{Name: name} err = cosmovisor.DoUpgrade(cfg, info) s.Require().Error(err, name) currentBin, err := cfg.CurrentBin() @@ -111,7 +111,7 @@ func (s *upgradeTestSuite) TestDoUpgradeNoDownloadUrl() { // make sure it updates a few times for _, upgrade := range []string{"chain2", "chain3"} { // now set it to a valid upgrade and make sure CurrentBin is now set properly - info := cosmovisor.UpgradeInfo{Name: upgrade} + info := &cosmovisor.UpgradeInfo{Name: upgrade} err = cosmovisor.DoUpgrade(cfg, info) s.Require().NoError(err) // we should see current point to the new upgrade dir @@ -130,30 +130,30 @@ func (s *upgradeTestSuite) TestOsArch() { func (s *upgradeTestSuite) TestGetDownloadURL() { // all download tests will fail if we are not on linux... - ref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/ref_to_chain3-zip_dir.json")) + ref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/ref_zipped")) s.Require().NoError(err) - badref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/chain2-zip_bin/autod.zip")) // "./testdata/repo/zip_binary/autod.zip")) + badref, err := filepath.Abs(filepath.FromSlash("./testdata/repo/zip_binary/autod.zip")) s.Require().NoError(err) cases := map[string]struct { - info string - url string - err string + info string + url string + isErr bool }{ "missing": { - err: "downloading reference link : invalid source string:", + isErr: true, }, "follow reference": { info: ref, - url: "https://github.com/cosmos/cosmos-sdk/raw/robert/cosmvisor-file-watch/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4", + url: "https://github.com/cosmos/cosmos-sdk/raw/aa5d6140ad4011bb33d472dca8246a0dcbe223ee/cosmovisor/testdata/repo/zip_directory/autod.zip?checksum=sha256:3784e4574cad69b67e34d4ea4425eff140063a3870270a301d6bb24a098a27ae", }, "malformated reference target": { - info: badref, - err: "upgrade info doesn't contain binary map", + info: badref, + isErr: true, }, "missing link": { - info: "https://no.such.domain/exists.txt", - err: "dial tcp: lookup no.such.domain: no such host", + info: "https://no.such.domain/exists.txt", + isErr: true, }, "proper binary": { info: `{"binaries": {"linux/amd64": "https://foo.bar/", "windows/amd64": "https://something.else"}}`, @@ -168,22 +168,19 @@ func (s *upgradeTestSuite) TestGetDownloadURL() { url: "https://foo.bar/portable", }, "missing binary": { - info: `{"binaries": {"linux/arm": "https://foo.bar/"}}`, - err: "cannot find binary for", + info: `{"binaries": {"linux/arm": "https://foo.bar/"}}`, + isErr: true, }, } - for name, tc := range cases { - s.Run(name, func() { - url, err := cosmovisor.GetDownloadURL(cosmovisor.UpgradeInfo{Info: tc.info}) - if tc.err != "" { - s.Require().Error(err) - s.Require().Contains(err.Error(), tc.err) - } else { - s.Require().NoError(err) - s.Require().Equal(tc.url, url) - } - }) + for _, tc := range cases { + url, err := cosmovisor.GetDownloadURL(&cosmovisor.UpgradeInfo{Info: tc.info}) + if tc.isErr { + s.Require().Error(err) + } else { + s.Require().NoError(err) + s.Require().Equal(tc.url, url) + } } } @@ -248,7 +245,7 @@ func (s *upgradeTestSuite) TestDownloadBinary() { } upgrade := "amazonas" - info := cosmovisor.UpgradeInfo{ + info := &cosmovisor.UpgradeInfo{ Name: upgrade, Info: fmt.Sprintf(`{"binaries":{"%s": "%s"}}`, cosmovisor.OSArch(), url), } @@ -274,7 +271,9 @@ func (s *upgradeTestSuite) TestDownloadBinary() { // returns the directory (which can now be used as Config.Home) and modified safely func copyTestData(t *testing.T, subdir string) string { t.Helper() + tmpdir := t.TempDir() + require.NoError(t, copy.Copy(filepath.Join("testdata", subdir), tmpdir)) return tmpdir diff --git a/simapp/simd/cmd/root.go b/simapp/simd/cmd/root.go index 347c5d73308d..9f8660a27f54 100644 --- a/simapp/simd/cmd/root.go +++ b/simapp/simd/cmd/root.go @@ -6,14 +6,13 @@ import ( "os" "path/filepath" + serverconfig "github.com/cosmos/cosmos-sdk/server/config" "github.com/spf13/cast" "github.com/spf13/cobra" tmcli "github.com/tendermint/tendermint/libs/cli" "github.com/tendermint/tendermint/libs/log" dbm "github.com/tendermint/tm-db" - serverconfig "github.com/cosmos/cosmos-sdk/server/config" - "github.com/cosmos/cosmos-sdk/baseapp" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" diff --git a/simapp/simd/cmd/testnet_test.go b/simapp/simd/cmd/testnet_test.go index da46913eae03..da58fd454d82 100644 --- a/simapp/simd/cmd/testnet_test.go +++ b/simapp/simd/cmd/testnet_test.go @@ -5,10 +5,6 @@ import ( "fmt" "testing" - "github.com/spf13/viper" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/log" - "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/server" @@ -16,6 +12,9 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" genutiltest "github.com/cosmos/cosmos-sdk/x/genutil/client/testutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" + "github.com/spf13/viper" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/libs/log" ) func Test_TestnetCmd(t *testing.T) { diff --git a/store/cachemulti/store_test.go b/store/cachemulti/store_test.go index ef5bd5eaebb3..8747df9ef966 100644 --- a/store/cachemulti/store_test.go +++ b/store/cachemulti/store_test.go @@ -4,9 +4,8 @@ import ( "fmt" "testing" - "github.com/stretchr/testify/require" - "github.com/cosmos/cosmos-sdk/store/types" + "github.com/stretchr/testify/require" ) func TestStoreGetKVStore(t *testing.T) { diff --git a/store/types/gas.go b/store/types/gas.go index 20ec518e3f18..fe72cc520701 100644 --- a/store/types/gas.go +++ b/store/types/gas.go @@ -130,7 +130,7 @@ type infiniteGasMeter struct { consumed Gas } -// NewInfiniteGasMeter returns a new gas meter without a limit. +// NewInfiniteGasMeter returns a reference to a new infiniteGasMeter. func NewInfiniteGasMeter() GasMeter { return &infiniteGasMeter{ consumed: 0, diff --git a/types/handler_test.go b/types/handler_test.go index c7fc961a5607..449d1b602f5c 100644 --- a/types/handler_test.go +++ b/types/handler_test.go @@ -36,7 +36,7 @@ func (s *handlerTestSuite) TestChainAnteDecorators() { mockAnteDecorator2 := mocks.NewMockAnteDecorator(mockCtrl) // NOTE: we can't check that mockAnteDecorator2 is passed as the last argument because // ChainAnteDecorators wraps the decorators into closures, so each decorator is - // receiving a closure. + // receving a closure. mockAnteDecorator1.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1) mockAnteDecorator2.EXPECT().AnteHandle(gomock.Eq(ctx), gomock.Eq(tx), true, gomock.Any()).Times(1) diff --git a/x/distribution/types/proposal.go b/x/distribution/types/proposal.go index 0256f5764dee..2eed72a2e46e 100644 --- a/x/distribution/types/proposal.go +++ b/x/distribution/types/proposal.go @@ -18,6 +18,7 @@ var _ govtypes.Content = &CommunityPoolSpendProposal{} func init() { govtypes.RegisterProposalType(ProposalTypeCommunityPoolSpend) + govtypes.RegisterProposalTypeCodec(&CommunityPoolSpendProposal{}, "cosmos-sdk/CommunityPoolSpendProposal") } // NewCommunityPoolSpendProposal creates a new community pool spned proposal. diff --git a/x/gov/types/codec.go b/x/gov/types/codec.go index 1c357caa3304..19fcddae9459 100644 --- a/x/gov/types/codec.go +++ b/x/gov/types/codec.go @@ -36,6 +36,16 @@ func RegisterInterfaces(registry types.InterfaceRegistry) { msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) } +// RegisterProposalTypeCodec registers an external proposal content type defined +// in another module for the internal ModuleCdc. This allows the MsgSubmitProposal +// to be correctly Amino encoded and decoded. +// +// NOTE: This should only be used for applications that are still using a concrete +// Amino codec for serialization. +func RegisterProposalTypeCodec(o interface{}, name string) { + amino.RegisterConcrete(o, name, nil) +} + var ( amino = codec.NewLegacyAmino() ModuleCdc = codec.NewAminoCodec(amino) diff --git a/x/params/types/proposal/proposal.go b/x/params/types/proposal/proposal.go index 4db9b7225c84..3a2f97a77247 100644 --- a/x/params/types/proposal/proposal.go +++ b/x/params/types/proposal/proposal.go @@ -19,6 +19,7 @@ var _ govtypes.Content = &ParameterChangeProposal{} func init() { govtypes.RegisterProposalType(ProposalTypeChange) + govtypes.RegisterProposalTypeCodec(&ParameterChangeProposal{}, "cosmos-sdk/ParameterChangeProposal") } func NewParameterChangeProposal(title, description string, changes []ParamChange) *ParameterChangeProposal { diff --git a/x/upgrade/types/proposal.go b/x/upgrade/types/proposal.go index b30bd9f830a6..6fcaa1df883e 100644 --- a/x/upgrade/types/proposal.go +++ b/x/upgrade/types/proposal.go @@ -20,7 +20,9 @@ var _ gov.Content = &SoftwareUpgradeProposal{} func init() { gov.RegisterProposalType(ProposalTypeSoftwareUpgrade) + gov.RegisterProposalTypeCodec(&SoftwareUpgradeProposal{}, "cosmos-sdk/SoftwareUpgradeProposal") gov.RegisterProposalType(ProposalTypeCancelSoftwareUpgrade) + gov.RegisterProposalTypeCodec(&CancelSoftwareUpgradeProposal{}, "cosmos-sdk/CancelSoftwareUpgradeProposal") } func (sup *SoftwareUpgradeProposal) GetTitle() string { return sup.Title }