chore: set tools to main (#23687)
This commit is contained in:
parent
224e68e1e5
commit
98f2048979
@ -31,6 +31,11 @@ require (
|
||||
pgregory.net/rapid v1.1.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/tendermint/go-amino v0.16.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.5.2 // indirect
|
||||
@ -66,7 +71,6 @@ require (
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/iavl v1.2.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
@ -169,7 +173,6 @@ require (
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
|
||||
@ -31,9 +31,17 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [v0.2.0-rc.2](https://github.com/cosmos/cosmos-sdk/releases/tag/tools/confix/v0.2.0-rc.2) - 2025-01-16
|
||||
|
||||
* [#23238](https://github.com/cosmos/cosmos-sdk/pull/23238) Loosen `app.toml` validation for consistency between baseapp chains and v2 chains.
|
||||
|
||||
## [v0.2.0-rc.1](https://github.com/cosmos/cosmos-sdk/releases/tag/tools/confix/v0.2.0-rc.1) - 2024-12-18
|
||||
|
||||
* [#21052](https://github.com/cosmos/cosmos-sdk/pull/21052) Add a migration to v2 config.
|
||||
|
||||
## [v0.1.2](https://github.com/cosmos/cosmos-sdk/releases/tag/tools/confix/v0.1.2) - 2024-08-13
|
||||
|
||||
* (confix) [#21202](https://github.com/cosmos/cosmos-sdk/pull/21202) Allow customization of migration `PlanBuilder`.
|
||||
* [#21202](https://github.com/cosmos/cosmos-sdk/pull/21202) Allow customization of migration `PlanBuilder`.
|
||||
|
||||
## [v0.1.1](https://github.com/cosmos/cosmos-sdk/releases/tag/tools/confix/v0.1.1) - 2023-12-11
|
||||
|
||||
|
||||
@ -4,9 +4,11 @@ all: confix condiff test
|
||||
|
||||
confix:
|
||||
go build -mod=readonly ./cmd/confix
|
||||
@echo "confix binary has been successfully built in tools/confix/confix"
|
||||
|
||||
condiff:
|
||||
go build -mod=readonly ./cmd/condiff
|
||||
@echo "condiff binary has been successfully built in tools/confix/condiff"
|
||||
|
||||
test:
|
||||
go test -mod=readonly -race ./...
|
||||
|
||||
@ -23,7 +23,7 @@ import "cosmossdk.io/tools/confix/cmd"
|
||||
Find the following line:
|
||||
|
||||
```go
|
||||
initRootCmd(rootCmd, encodingConfig)
|
||||
initRootCmd(rootCmd, moduleManager)
|
||||
```
|
||||
|
||||
After that line, add the following:
|
||||
@ -39,6 +39,11 @@ An implementation example can be found in `simapp`.
|
||||
|
||||
The command will be available as `simd config`.
|
||||
|
||||
:::tip
|
||||
Using confix directly in the application can have less features than using it standalone.
|
||||
This is because confix is versioned with the SDK, while `latest` is the standalone version.
|
||||
:::
|
||||
|
||||
### Using Confix Standalone
|
||||
|
||||
To use Confix standalone, without having to add it in your application, install it with the following command:
|
||||
@ -93,14 +98,16 @@ confix set ~/.simapp/config/client.toml chain-id "foo-1" # sets the value chain-
|
||||
|
||||
### Migrate
|
||||
|
||||
Migrate a configuration file to a new version, e.g.:
|
||||
Migrate a configuration file to a new version, config type defaults to `app.toml`, if you want to change it to `client.toml`, please indicate it by adding the optional parameter, e.g.:
|
||||
|
||||
```shell
|
||||
simd config migrate v0.47 # migrates defaultHome/config/app.toml to the latest v0.47 config
|
||||
simd config migrate v0.50 # migrates defaultHome/config/app.toml to the latest v0.50 config
|
||||
simd config migrate v0.50 --client # migrates defaultHome/config/client.toml to the latest v0.50 config
|
||||
```
|
||||
|
||||
```shell
|
||||
confix migrate v0.47 ~/.simapp/config/app.toml # migrate ~/.simapp/config/app.toml to the latest v0.47 config
|
||||
confix migrate v0.50 ~/.simapp/config/app.toml # migrate ~/.simapp/config/app.toml to the latest v0.50 config
|
||||
confix migrate v0.50 ~/.simapp/config/client.toml --client # migrate ~/.simapp/config/client.toml to the latest v0.50 config
|
||||
```
|
||||
|
||||
### Diff
|
||||
@ -109,10 +116,12 @@ Get the diff between a given configuration file and the default configuration fi
|
||||
|
||||
```shell
|
||||
simd config diff v0.47 # gets the diff between defaultHome/config/app.toml and the latest v0.47 config
|
||||
simd config diff v0.47 --client # gets the diff between defaultHome/config/client.toml and the latest v0.47 config
|
||||
```
|
||||
|
||||
```shell
|
||||
confix diff v0.47 ~/.simapp/config/app.toml # gets the diff between ~/.simapp/config/app.toml and the latest v0.47 config
|
||||
confix diff v0.47 ~/.simapp/config/client.toml --client # gets the diff between ~/.simapp/config/client.toml and the latest v0.47 config
|
||||
```
|
||||
|
||||
### View
|
||||
@ -129,9 +138,19 @@ confix view ~/.simapp/config/client.toml # views the current app client conf
|
||||
|
||||
### Maintainer
|
||||
|
||||
At each SDK modification of the default configuration, add the default SDK config under `data/v0.XX-app.toml`.
|
||||
At each SDK modification of the default configuration, add the default SDK config under `data/vXX-app.toml`.
|
||||
This allows users to use the tool standalone.
|
||||
|
||||
### Compatibility
|
||||
|
||||
The recommended standalone version is `latest`, which is using the latest development version of the Confix.
|
||||
|
||||
| SDK Version | Confix Version |
|
||||
| ----------- | -------------- |
|
||||
| v0.50 | v0.1.x |
|
||||
| v0.52 | v0.2.x |
|
||||
| v2 | v0.2.x |
|
||||
|
||||
## Credits
|
||||
|
||||
This project is based on the [CometBFT RFC 019](https://github.com/cometbft/cometbft/blob/5013bc3f4a6d64dcc2bf02ccc002ebc9881c62e4/docs/rfc/rfc-019-config-version.md) and their own implementation of [confix](https://github.com/cometbft/cometbft/blob/v0.36.x/scripts/confix/confix.go).
|
||||
This project is based on the [CometBFT RFC 019](https://github.com/cometbft/cometbft/blob/5013bc3f4a6d64dcc2bf02ccc002ebc9881c62e4/docs/rfc/rfc-019-config-version.md) and their never released own implementation of [confix](https://github.com/cometbft/cometbft/blob/v0.36.x/scripts/confix/confix.go).
|
||||
|
||||
@ -4,6 +4,10 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const (
|
||||
tomlSuffix = ".toml"
|
||||
)
|
||||
|
||||
// ConfigCommand contains all the confix commands
|
||||
// These command can be used to interactively update an application config value.
|
||||
func ConfigCommand() *cobra.Command {
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -12,22 +15,31 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
// DiffCommand creates a new command for comparing configuration files
|
||||
func DiffCommand() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "diff [target-version] <app-toml-path>",
|
||||
Short: "Outputs all config values that are different from the app.toml defaults.",
|
||||
cmd := &cobra.Command{
|
||||
Use: "diff [target-version] <config-path>",
|
||||
Short: "Outputs all config values that are different from the default.",
|
||||
Long: "This command compares the specified configuration file (app.toml or client.toml) with the defaults and outputs any differences.",
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var filename string
|
||||
var configPath string
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
switch {
|
||||
|
||||
case len(args) > 1:
|
||||
filename = args[1]
|
||||
configPath = args[1]
|
||||
case clientCtx.HomeDir != "":
|
||||
filename = fmt.Sprintf("%s/config/app.toml", clientCtx.HomeDir)
|
||||
configPath = filepath.Join(clientCtx.HomeDir, "config", "app.toml")
|
||||
default:
|
||||
return fmt.Errorf("must provide a path to the app.toml file")
|
||||
return errors.New("must provide a path to the app.toml or client.toml")
|
||||
}
|
||||
|
||||
configType := confix.AppConfigType
|
||||
if ok, _ := cmd.Flags().GetBool(confix.ClientConfigType); ok {
|
||||
configPath = strings.ReplaceAll(configPath, "app.toml", "client.toml") // for the case we are using the home dir of client ctx
|
||||
configType = confix.ClientConfigType
|
||||
} else if strings.HasSuffix(configPath, "client.toml") {
|
||||
return errors.New("app.toml file expected, got client.toml, use --client flag to diff client.toml")
|
||||
}
|
||||
|
||||
targetVersion := args[0]
|
||||
@ -35,14 +47,14 @@ func DiffCommand() *cobra.Command {
|
||||
return fmt.Errorf("unknown version %q, supported versions are: %q", targetVersion, slices.Collect(maps.Keys(confix.Migrations)))
|
||||
}
|
||||
|
||||
targetVersionFile, err := confix.LoadLocalConfig(targetVersion)
|
||||
targetVersionFile, err := confix.LoadLocalConfig(targetVersion, configType)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to load internal config: %w", err))
|
||||
return fmt.Errorf("failed to load internal config: %w", err)
|
||||
}
|
||||
|
||||
rawFile, err := confix.LoadConfig(filename)
|
||||
rawFile, err := confix.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %v", err)
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
diff := confix.DiffValues(rawFile, targetVersionFile)
|
||||
@ -58,4 +70,8 @@ func DiffCommand() *cobra.Command {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool(confix.ClientConfigType, false, "diff client.toml instead of app.toml")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -2,9 +2,12 @@ package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@ -21,22 +24,38 @@ var (
|
||||
|
||||
func MigrateCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "migrate [target-version] <app-toml-path> (options)",
|
||||
Short: "Migrate Cosmos SDK app configuration file to the specified version",
|
||||
Long: `Migrate the contents of the Cosmos SDK app configuration (app.toml) to the specified version.
|
||||
Use: "migrate [target-version] <config-path>",
|
||||
Short: "Migrate Cosmos SDK configuration file to the specified version",
|
||||
Long: `Migrate the contents of the Cosmos SDK configuration (app.toml or client.toml) to the specified version. Configuration type is app by default.
|
||||
The output is written in-place unless --stdout is provided.
|
||||
In case of any error in updating the file, no output is written.`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
var filename string
|
||||
var configPath string
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
|
||||
configType := confix.AppConfigType
|
||||
isClient, _ := cmd.Flags().GetBool(confix.ClientConfigType)
|
||||
|
||||
if isClient {
|
||||
configType = confix.ClientConfigType
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(args) > 1:
|
||||
filename = args[1]
|
||||
configPath = args[1]
|
||||
case clientCtx.HomeDir != "":
|
||||
filename = fmt.Sprintf("%s/config/app.toml", clientCtx.HomeDir)
|
||||
suffix := "app.toml"
|
||||
if isClient {
|
||||
suffix = "client.toml"
|
||||
}
|
||||
configPath = filepath.Join(clientCtx.HomeDir, "config", suffix)
|
||||
default:
|
||||
return fmt.Errorf("must provide a path to the app.toml file")
|
||||
return errors.New("must provide a path to the app.toml or client.toml")
|
||||
}
|
||||
|
||||
if strings.HasSuffix(configPath, "client.toml") && !isClient {
|
||||
return errors.New("app.toml file expected, got client.toml, use --client flag to migrate client.toml")
|
||||
}
|
||||
|
||||
targetVersion := args[0]
|
||||
@ -45,9 +64,9 @@ In case of any error in updating the file, no output is written.`,
|
||||
return fmt.Errorf("unknown version %q, supported versions are: %q", targetVersion, slices.Collect(maps.Keys(confix.Migrations)))
|
||||
}
|
||||
|
||||
rawFile, err := confix.LoadConfig(filename)
|
||||
rawFile, err := confix.LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load config: %v", err)
|
||||
return fmt.Errorf("failed to load config: %w", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
@ -55,12 +74,15 @@ In case of any error in updating the file, no output is written.`,
|
||||
ctx = confix.WithLogWriter(ctx, cmd.ErrOrStderr())
|
||||
}
|
||||
|
||||
outputPath := filename
|
||||
outputPath := configPath
|
||||
if FlagStdOut {
|
||||
outputPath = ""
|
||||
}
|
||||
|
||||
if err := confix.Upgrade(ctx, plan(rawFile, targetVersion), filename, outputPath, FlagSkipValidate); err != nil {
|
||||
// get transformation steps and formatDoc in which plan need to be applied
|
||||
steps, formatDoc := plan(rawFile, targetVersion, configType)
|
||||
|
||||
if err := confix.Upgrade(ctx, steps, formatDoc, configPath, outputPath, FlagSkipValidate); err != nil {
|
||||
return fmt.Errorf("failed to migrate config: %w", err)
|
||||
}
|
||||
|
||||
@ -71,6 +93,7 @@ In case of any error in updating the file, no output is written.`,
|
||||
cmd.Flags().BoolVar(&FlagStdOut, "stdout", false, "print the updated config to stdout")
|
||||
cmd.Flags().BoolVar(&FlagVerbose, "verbose", false, "log changes to stderr")
|
||||
cmd.Flags().BoolVar(&FlagSkipValidate, "skip-validate", false, "skip configuration validation (allows to migrate unknown configurations)")
|
||||
cmd.Flags().Bool(confix.ClientConfigType, false, "migrate client.toml instead of app.toml")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package cmd_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -9,30 +9,31 @@ import (
|
||||
|
||||
"cosmossdk.io/tools/confix/cmd"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
|
||||
)
|
||||
|
||||
func TestMigradeCmd(t *testing.T) {
|
||||
func TestMigrateCmd(t *testing.T) {
|
||||
clientCtx, cleanup := initClientContext(t)
|
||||
defer cleanup()
|
||||
|
||||
_, err := clitestutil.ExecTestCLICmd(client.Context{}, cmd.MigrateCommand(), []string{"v0.0"})
|
||||
assert.ErrorContains(t, err, "must provide a path to the app.toml file")
|
||||
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.0"})
|
||||
_, err := clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.0"})
|
||||
assert.ErrorContains(t, err, "unknown version")
|
||||
|
||||
// clientCtx does not create app.toml, so this should fail
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.45"})
|
||||
assert.ErrorContains(t, err, "no such file or directory")
|
||||
|
||||
// try to migrate from client.toml it should fail without --skip-validate
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.46", fmt.Sprintf("%s/config/client.toml", clientCtx.HomeDir)})
|
||||
// try to migrate from unsupported.toml it should fail without --skip-validate
|
||||
_, err = clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.46", filepath.Join(clientCtx.HomeDir, "config", "unsupported.toml")})
|
||||
assert.ErrorContains(t, err, "failed to migrate config")
|
||||
|
||||
// try to migrate from client.toml - it should work and give us a big diff
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.46", fmt.Sprintf("%s/config/client.toml", clientCtx.HomeDir), "--skip-validate", "--verbose"})
|
||||
// try to migrate from unsupported.toml - it should work and give us a big diff
|
||||
out, err := clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.46", filepath.Join(clientCtx.HomeDir, "config", "unsupported.toml"), "--skip-validate", "--verbose"})
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, strings.Contains(out.String(), "add app-db-backend key"))
|
||||
|
||||
// this should work
|
||||
out, err = clitestutil.ExecTestCLICmd(clientCtx, cmd.MigrateCommand(), []string{"v0.52", filepath.Join(clientCtx.HomeDir, "config", "client.toml"), "--client", "--verbose"})
|
||||
assert.NilError(t, err)
|
||||
assert.Assert(t, strings.Contains(out.String(), "add keyring-default-keyname key"))
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/creachadair/tomledit"
|
||||
@ -19,7 +20,7 @@ import (
|
||||
// SetCommand returns a CLI command to interactively update an application config value.
|
||||
func SetCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "set [config] [key] [value]",
|
||||
Use: "set <config> <key> <value>",
|
||||
Short: "Set an application config value",
|
||||
Long: "Set an application config value. The [config] argument must be the path of the file when using the `confix` tool standalone, otherwise it must be the name of the config file without the .toml extension.",
|
||||
Args: cobra.ExactArgs(3),
|
||||
@ -30,7 +31,7 @@ func SetCommand() *cobra.Command {
|
||||
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
if clientCtx.HomeDir != "" {
|
||||
filename = fmt.Sprintf("%s/config/%s.toml", clientCtx.HomeDir, filename)
|
||||
filename = filepath.Join(clientCtx.HomeDir, "config", filename+tomlSuffix)
|
||||
}
|
||||
|
||||
plan := transform.Plan{
|
||||
@ -72,7 +73,12 @@ func SetCommand() *cobra.Command {
|
||||
ctx = confix.WithLogWriter(ctx, cmd.ErrOrStderr())
|
||||
}
|
||||
|
||||
return confix.Upgrade(ctx, plan, filename, outputPath, FlagSkipValidate)
|
||||
doc, err := confix.LoadConfig(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return confix.Upgrade(ctx, plan, doc, filename, outputPath, FlagSkipValidate)
|
||||
},
|
||||
}
|
||||
|
||||
@ -86,7 +92,7 @@ func SetCommand() *cobra.Command {
|
||||
// GetCommand returns a CLI command to interactively get an application config value.
|
||||
func GetCommand() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "get [config] [key]",
|
||||
Use: "get <config> <key>",
|
||||
Short: "Get an application config value",
|
||||
Long: "Get an application config value. The [config] argument must be the path of the file when using the `confix` tool standalone, otherwise it must be the name of the config file without the .toml extension.",
|
||||
Args: cobra.ExactArgs(2),
|
||||
@ -97,7 +103,7 @@ func GetCommand() *cobra.Command {
|
||||
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
if clientCtx.HomeDir != "" {
|
||||
filename = fmt.Sprintf("%s/config/%s.toml", clientCtx.HomeDir, filename)
|
||||
filename = filepath.Join(clientCtx.HomeDir, "config", filename+tomlSuffix)
|
||||
}
|
||||
|
||||
doc, err := confix.LoadConfig(filename)
|
||||
|
||||
@ -3,6 +3,7 @@ package cmd_test
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -17,6 +18,7 @@ import (
|
||||
|
||||
// initClientContext initiates client Context for tests
|
||||
func initClientContext(t *testing.T) (client.Context, func()) {
|
||||
t.Helper()
|
||||
home := t.TempDir()
|
||||
chainID := "test-chain"
|
||||
clientCtx := client.Context{}.
|
||||
@ -26,6 +28,8 @@ func initClientContext(t *testing.T) (client.Context, func()) {
|
||||
clientCtx, err := config.ReadFromClientConfig(clientCtx)
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, clientCtx.ChainID, chainID)
|
||||
|
||||
_ = os.Link(filepath.Join(home, "config", "client.toml"), filepath.Join(home, "config", "unsupported.toml"))
|
||||
return clientCtx, func() { _ = os.RemoveAll(home) }
|
||||
}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/cobra"
|
||||
@ -12,10 +13,10 @@ import (
|
||||
)
|
||||
|
||||
func ViewCommand() *cobra.Command {
|
||||
flagOutputFomat := "output-format"
|
||||
flagOutputFormat := "output-format"
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "view [config]",
|
||||
Use: "view <config>",
|
||||
Short: "View the config file",
|
||||
Long: "View the config file. The [config] argument must be the path of the file when using the `confix` tool standalone, otherwise it must be the name of the config file without the .toml extension.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
@ -24,7 +25,7 @@ func ViewCommand() *cobra.Command {
|
||||
|
||||
clientCtx := client.GetClientContextFromCmd(cmd)
|
||||
if clientCtx.HomeDir != "" {
|
||||
filename = fmt.Sprintf("%s/config/%s.toml", clientCtx.HomeDir, filename)
|
||||
filename = filepath.Join(clientCtx.HomeDir, "config", filename+tomlSuffix)
|
||||
}
|
||||
|
||||
file, err := os.ReadFile(filename)
|
||||
@ -32,7 +33,7 @@ func ViewCommand() *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
if format, _ := cmd.Flags().GetString(flagOutputFomat); format == "toml" {
|
||||
if format, _ := cmd.Flags().GetString(flagOutputFormat); format == "toml" {
|
||||
cmd.Println(string(file))
|
||||
return nil
|
||||
}
|
||||
@ -49,7 +50,7 @@ func ViewCommand() *cobra.Command {
|
||||
}
|
||||
|
||||
// output flag
|
||||
cmd.Flags().String(flagOutputFomat, "toml", "Output format (json|toml)")
|
||||
cmd.Flags().String(flagOutputFormat, "toml", "Output format (json|toml)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ halt-time = 0
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: Tendermint block pruning is dependant on this parameter in conjunction
|
||||
# Note: Tendermint block pruning is dependent on this parameter in conjunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
|
||||
@ -43,7 +43,7 @@ halt-time = 0
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: Tendermint block pruning is dependant on this parameter in conjunction
|
||||
# Note: Tendermint block pruning is dependent on this parameter in conjunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
@ -172,7 +172,7 @@ enable-fee-suggestion = false
|
||||
# GasToSuggest defines gas limit when calculating the fee
|
||||
gas-to-suggest = 200000
|
||||
|
||||
# DenomToSuggest defines the defult denom for fee suggestion.
|
||||
# DenomToSuggest defines the default denom for fee suggestion.
|
||||
# Price must be in minimum-gas-prices.
|
||||
denom-to-suggest = "uatom"
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ halt-time = 0
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: Tendermint block pruning is dependant on this parameter in conjunction
|
||||
# Note: Tendermint block pruning is dependent on this parameter in conjunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
@ -170,7 +170,7 @@ enable-fee-suggestion = false
|
||||
# GasToSuggest defines gas limit when calculating the fee
|
||||
gas-to-suggest = 200000
|
||||
|
||||
# DenomToSuggest defines the defult denom for fee suggestion.
|
||||
# DenomToSuggest defines the default denom for fee suggestion.
|
||||
# Price must be in minimum-gas-prices.
|
||||
denom-to-suggest = "uatom"
|
||||
|
||||
|
||||
17
tools/confix/data/v0.47-client.toml
Normal file
17
tools/confix/data/v0.47-client.toml
Normal file
@ -0,0 +1,17 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Client Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The network chain ID
|
||||
chain-id = "demo"
|
||||
# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory)
|
||||
keyring-backend = "os"
|
||||
# CLI output format (text|json)
|
||||
output = "text"
|
||||
# <host>:<port> to Tendermint RPC interface for this chain
|
||||
node = "tcp://localhost:26657"
|
||||
# Transaction broadcasting mode (sync|async|block)
|
||||
broadcast-mode = "sync"
|
||||
@ -47,7 +47,7 @@ halt-time = 0
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: CometBFT block pruning is dependant on this parameter in conjunction
|
||||
# Note: CometBFT block pruning is dependent on this parameter in conjunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
|
||||
17
tools/confix/data/v0.50-client.toml
Normal file
17
tools/confix/data/v0.50-client.toml
Normal file
@ -0,0 +1,17 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Client Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The network chain ID
|
||||
chain-id = "demo"
|
||||
# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory)
|
||||
keyring-backend = "os"
|
||||
# CLI output format (text|json)
|
||||
output = "text"
|
||||
# <host>:<port> to Tendermint RPC interface for this chain
|
||||
node = "tcp://localhost:26657"
|
||||
# Transaction broadcasting mode (sync|async|block)
|
||||
broadcast-mode = "sync"
|
||||
228
tools/confix/data/v0.52-app.toml
Normal file
228
tools/confix/data/v0.52-app.toml
Normal file
@ -0,0 +1,228 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Base Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The minimum gas prices a validator is willing to accept for processing a
|
||||
# transaction. A transaction's fees must meet the minimum of any denomination
|
||||
# specified in this config (e.g. 0.25token1,0.0001token2).
|
||||
minimum-gas-prices = "0stake"
|
||||
|
||||
# The maximum gas a query coming over rest/grpc may consume.
|
||||
# If this is set to zero, the query can consume an unbounded amount of gas.
|
||||
query-gas-limit = "0"
|
||||
|
||||
# default: the last 362880 states are kept, pruning at 10 block intervals
|
||||
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
|
||||
# everything: 2 latest states will be kept; pruning at 10 block intervals.
|
||||
# custom: allow pruning options to be manually specified through 'pruning-keep-recent', and 'pruning-interval'
|
||||
pruning = "default"
|
||||
|
||||
# These are applied if and only if the pruning strategy is custom.
|
||||
pruning-keep-recent = "0"
|
||||
pruning-interval = "0"
|
||||
|
||||
# HaltHeight contains a non-zero block height at which a node will gracefully
|
||||
# halt and shutdown that can be used to assist upgrades and testing.
|
||||
#
|
||||
# Note: Commitment of state will be attempted on the corresponding block.
|
||||
halt-height = 0
|
||||
|
||||
# HaltTime contains a non-zero minimum block time (in Unix seconds) at which
|
||||
# a node will gracefully halt and shutdown that can be used to assist upgrades
|
||||
# and testing.
|
||||
#
|
||||
# Note: Commitment of state will be attempted on the corresponding block.
|
||||
halt-time = 0
|
||||
|
||||
# MinRetainBlocks defines the minimum block height offset from the current
|
||||
# block being committed, such that all blocks past this offset are pruned
|
||||
# from CometBFT. It is used as part of the process of determining the
|
||||
# ResponseCommit.RetainHeight value during ABCI Commit. A value of 0 indicates
|
||||
# that no blocks should be pruned.
|
||||
#
|
||||
# This configuration value is only responsible for pruning CometBFT blocks.
|
||||
# It has no bearing on application state pruning which is determined by the
|
||||
# "pruning-*" configurations.
|
||||
#
|
||||
# Note: CometBFT block pruning is dependent on this parameter in conjunction
|
||||
# with the unbonding (safety threshold) period, state pruning and state sync
|
||||
# snapshot parameters to determine the correct minimum value of
|
||||
# ResponseCommit.RetainHeight.
|
||||
min-retain-blocks = 0
|
||||
|
||||
# InterBlockCache enables inter-block caching.
|
||||
inter-block-cache = true
|
||||
|
||||
# IndexEvents defines the set of events in the form {eventType}.{attributeKey},
|
||||
# which informs CometBFT what to index. If empty, all events will be indexed.
|
||||
#
|
||||
# Example:
|
||||
# ["message.sender", "message.recipient"]
|
||||
index-events = []
|
||||
|
||||
# IavlCacheSize set the size of the iavl tree cache (in number of nodes).
|
||||
iavl-cache-size = 781250
|
||||
|
||||
# IAVLDisableFastNode enables or disables the fast node feature of IAVL.
|
||||
# Default is false.
|
||||
iavl-disable-fastnode = false
|
||||
|
||||
# AppDBBackend defines the database backend type to use for the application and snapshots DBs.
|
||||
# An empty string indicates that a fallback will be used.
|
||||
# The fallback is the db_backend value set in CometBFT's config.toml.
|
||||
app-db-backend = ""
|
||||
|
||||
###############################################################################
|
||||
### Telemetry Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[telemetry]
|
||||
|
||||
# Prefixed with keys to separate services.
|
||||
service-name = ""
|
||||
|
||||
# Enabled enables the application telemetry functionality. When enabled,
|
||||
# an in-memory sink is also enabled by default. Operators may also enabled
|
||||
# other sinks such as Prometheus.
|
||||
enabled = false
|
||||
|
||||
# Enable prefixing gauge values with hostname.
|
||||
enable-hostname = false
|
||||
|
||||
# Enable adding hostname to labels.
|
||||
enable-hostname-label = false
|
||||
|
||||
# Enable adding service to labels.
|
||||
enable-service-label = false
|
||||
|
||||
# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink.
|
||||
prometheus-retention-time = 0
|
||||
|
||||
# GlobalLabels defines a global set of name/value label tuples applied to all
|
||||
# metrics emitted using the wrapper functions defined in telemetry package.
|
||||
#
|
||||
# Example:
|
||||
# [["chain_id", "cosmoshub-1"]]
|
||||
global-labels = []
|
||||
|
||||
# MetricsSink defines the type of metrics sink to use.
|
||||
metrics-sink = "mem"
|
||||
|
||||
# StatsdAddr defines the address of a statsd server to send metrics to.
|
||||
# Only utilized if MetricsSink is set to "statsd" or "dogstatsd".
|
||||
statsd-addr = ""
|
||||
|
||||
# DatadogHostname defines the hostname to use when emitting metrics to
|
||||
# Datadog. Only utilized if MetricsSink is set to "dogstatsd".
|
||||
datadog-hostname = ""
|
||||
|
||||
###############################################################################
|
||||
### API Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[api]
|
||||
|
||||
# Enable defines if the API server should be enabled.
|
||||
enable = false
|
||||
|
||||
# Swagger defines if swagger documentation should automatically be registered.
|
||||
swagger = false
|
||||
|
||||
# Address defines the API server to listen on.
|
||||
address = "tcp://localhost:1317"
|
||||
|
||||
# MaxOpenConnections defines the number of maximum open connections.
|
||||
max-open-connections = 1000
|
||||
|
||||
# RPCReadTimeout defines the CometBFT RPC read timeout (in seconds).
|
||||
rpc-read-timeout = 10
|
||||
|
||||
# RPCWriteTimeout defines the CometBFT RPC write timeout (in seconds).
|
||||
rpc-write-timeout = 0
|
||||
|
||||
# RPCMaxBodyBytes defines the CometBFT maximum request body (in bytes).
|
||||
rpc-max-body-bytes = 1000000
|
||||
|
||||
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
|
||||
enabled-unsafe-cors = false
|
||||
|
||||
###############################################################################
|
||||
### gRPC Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[grpc]
|
||||
|
||||
# Enable defines if the gRPC server should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the gRPC server address to bind to.
|
||||
address = "localhost:9090"
|
||||
|
||||
# MaxRecvMsgSize defines the max message size in bytes the server can receive.
|
||||
# The default value is 10MB.
|
||||
max-recv-msg-size = "10485760"
|
||||
|
||||
# MaxSendMsgSize defines the max message size in bytes the server can send.
|
||||
# The default value is math.MaxInt32.
|
||||
max-send-msg-size = "2147483647"
|
||||
|
||||
# SkipCheckHeader defines if the gRPC server should bypass check header.
|
||||
skip-check-header = false
|
||||
|
||||
###############################################################################
|
||||
### State Sync Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# State sync snapshots allow other nodes to rapidly join the network without replaying historical
|
||||
# blocks, instead downloading and applying a snapshot of the application state at a given height.
|
||||
[state-sync]
|
||||
|
||||
# snapshot-interval specifies the block interval at which local state sync snapshots are
|
||||
# taken (0 to disable).
|
||||
snapshot-interval = 0
|
||||
|
||||
# snapshot-keep-recent specifies the number of recent snapshots to keep and serve (0 to keep all).
|
||||
snapshot-keep-recent = 2
|
||||
|
||||
###############################################################################
|
||||
### State Streaming ###
|
||||
###############################################################################
|
||||
|
||||
# Streaming allows nodes to stream state to external systems.
|
||||
[streaming]
|
||||
|
||||
# streaming.abci specifies the configuration for the ABCI Listener streaming service.
|
||||
[streaming.abci]
|
||||
|
||||
# List of kv store keys to stream out via gRPC.
|
||||
# The store key names MUST match the module's StoreKey name.
|
||||
#
|
||||
# Example:
|
||||
# ["acc", "bank", "gov", "staking", "mint"[,...]]
|
||||
# ["*"] to expose all keys.
|
||||
keys = []
|
||||
|
||||
# The plugin name used for streaming via gRPC.
|
||||
# Streaming is only enabled if this is set.
|
||||
# Supported plugins: abci
|
||||
plugin = ""
|
||||
|
||||
# stop-node-on-err specifies whether to stop the node on message delivery error.
|
||||
stop-node-on-err = true
|
||||
|
||||
###############################################################################
|
||||
### Mempool ###
|
||||
###############################################################################
|
||||
|
||||
[mempool]
|
||||
|
||||
# Setting max-txs to 0 will allow for a unbounded amount of transactions in the mempool.
|
||||
# Setting max_txs to negative 1 (-1) will disable transactions from being inserted into the mempool (no-op mempool).
|
||||
# Setting max_txs to a positive number (> 0) will limit the number of transactions in the mempool, by the specified amount.
|
||||
#
|
||||
# Note, this configuration only applies to SDK built-in app-side mempool
|
||||
# implementations.
|
||||
max-txs = -1
|
||||
27
tools/confix/data/v0.52-client.toml
Normal file
27
tools/confix/data/v0.52-client.toml
Normal file
@ -0,0 +1,27 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Client Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The network chain ID
|
||||
chain-id = "demo"
|
||||
# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory)
|
||||
keyring-backend = "test"
|
||||
# Default key name, if set, defines the default key to use for signing transaction when the --from flag is not specified
|
||||
keyring-default-keyname = ""
|
||||
# CLI output format (text|json)
|
||||
output = "text"
|
||||
# <host>:<port> to CometBFT RPC interface for this chain
|
||||
node = "tcp://localhost:26657"
|
||||
# Transaction broadcasting mode (sync|async)
|
||||
broadcast-mode = "sync"
|
||||
|
||||
# gRPC server endpoint to which the client will connect.
|
||||
# It can be overwritten by the --grpc-addr flag in each command.
|
||||
grpc-address = ""
|
||||
|
||||
# Allow the gRPC client to connect over insecure channels.
|
||||
# It can be overwritten by the --grpc-insecure flag in each command.
|
||||
grpc-insecure = false
|
||||
178
tools/confix/data/v2-app.toml
Normal file
178
tools/confix/data/v2-app.toml
Normal file
@ -0,0 +1,178 @@
|
||||
[comet]
|
||||
|
||||
# min-retain-blocks defines the minimum block height offset from the current block being committed, such that all blocks past this offset are pruned from CometBFT. A value of 0 indicates that no blocks should be pruned.
|
||||
min-retain-blocks = 0
|
||||
|
||||
# halt-height contains a non-zero block height at which a node will gracefully halt and shutdown that can be used to assist upgrades and testing.
|
||||
halt-height = 0
|
||||
|
||||
# halt-time contains a non-zero minimum block time (in Unix seconds) at which a node will gracefully halt and shutdown that can be used to assist upgrades and testing.
|
||||
halt-time = 0
|
||||
|
||||
# address defines the CometBFT RPC server address to bind to.
|
||||
address = 'tcp://127.0.0.1:26658'
|
||||
|
||||
# transport defines the CometBFT RPC server transport protocol: socket, grpc
|
||||
transport = 'socket'
|
||||
|
||||
# trace enables the CometBFT RPC server to output trace information about its internal operations.
|
||||
trace = false
|
||||
|
||||
# standalone starts the application without the CometBFT node. The node should be started separately.
|
||||
standalone = false
|
||||
|
||||
# index-abci-events defines the set of events in the form {eventType}.{attributeKey}, which informs CometBFT what to index. If empty, all events will be indexed.
|
||||
index-abci-events = []
|
||||
|
||||
# disable-index-abci-events disables the ABCI event indexing done by CometBFT. Useful when relying on the SDK indexer for event indexing, but still want events to be included in FinalizeBlockResponse.
|
||||
disable-index-abci-events = false
|
||||
|
||||
# disable-abci-events disables all ABCI events. Useful when relying on the SDK indexer for event indexing.
|
||||
disable-abci-events = false
|
||||
|
||||
# mempool defines the configuration for the SDK built-in app-side mempool implementations.
|
||||
[comet.mempool]
|
||||
|
||||
# max-txs defines the maximum number of transactions that can be in the mempool. A value of 0 indicates an unbounded mempool, a negative value disables the app-side mempool.
|
||||
max-txs = -1
|
||||
|
||||
# indexer defines the configuration for the SDK built-in indexer implementation.
|
||||
[comet.indexer]
|
||||
|
||||
# Buffer size of the channels used for buffering data sent to indexer go routines.
|
||||
channel_buffer_size = 1024
|
||||
|
||||
# Target is a map of named indexer targets to their configuration.
|
||||
[comet.indexer.target]
|
||||
|
||||
[grpc]
|
||||
|
||||
# Enable defines if the gRPC server should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the gRPC server address to bind to.
|
||||
address = 'localhost:9090'
|
||||
|
||||
# MaxRecvMsgSize defines the max message size in bytes the server can receive.
|
||||
# The default value is 10MB.
|
||||
max-recv-msg-size = 10485760
|
||||
|
||||
# MaxSendMsgSize defines the max message size in bytes the server can send.
|
||||
# The default value is math.MaxInt32.
|
||||
max-send-msg-size = 2147483647
|
||||
|
||||
[grpc-gateway]
|
||||
|
||||
# Enable defines if the gRPC-Gateway should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the address the gRPC-Gateway server binds to.
|
||||
address = 'localhost:1317'
|
||||
|
||||
[rest]
|
||||
|
||||
# Enable defines if the REST server should be enabled.
|
||||
enable = true
|
||||
|
||||
# Address defines the REST server address to bind to.
|
||||
address = 'localhost:8080'
|
||||
|
||||
[server]
|
||||
|
||||
# minimum-gas-prices defines the price which a validator is willing to accept for processing a transaction. A transaction's fees must meet the minimum of any denomination specified in this config (e.g. 0.25token1;0.0001token2).
|
||||
minimum-gas-prices = '0stake'
|
||||
|
||||
[store]
|
||||
|
||||
# The type of database for application and snapshots databases.
|
||||
app-db-backend = 'goleveldb'
|
||||
|
||||
[store.options]
|
||||
|
||||
# State commitment database type. Currently we support: "iavl" and "iavl-v2"
|
||||
sc-type = 'iavl'
|
||||
|
||||
# Pruning options for state commitment
|
||||
[store.options.sc-pruning-option]
|
||||
|
||||
# Number of recent heights to keep on disk.
|
||||
keep-recent = 2
|
||||
|
||||
# Height interval at which pruned heights are removed from disk.
|
||||
interval = 100
|
||||
|
||||
[store.options.iavl-config]
|
||||
|
||||
# CacheSize set the size of the iavl tree cache.
|
||||
cache-size = 500000
|
||||
|
||||
# If true, the tree will work like no fast storage and always not upgrade fast storage.
|
||||
skip-fast-storage-upgrade = true
|
||||
|
||||
[store.options.iavl-v2-config]
|
||||
|
||||
# CheckpointInterval set the interval of the checkpoint.
|
||||
checkpoint-interval = 0
|
||||
|
||||
# CheckpointMemory set the memory of the checkpoint.
|
||||
checkpoint-memory = 0
|
||||
|
||||
# StateStorage set the state storage.
|
||||
state-storage = false
|
||||
|
||||
# HeightFilter set the height filter.
|
||||
height-filter = 0
|
||||
|
||||
# EvictionDepth set the eviction depth.
|
||||
eviction-depth = 0
|
||||
|
||||
# PruneRatio set the prune ratio.
|
||||
prune-ratio = 0.0
|
||||
|
||||
# MinimumKeepVersions set the minimum keep versions.
|
||||
minimum-keep-versions = 0
|
||||
|
||||
[swagger]
|
||||
|
||||
# Enable enables/disables the Swagger UI server
|
||||
enable = true
|
||||
|
||||
# Address defines the server address to bind to
|
||||
address = 'localhost:8090'
|
||||
|
||||
[telemetry]
|
||||
|
||||
# Enable enables the application telemetry functionality. When enabled, an in-memory sink is also enabled by default. Operators may also enabled other sinks such as Prometheus.
|
||||
enable = true
|
||||
|
||||
# Address defines the metrics server address to bind to.
|
||||
address = 'localhost:7180'
|
||||
|
||||
# Prefixed with keys to separate services.
|
||||
service-name = ''
|
||||
|
||||
# Enable prefixing gauge values with hostname.
|
||||
enable-hostname = false
|
||||
|
||||
# Enable adding hostname to labels.
|
||||
enable-hostname-label = false
|
||||
|
||||
# Enable adding service to labels.
|
||||
enable-service-label = false
|
||||
|
||||
# PrometheusRetentionTime, when positive, enables a Prometheus metrics sink. It defines the retention duration in seconds.
|
||||
prometheus-retention-time = 600
|
||||
|
||||
# GlobalLabels defines a global set of name/value label tuples applied to all metrics emitted using the wrapper functions defined in telemetry package.
|
||||
# Example:
|
||||
# [["chain_id", "cosmoshub-1"]]
|
||||
global-labels = []
|
||||
|
||||
# MetricsSink defines the type of metrics backend to use. Default is in memory
|
||||
metrics-sink = ''
|
||||
|
||||
# StatsdAddr defines the address of a statsd server to send metrics to. Only utilized if MetricsSink is set to "statsd" or "dogstatsd".
|
||||
stats-addr = ''
|
||||
|
||||
# DatadogHostname defines the hostname to use when emitting metrics to Datadog. Only utilized if MetricsSink is set to "dogstatsd".
|
||||
data-dog-hostname = ''
|
||||
27
tools/confix/data/v2-client.toml
Normal file
27
tools/confix/data/v2-client.toml
Normal file
@ -0,0 +1,27 @@
|
||||
# This is a TOML config file.
|
||||
# For more information, see https://github.com/toml-lang/toml
|
||||
|
||||
###############################################################################
|
||||
### Client Configuration ###
|
||||
###############################################################################
|
||||
|
||||
# The network chain ID
|
||||
chain-id = ""
|
||||
# The keyring's backend, where the keys are stored (os|file|kwallet|pass|test|memory)
|
||||
keyring-backend = "test"
|
||||
# Default key name, if set, defines the default key to use for signing transaction when the --from flag is not specified
|
||||
keyring-default-keyname = ""
|
||||
# CLI output format (text|json)
|
||||
output = "text"
|
||||
# <host>:<port> to CometBFT RPC interface for this chain
|
||||
node = "tcp://localhost:26657"
|
||||
# Transaction broadcasting mode (sync|async)
|
||||
broadcast-mode = "sync"
|
||||
|
||||
# gRPC server endpoint to which the client will connect.
|
||||
# It can be overwritten by the --grpc-addr flag in each command.
|
||||
grpc-address = ""
|
||||
|
||||
# Allow the gRPC client to connect over insecure channels.
|
||||
# It can be overwritten by the --grpc-insecure flag in each command.
|
||||
grpc-insecure = false
|
||||
@ -77,7 +77,7 @@ func DiffKeys(lhs, rhs *tomledit.Document) []Diff {
|
||||
return diff
|
||||
}
|
||||
|
||||
// DiffKeys diffs the keyspaces with different values of the TOML documents in files lhs and rhs.
|
||||
// DiffValues diffs the keyspaces with different values of the TOML documents in files lhs and rhs.
|
||||
func DiffValues(lhs, rhs *tomledit.Document) []Diff {
|
||||
diff := diffDocs(allKVs(lhs.Global), allKVs(rhs.Global), true)
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/creachadair/tomledit"
|
||||
)
|
||||
@ -11,11 +13,16 @@ import (
|
||||
//go:embed data
|
||||
var data embed.FS
|
||||
|
||||
// LoadConfig loads and parses the TOML document from confix data
|
||||
func LoadLocalConfig(name string) (*tomledit.Document, error) {
|
||||
f, err := data.Open(fmt.Sprintf("data/%s-app.toml", name))
|
||||
// LoadLocalConfig loads and parses the TOML document from confix data
|
||||
func LoadLocalConfig(name, configType string) (*tomledit.Document, error) {
|
||||
fileName, err := getFileName(name, configType)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to read file: %w. This file should have been included in confix", err))
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err := data.Open(filepath.Join("data", fileName))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read file: %w. This file should have been included in confix", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
@ -26,9 +33,21 @@ func LoadLocalConfig(name string) (*tomledit.Document, error) {
|
||||
func LoadConfig(path string) (*tomledit.Document, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open %q: %v", path, err)
|
||||
return nil, fmt.Errorf("failed to open %q: %w", path, err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return tomledit.Parse(f)
|
||||
}
|
||||
|
||||
// getFileName constructs the filename based on the type of configuration (app or client)
|
||||
func getFileName(name, configType string) (string, error) {
|
||||
switch strings.ToLower(configType) {
|
||||
case "app":
|
||||
return fmt.Sprintf("%s-app.toml", name), nil
|
||||
case "client":
|
||||
return fmt.Sprintf("%s-client.toml", name), nil
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported config type: %q", configType)
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,155 +3,163 @@ module cosmossdk.io/tools/confix
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
github.com/cosmos/cosmos-sdk v0.50.6
|
||||
github.com/creachadair/atomicfile v0.3.1
|
||||
github.com/creachadair/tomledit v0.0.24
|
||||
github.com/pelletier/go-toml/v2 v2.2.2
|
||||
github.com/cosmos/cosmos-sdk v0.50.11
|
||||
github.com/creachadair/atomicfile v0.3.7
|
||||
github.com/creachadair/tomledit v0.0.27
|
||||
github.com/pelletier/go-toml/v2 v2.2.3
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
gotest.tools/v3 v3.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.5 // indirect
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.6 // indirect
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/core v0.11.0 // indirect
|
||||
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
|
||||
cosmossdk.io/core v0.11.1 // indirect
|
||||
cosmossdk.io/depinject v1.1.0 // indirect
|
||||
cosmossdk.io/errors v1.0.1 // indirect
|
||||
cosmossdk.io/log v1.4.1 // indirect
|
||||
cosmossdk.io/math v1.3.0 // indirect
|
||||
cosmossdk.io/store v1.1.0 // indirect
|
||||
cosmossdk.io/x/tx v0.13.4 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
cosmossdk.io/log v1.5.0 // indirect
|
||||
cosmossdk.io/math v1.5.0 // indirect
|
||||
cosmossdk.io/store v1.1.1 // indirect
|
||||
cosmossdk.io/x/tx v1.0.0-alpha.3 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/DataDog/zstd v1.5.6 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
|
||||
github.com/bgentry/speakeasy v0.2.0 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v1.1.1 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
|
||||
github.com/cockroachdb/pebble v1.1.2 // 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.12 // indirect
|
||||
github.com/cometbft/cometbft-db v0.11.0 // indirect
|
||||
github.com/cometbft/cometbft v0.38.17 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.0.2 // indirect
|
||||
github.com/cosmos/cosmos-db v1.1.1 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // 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
|
||||
github.com/cosmos/iavl v1.1.2 // indirect
|
||||
github.com/cosmos/iavl v1.2.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.6.0 // indirect
|
||||
github.com/emicklei/dot v1.6.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/emicklei/dot v1.6.2 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.12.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.30.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.3 // indirect
|
||||
github.com/hashicorp/go-plugin v1.5.2 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.4 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.1 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.9 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/linxGnu/grocksdb v1.8.14 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/linxGnu/grocksdb v1.9.7 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/minio/highwayhash v1.0.3 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.1 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/stretchr/testify v1.10.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/tools v0.22.0 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect; indirectÎ
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/grpc v1.70.0 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
nhooyr.io/websocket v1.8.6 // indirect
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cosmossdk.io/api v0.7.5 h1:eMPTReoNmGUm8DeiQL9DyM8sYDjEhWzL1+nLbI9DqtQ=
|
||||
cosmossdk.io/api v0.7.5/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38=
|
||||
cosmossdk.io/api v0.7.6 h1:PC20PcXy1xYKH2KU4RMurVoFjjKkCgYRbVAD4PdqUuY=
|
||||
cosmossdk.io/api v0.7.6/go.mod h1:IcxpYS5fMemZGqyYtErK7OqvdM0C8kdW3dq8Q/XIG38=
|
||||
cosmossdk.io/collections v0.4.0 h1:PFmwj2W8szgpD5nOd8GWH6AbYNi1f2J6akWXJ7P5t9s=
|
||||
cosmossdk.io/collections v0.4.0/go.mod h1:oa5lUING2dP+gdDquow+QjlF45eL1t4TJDypgGd+tv0=
|
||||
cosmossdk.io/core v0.11.0 h1:vtIafqUi+1ZNAE/oxLOQQ7Oek2n4S48SWLG8h/+wdbo=
|
||||
cosmossdk.io/core v0.11.0/go.mod h1:LaTtayWBSoacF5xNzoF8tmLhehqlA9z1SWiPuNC6X1w=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.4 h1:PLNp8ZYAMPTUKyG9IK2hsbciDWqna2z1Wsl98okJopc=
|
||||
cosmossdk.io/depinject v1.0.0-alpha.4/go.mod h1:HeDk7IkR5ckZ3lMGs/o91AVUc7E596vMaOmslGFM3yU=
|
||||
cosmossdk.io/core v0.11.1 h1:h9WfBey7NAiFfIcUhDVNS503I2P2HdZLebJlUIs8LPA=
|
||||
cosmossdk.io/core v0.11.1/go.mod h1:OJzxcdC+RPrgGF8NJZR2uoQr56tc7gfBKhiKeDO7hH0=
|
||||
cosmossdk.io/depinject v1.1.0 h1:wLan7LG35VM7Yo6ov0jId3RHWCGRhe8E8bsuARorl5E=
|
||||
cosmossdk.io/depinject v1.1.0/go.mod h1:kkI5H9jCGHeKeYWXTqYdruogYrEeWvBQCw1Pj4/eCFI=
|
||||
cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0=
|
||||
cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U=
|
||||
cosmossdk.io/log v1.4.1 h1:wKdjfDRbDyZRuWa8M+9nuvpVYxrEOwbD/CA8hvhU8QM=
|
||||
cosmossdk.io/log v1.4.1/go.mod h1:k08v0Pyq+gCP6phvdI6RCGhLf/r425UT6Rk/m+o74rU=
|
||||
cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE=
|
||||
cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k=
|
||||
cosmossdk.io/store v1.1.0 h1:LnKwgYMc9BInn9PhpTFEQVbL9UK475G2H911CGGnWHk=
|
||||
cosmossdk.io/store v1.1.0/go.mod h1:oZfW/4Fc/zYqu3JmQcQdUJ3fqu5vnYTn3LZFFy8P8ng=
|
||||
cosmossdk.io/x/tx v0.13.4 h1:Eg0PbJgeO0gM8p5wx6xa0fKR7hIV6+8lC56UrsvSo0Y=
|
||||
cosmossdk.io/x/tx v0.13.4/go.mod h1:BkFqrnGGgW50Y6cwTy+JvgAhiffbGEKW6KF9ufcDpvk=
|
||||
cosmossdk.io/log v1.5.0 h1:dVdzPJW9kMrnAYyMf1duqacoidB9uZIl+7c6z0mnq0g=
|
||||
cosmossdk.io/log v1.5.0/go.mod h1:Tr46PUJjiUthlwQ+hxYtUtPn4D/oCZXAkYevBeh5+FI=
|
||||
cosmossdk.io/math v1.5.0 h1:sbOASxee9Zxdjd6OkzogvBZ25/hP929vdcYcBJQbkLc=
|
||||
cosmossdk.io/math v1.5.0/go.mod h1:AAwwBmUhqtk2nlku174JwSll+/DepUXW3rWIXN5q+Nw=
|
||||
cosmossdk.io/store v1.1.1 h1:NA3PioJtWDVU7cHHeyvdva5J/ggyLDkyH0hGHl2804Y=
|
||||
cosmossdk.io/store v1.1.1/go.mod h1:8DwVTz83/2PSI366FERGbWSH7hL6sB7HbYp8bqksNwM=
|
||||
cosmossdk.io/x/tx v1.0.0-alpha.3 h1:+55/JFH5QRqnFhOI2heH3DKsaNL0RpXcJOQNzUvHiaQ=
|
||||
cosmossdk.io/x/tx v1.0.0-alpha.3/go.mod h1:h4pQ/j6Gfu8goB1R3Jbl4qY4RjYVNAsoylcleTXdSRg=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
|
||||
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
|
||||
github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o=
|
||||
@ -31,21 +31,19 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible h1:qSG2N4FghB1He/r2mFrWKCaL7dXCilEuNEeAn20fdD4=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
|
||||
github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
|
||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I=
|
||||
github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg=
|
||||
github.com/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I=
|
||||
github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg=
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@ -56,8 +54,8 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
@ -69,18 +67,21 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 h1:41iFGWnSlI2gVpmOtVTJZNodLdLQLn/KsJqFvXwnd/s=
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bgentry/speakeasy v0.2.0 h1:tgObeVOf8WAvtuAX6DhJ4xks4CFNwPDZiqzGqIHE51E=
|
||||
github.com/bgentry/speakeasy v0.2.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
||||
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY=
|
||||
github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE=
|
||||
github.com/bytedance/sonic v1.12.8 h1:4xYRVRlXIgvSZ4e8iVTlMF5szgpXd4AfvuWgA8I8lgs=
|
||||
github.com/bytedance/sonic v1.12.8/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0=
|
||||
github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
@ -88,8 +89,6 @@ github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInq
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
|
||||
github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
@ -102,6 +101,9 @@ github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6D
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
@ -111,42 +113,42 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E=
|
||||
github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg=
|
||||
github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I=
|
||||
github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
|
||||
github.com/cockroachdb/pebble v1.1.1 h1:XnKU22oiCLy2Xn8vp1re67cXg4SAasg/WDt1NtcRFaw=
|
||||
github.com/cockroachdb/pebble v1.1.1/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k=
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=
|
||||
github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=
|
||||
github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
|
||||
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
|
||||
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/cometbft/cometbft v0.38.12 h1:OWsLZN2KcSSFe8bet9xCn07VwhBnavPea3VyPnNq1bg=
|
||||
github.com/cometbft/cometbft v0.38.12/go.mod h1:GPHp3/pehPqgX1930HmK1BpBLZPxB75v/dZg8Viwy+o=
|
||||
github.com/cometbft/cometbft-db v0.11.0 h1:M3Lscmpogx5NTbb1EGyGDaFRdsoLWrUWimFEyf7jej8=
|
||||
github.com/cometbft/cometbft-db v0.11.0/go.mod h1:GDPJAC/iFHNjmZZPN8V8C1yr/eyityhi2W1hz2MGKSc=
|
||||
github.com/cometbft/cometbft v0.38.17 h1:FkrQNbAjiFqXydeAO81FUzriL4Bz0abYxN/eOHrQGOk=
|
||||
github.com/cometbft/cometbft v0.38.17/go.mod h1:5l0SkgeLRXi6bBfQuevXjKqML1jjfJJlvI1Ulp02/o4=
|
||||
github.com/cometbft/cometbft-db v0.14.1 h1:SxoamPghqICBAIcGpleHbmoPqy+crij/++eZz3DlerQ=
|
||||
github.com/cometbft/cometbft-db v0.14.1/go.mod h1:KHP1YghilyGV/xjD5DP3+2hyigWx0WTp9X+0Gnx0RxQ=
|
||||
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk=
|
||||
github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis=
|
||||
github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs=
|
||||
github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA=
|
||||
github.com/cosmos/cosmos-db v1.1.1 h1:FezFSU37AlBC8S98NlSagL76oqBRWq/prTPvFcEJNCM=
|
||||
github.com/cosmos/cosmos-db v1.1.1/go.mod h1:AghjcIPqdhSLP/2Z0yha5xPH3nLnskz81pBx3tcVSAw=
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 h1:eNcayDLpip+zVLRLYafhzLvQlSmyab+RC5W7ZfmxJLA=
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5/go.mod h1:hQGLpiIUloJBMdQMMWb/4wRApmI9hjHH05nefC0Ojec=
|
||||
github.com/cosmos/cosmos-sdk v0.50.6 h1:efR3MsvMHX5sxS3be+hOobGk87IzlZbSpsI2x/Vw3hk=
|
||||
github.com/cosmos/cosmos-sdk v0.50.6/go.mod h1:lVkRY6cdMJ0fG3gp8y4hFrsKZqF4z7y0M2UXFb9Yt40=
|
||||
github.com/cosmos/cosmos-sdk v0.50.11 h1:LxR1aAc8kixdrs3itO+3a44sFoc+vjxVAOyPFx22yjk=
|
||||
github.com/cosmos/cosmos-sdk v0.50.11/go.mod h1:gt14Meok2IDCjbDtjwkbUcgVNEpUBDN/4hg9cCUtLgw=
|
||||
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
|
||||
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
|
||||
github.com/cosmos/gogogateway v1.2.0 h1:Ae/OivNhp8DqBi/sh2A8a1D0y638GpL3tkmLQAiKxTE=
|
||||
@ -154,19 +156,20 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ
|
||||
github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU=
|
||||
github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro=
|
||||
github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0=
|
||||
github.com/cosmos/iavl v1.1.2 h1:zL9FK7C4L/P4IF1Dm5fIwz0WXCnn7Bp1M2FxH0ayM7Y=
|
||||
github.com/cosmos/iavl v1.1.2/go.mod h1:jLeUvm6bGT1YutCaL2fIar/8vGUE8cPZvh/gXEWDaDM=
|
||||
github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8=
|
||||
github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw=
|
||||
github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU=
|
||||
github.com/cosmos/ics23/go v0.11.0/go.mod h1:A8OjxPE67hHST4Icw94hOxxFEJMBG031xIGF/JHNIY0=
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 h1:7ehuBGuyIytsXbd4MP43mLeoN2LTOEnk5nvue4rK+yM=
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3/go.mod h1:HENcEP+VtahZFw38HZ3+LS3Iv5XV6svsnkk9vdJtLr8=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creachadair/atomicfile v0.3.1 h1:yQORkHjSYySh/tv5th1dkKcn02NEW5JleB84sjt+W4Q=
|
||||
github.com/creachadair/atomicfile v0.3.1/go.mod h1:mwfrkRxFKwpNAflYZzytbSwxvbK6fdGRRlp0KEQc0qU=
|
||||
github.com/creachadair/tomledit v0.0.24 h1:5Xjr25R2esu1rKCbQEmjZYlrhFkDspoAbAKb6QKQDhQ=
|
||||
github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLCOjOshAWSzWr8U=
|
||||
github.com/creachadair/atomicfile v0.3.7 h1:wdg8+Isz07NDMi2yZQAoI1EKB9SxuDhvo5MUii/ZqlM=
|
||||
github.com/creachadair/atomicfile v0.3.7/go.mod h1:lUrZrE/XjMA7rJY/n8dF7/sSpy6KjtPaxPbrDambthA=
|
||||
github.com/creachadair/mds v0.22.1 h1:Wink9jeYR7brBbOkOTVZVrd6vyb5W4ZBRhlZd96TSgU=
|
||||
github.com/creachadair/mds v0.22.1/go.mod h1:ArfS0vPHoLV/SzuIzoqTEZfoYmac7n9Cj8XPANHocvw=
|
||||
github.com/creachadair/tomledit v0.0.27 h1:6xOpEnkKmcpT/gmKhabN0JXrqNX065lyje1/mXTSSIE=
|
||||
github.com/creachadair/tomledit v0.0.27/go.mod h1:v1EWpgCisD3ct1kO8Gq4o4pdgX5JXD0rBI2PJ4UnPoA=
|
||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||
@ -177,13 +180,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
@ -204,8 +206,8 @@ github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5m
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/emicklei/dot v1.6.1 h1:ujpDlBkkwgWUY+qPId5IwapRW/xEoligRSYjioR6DFI=
|
||||
github.com/emicklei/dot v1.6.1/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
|
||||
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
|
||||
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@ -216,9 +218,8 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
@ -230,10 +231,10 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps=
|
||||
github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/getsentry/sentry-go v0.30.0 h1:lWUwDnY7sKHaVIoZ9wYqRHJ5iEmoc0pqcRqFkosKzBo=
|
||||
github.com/getsentry/sentry-go v0.30.0/go.mod h1:WU9B9/1/sHDqeV8T+3VwwbjeR5MSXs/6aqG3mqZrezA=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
@ -246,8 +247,8 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
|
||||
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
|
||||
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
|
||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
@ -256,6 +257,10 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
|
||||
@ -291,10 +296,13 @@ github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXP
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
|
||||
github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
@ -317,18 +325,21 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v1.12.1 h1:MVlul7pQNoDzWRLTw5imwYsl+usrS1TXG2H4jg6ImGw=
|
||||
github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
@ -344,14 +355,16 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
|
||||
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
@ -371,17 +384,17 @@ github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyN
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-metrics v0.5.3 h1:M5uADWMOGCTUNU1YuC4hfknOeHNaX54LDm4oYSucoNE=
|
||||
github.com/hashicorp/go-metrics v0.5.3/go.mod h1:KEjodfebIOuBYSAe/bHTm+HChmKSxAOXPBieMLYozDE=
|
||||
github.com/hashicorp/go-metrics v0.5.4 h1:8mmPiIJkTPPEbAiV97IxdAGNdRdaWwVap1BU6elejKY=
|
||||
github.com/hashicorp/go-metrics v0.5.4/go.mod h1:CG5yz4NZ/AI/aQt9Ucm/vdBnbh7fvmv4lxZ350i+QQI=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-plugin v1.5.2 h1:aWv8eimFqWlsEiMrYZdPYl+FdHaBJSN4AWwGWfT1G2Y=
|
||||
github.com/hashicorp/go-plugin v1.5.2/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
|
||||
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
|
||||
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
@ -403,15 +416,15 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 h1:jtBwzzcHuTmFrQN6xQZn6CQEO/V9f7HsjsjeEZ6auqU=
|
||||
github.com/hdevalence/ed25519consensus v0.1.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hashicorp/yamux v0.1.2 h1:XtB8kyFOyHXYVFnwT5C3+Bdo8gArse7j2AQ0DA0Uey8=
|
||||
github.com/hashicorp/yamux v0.1.2/go.mod h1:C+zze2n6e/7wshOZep2A70/aQU6QBRWJO/G6FT1wIns=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 h1:37ICyZqdyj0lAZ8P4D1d1id3HqbbG1N3iBb1Tb4rdcU=
|
||||
github.com/hdevalence/ed25519consensus v0.2.0/go.mod h1:w3BHWjwJbFU29IRHL1Iqkw3sus+7FctEyM4RqDxYNzo=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huandu/go-assert v1.1.5 h1:fjemmA7sSfYHJD7CUqs9qTwwfdNAx7/j2/ZlHXzNB3c=
|
||||
github.com/huandu/go-assert v1.1.5/go.mod h1:yOLvuqZwmcHIC5rIzrBhT7D3Q9c3GFnd0JrPVhn/06U=
|
||||
github.com/huandu/skiplist v1.2.0 h1:gox56QD77HzSC0w+Ws3MH3iie755GBJU1OER3h5VsYw=
|
||||
github.com/huandu/skiplist v1.2.0/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w=
|
||||
github.com/huandu/skiplist v1.2.1 h1:dTi93MgjwErA/8idWTzIw4Y1kZsMWx35fmI2c8Rij7w=
|
||||
github.com/huandu/skiplist v1.2.1/go.mod h1:7v3iFjLcSAzO4fN5B8dvebvo/qsfumiLiDXMrPiHF9w=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
@ -434,6 +447,7 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
|
||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
@ -445,9 +459,12 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
@ -463,16 +480,15 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
|
||||
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
|
||||
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/linxGnu/grocksdb v1.8.14 h1:HTgyYalNwBSG/1qCQUIott44wU5b2Y9Kr3z7SK5OfGQ=
|
||||
github.com/linxGnu/grocksdb v1.8.14/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA=
|
||||
github.com/linxGnu/grocksdb v1.9.7 h1:Bp2r1Yti/IXxEobZZnDooXAui/Q+5gVqgQMenLWyDUw=
|
||||
github.com/linxGnu/grocksdb v1.9.7/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA=
|
||||
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@ -491,14 +507,11 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
|
||||
github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY=
|
||||
github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q=
|
||||
github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
|
||||
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@ -529,8 +542,9 @@ github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxzi
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
|
||||
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a h1:dlRvE5fWabOchtH7znfiFCcOvmIYgOeAS5ifBXBlh9Q=
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a/go.mod h1:hVoHR2EVESiICEMbg137etN/Lx+lSrHPTD39Z/uE+2s=
|
||||
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
|
||||
@ -557,8 +571,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034=
|
||||
github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ=
|
||||
github.com/opencontainers/runc v1.1.3 h1:vIXrkId+0/J2Ymu2m7VjGvbSlAId9XNRPhn2p4b+d8w=
|
||||
github.com/opencontainers/runc v1.1.3/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||
github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
|
||||
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
@ -574,13 +588,11 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67 h1:jik8PHtAIsPlCRJjJzl4udgEf7hawInF9texMeO2jrU=
|
||||
github.com/petermattis/goid v0.0.0-20231207134359-e60b3f734c67/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw=
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
@ -601,8 +613,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
|
||||
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.20.1 h1:IMJXHOD6eARkQpxo8KkhgEVFlBNm+nkrFUyGlIu7Na8=
|
||||
github.com/prometheus/client_golang v1.20.1/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
@ -617,14 +630,16 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
@ -634,15 +649,14 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
@ -651,41 +665,33 @@ github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgY
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
|
||||
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
|
||||
github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU=
|
||||
github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
@ -706,9 +712,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs=
|
||||
@ -719,16 +725,16 @@ github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
|
||||
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
|
||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@ -737,12 +743,24 @@ github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWp
|
||||
github.com/zondax/ledger-go v0.14.3 h1:wEpJt2CEcBJ428md/5MgSLsXLBos98sBOyxNmCjfUCw=
|
||||
github.com/zondax/ledger-go v0.14.3/go.mod h1:IKKaoxupuB43g4NxeQmbLXv7T9AlQyie1UpHb342ycI=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5 h1:qxen9oVGzDdIRP6ejyAJc760RwW4SnVDiTYTzwnXuxo=
|
||||
go.etcd.io/bbolt v1.4.0-alpha.0.0.20240404170359-43604f3112c5/go.mod h1:eW0HG9/oHQhvRCvb1/pIXW4cOvtDqeQK+XSi3TnwaXY=
|
||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
@ -758,22 +776,23 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0 h1:985EYyeCOxTpcgOTJpflJUwOeEz0CQOdPt73OzpE9F8=
|
||||
golang.org/x/exp v0.0.0-20240404231335-c0f41cb1a7a0/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f h1:XdNn9LlyWAhLVp6P/i8QYBW+hlyhrhei9uErw2B5GJo=
|
||||
golang.org/x/exp v0.0.0-20241108190413-2d47ceb2692f/go.mod h1:D5SMRVC3C2/4+F/DB1wZsLRnSNimn2Sp/NPsCrsv8ak=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@ -788,8 +807,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -812,13 +831,14 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -829,9 +849,10 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -840,14 +861,11 @@ golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -869,9 +887,11 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -887,20 +907,21 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@ -924,8 +945,8 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
||||
golang.org/x/tools v0.27.0 h1:qEKojBykQkQ4EynWy4S8Weg69NumxKdn40Fce3uc/8o=
|
||||
golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@ -948,10 +969,10 @@ google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20220314164441-57ef72a4c106/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY=
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 h1:yrTuav+chrF0zF/joFGICKTzYv7mh/gr9AgEXrVU8ao=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
|
||||
@ -966,11 +987,12 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
|
||||
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
|
||||
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@ -985,8 +1007,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -1026,6 +1048,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
|
||||
nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw=
|
||||
pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
|
||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
|
||||
|
||||
63
tools/confix/match.go
Normal file
63
tools/confix/match.go
Normal file
@ -0,0 +1,63 @@
|
||||
package confix
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/creachadair/tomledit"
|
||||
"github.com/creachadair/tomledit/transform"
|
||||
)
|
||||
|
||||
// MatchKeys diffs the keyspaces of the TOML documents in files lhs and rhs.
|
||||
// Comments, order, and values are ignored for comparison purposes.
|
||||
// It will return in the format of map[oldKey]newKey
|
||||
func MatchKeys(lhs, rhs *tomledit.Document) map[string]string {
|
||||
matches := matchDocs(map[string]string{}, allKVs(lhs.Global), allKVs(rhs.Global))
|
||||
|
||||
lsec, rsec := lhs.Sections, rhs.Sections
|
||||
transform.SortSectionsByName(lsec)
|
||||
transform.SortSectionsByName(rsec)
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(lsec) && j < len(rsec) {
|
||||
switch {
|
||||
case lsec[i].Name.Before(rsec[j].Name):
|
||||
i++
|
||||
case rsec[j].Name.Before(lsec[i].Name):
|
||||
j++
|
||||
default:
|
||||
matches = matchDocs(matches, allKVs(lsec[i]), allKVs(rsec[j]))
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return matches
|
||||
}
|
||||
|
||||
// matchDocs get all the keys matching in lhs and rhs.
|
||||
// value of keys are ignored
|
||||
func matchDocs(matchesMap map[string]string, lhs, rhs []KV) map[string]string {
|
||||
sort.Slice(lhs, func(i, j int) bool {
|
||||
return lhs[i].Key < lhs[j].Key
|
||||
})
|
||||
sort.Slice(rhs, func(i, j int) bool {
|
||||
return rhs[i].Key < rhs[j].Key
|
||||
})
|
||||
|
||||
i, j := 0, 0
|
||||
for i < len(lhs) && j < len(rhs) {
|
||||
switch {
|
||||
case lhs[i].Key < rhs[j].Key:
|
||||
i++
|
||||
case lhs[i].Key > rhs[j].Key:
|
||||
j++
|
||||
default:
|
||||
// key exists in both lhs and rhs
|
||||
matchesMap[lhs[i].Key] = rhs[j].Key
|
||||
i++
|
||||
j++
|
||||
}
|
||||
}
|
||||
|
||||
return matchesMap
|
||||
}
|
||||
@ -11,43 +11,69 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
AppConfig = "app.toml"
|
||||
ClientConfig = "client.toml"
|
||||
CMTConfig = "config.toml"
|
||||
AppConfig = "app.toml"
|
||||
AppConfigType = "app"
|
||||
ClientConfig = "client.toml"
|
||||
ClientConfigType = "client"
|
||||
CMTConfig = "config.toml"
|
||||
)
|
||||
|
||||
// MigrationMap defines a mapping from a version to a transformation plan.
|
||||
type MigrationMap map[string]func(from *tomledit.Document, to string) transform.Plan
|
||||
type MigrationMap map[string]func(from *tomledit.Document, to, planType string) (transform.Plan, *tomledit.Document)
|
||||
|
||||
// loadDestConfigFile is the function signature to load the destination version
|
||||
// configuration toml file.
|
||||
type loadDestConfigFile func(to string) (*tomledit.Document, error)
|
||||
type loadDestConfigFile func(to, planType string) (*tomledit.Document, error)
|
||||
|
||||
var Migrations = MigrationMap{
|
||||
"v0.45": NoPlan, // Confix supports only the current supported SDK version. So we do not support v0.44 -> v0.45.
|
||||
"v0.46": defaultPlanBuilder,
|
||||
"v0.47": defaultPlanBuilder,
|
||||
"v0.50": defaultPlanBuilder,
|
||||
// "v0.xx.x": PlanBuilder, // add specific migration in case of configuration changes in minor versions
|
||||
"v0.52": defaultPlanBuilder,
|
||||
"v2": V2PlanBuilder,
|
||||
// "v0.xx.x": defaultPlanBuilder, // add specific migration in case of configuration changes in minor versions
|
||||
}
|
||||
|
||||
func defaultPlanBuilder(from *tomledit.Document, to string) transform.Plan {
|
||||
return PlanBuilder(from, to, LoadLocalConfig)
|
||||
type v2KeyChangesMap map[string][]string
|
||||
|
||||
// list all the keys which are need to be modified in v2
|
||||
var v2KeyChanges = v2KeyChangesMap{
|
||||
"minimum-gas-prices": []string{"server.minimum-gas-prices"},
|
||||
"min-retain-blocks": []string{"comet.min-retain-blocks"},
|
||||
"index-events": []string{"comet.index-abci-events"},
|
||||
"halt-height": []string{"comet.halt-height"},
|
||||
"halt-time": []string{"comet.halt-time"},
|
||||
"app-db-backend": []string{"store.app-db-backend"},
|
||||
"pruning-keep-recent": []string{
|
||||
"store.options.sc-pruning-option.keep-recent",
|
||||
},
|
||||
"pruning-interval": []string{
|
||||
"store.options.sc-pruning-option.interval",
|
||||
},
|
||||
"iavl-cache-size": []string{"store.options.iavl-config.cache-size"},
|
||||
"iavl-disable-fastnode": []string{"store.options.iavl-config.skip-fast-storage-upgrade"},
|
||||
"telemetry.enabled": []string{"telemetry.enable"},
|
||||
"mempool.max-txs": []string{"comet.mempool.max-txs"},
|
||||
// Add other key mappings as needed
|
||||
}
|
||||
|
||||
func defaultPlanBuilder(from *tomledit.Document, to, planType string) (transform.Plan, *tomledit.Document) {
|
||||
return PlanBuilder(from, to, planType, LoadLocalConfig)
|
||||
}
|
||||
|
||||
// PlanBuilder is a function that returns a transformation plan for a given diff between two files.
|
||||
func PlanBuilder(from *tomledit.Document, to string, loadFn loadDestConfigFile) transform.Plan {
|
||||
func PlanBuilder(from *tomledit.Document, to, planType string, loadFn loadDestConfigFile) (transform.Plan, *tomledit.Document) {
|
||||
plan := transform.Plan{}
|
||||
deletedSections := map[string]bool{}
|
||||
|
||||
target, err := loadFn(to)
|
||||
target, err := loadFn(to, planType)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to parse file: %w. This file should have been valid", err))
|
||||
}
|
||||
|
||||
diffs := DiffKeys(from, target)
|
||||
for _, diff := range diffs {
|
||||
diff := diff
|
||||
kv := diff.KV
|
||||
|
||||
var step transform.Step
|
||||
@ -119,11 +145,105 @@ func PlanBuilder(from *tomledit.Document, to string, loadFn loadDestConfigFile)
|
||||
plan = append(plan, step)
|
||||
}
|
||||
|
||||
return plan
|
||||
return plan, from
|
||||
}
|
||||
|
||||
// NoPlan returns a no-op plan.
|
||||
func NoPlan(_ *tomledit.Document, to string) transform.Plan {
|
||||
func NoPlan(from *tomledit.Document, to, planType string) (transform.Plan, *tomledit.Document) {
|
||||
fmt.Printf("no migration needed to %s\n", to)
|
||||
return transform.Plan{}
|
||||
return transform.Plan{}, from
|
||||
}
|
||||
|
||||
// V2PlanBuilder is a function that returns a transformation plan to convert to v2 config
|
||||
func V2PlanBuilder(from *tomledit.Document, to, planType string) (transform.Plan, *tomledit.Document) {
|
||||
target, err := LoadLocalConfig(to, planType)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to parse file: %w. This file should have been valid", err))
|
||||
}
|
||||
|
||||
plan := transform.Plan{}
|
||||
plan = updateMatchedKeysPlan(from, target, plan)
|
||||
plan = applyKeyChangesPlan(from, plan)
|
||||
|
||||
return plan, target
|
||||
}
|
||||
|
||||
// updateMatchedKeysPlan updates all matched keys with old key values
|
||||
func updateMatchedKeysPlan(from, target *tomledit.Document, plan transform.Plan) transform.Plan {
|
||||
matches := MatchKeys(from, target)
|
||||
for oldKey, newKey := range matches {
|
||||
oldEntry := getEntry(from, oldKey)
|
||||
if oldEntry == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if the key "app-db-backend" exists and if its value is empty in the existing config
|
||||
// If the value is empty, update the key value with the default value
|
||||
// of v2 i.e., goleveldb to prevent network failures.
|
||||
if isAppDBBackend(newKey, oldEntry) {
|
||||
continue // lets keep app-db-backend with v2 default value
|
||||
}
|
||||
|
||||
// update newKey value with old entry value
|
||||
step := createUpdateStep(oldKey, newKey, oldEntry)
|
||||
plan = append(plan, step)
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
// applyKeyChangesPlan checks if key changes are needed with the "to" version and applies them
|
||||
func applyKeyChangesPlan(from *tomledit.Document, plan transform.Plan) transform.Plan {
|
||||
changes := v2KeyChanges
|
||||
for oldKey, newKeys := range changes {
|
||||
oldEntry := getEntry(from, oldKey)
|
||||
if oldEntry == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, newKey := range newKeys {
|
||||
// check if the key "app-db-backend" exists and if its value is empty in the existing config
|
||||
// If the value is empty, update the key value with the default value
|
||||
// of v2 i.e., goleveldb to prevent network failures.
|
||||
if isAppDBBackend(newKey, oldEntry) {
|
||||
continue // lets keep app-db-backend with v2 default value
|
||||
}
|
||||
|
||||
// update newKey value with old entry value
|
||||
step := createUpdateStep(oldKey, newKey, oldEntry)
|
||||
plan = append(plan, step)
|
||||
}
|
||||
}
|
||||
return plan
|
||||
}
|
||||
|
||||
// getEntry retrieves the first entry for the given key from the document
|
||||
func getEntry(doc *tomledit.Document, key string) *parser.KeyValue {
|
||||
splitKeys := strings.Split(key, ".")
|
||||
entry := doc.First(splitKeys...)
|
||||
if entry == nil || entry.KeyValue == nil {
|
||||
return nil
|
||||
}
|
||||
return entry.KeyValue
|
||||
}
|
||||
|
||||
// isAppDBBackend checks if the key is "store.app-db-backend" and the value is empty
|
||||
func isAppDBBackend(key string, entry *parser.KeyValue) bool {
|
||||
return key == "store.app-db-backend" && entry.Value.String() == `""`
|
||||
}
|
||||
|
||||
// createUpdateStep creates a transformation step to update a key with a new key value
|
||||
func createUpdateStep(oldKey, newKey string, oldEntry *parser.KeyValue) transform.Step {
|
||||
return transform.Step{
|
||||
Desc: fmt.Sprintf("updating %s key with %s key", oldKey, newKey),
|
||||
T: transform.Func(func(_ context.Context, doc *tomledit.Document) error {
|
||||
splitNewKeys := strings.Split(newKey, ".")
|
||||
newEntry := doc.First(splitNewKeys...)
|
||||
if newEntry == nil || newEntry.KeyValue == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
newEntry.KeyValue.Value = oldEntry.Value
|
||||
return nil
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,7 +14,6 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
clientcfg "github.com/cosmos/cosmos-sdk/client/config"
|
||||
srvcfg "github.com/cosmos/cosmos-sdk/server/config"
|
||||
)
|
||||
|
||||
// Upgrade reads the configuration file at configPath and applies any
|
||||
@ -28,34 +27,30 @@ import (
|
||||
// Upgrade is a convenience wrapper for calls to LoadConfig, ApplyFixes, and
|
||||
// CheckValid. If the caller requires more control over the behavior of the
|
||||
// Upgrade, call those functions directly.
|
||||
func Upgrade(ctx context.Context, plan transform.Plan, configPath, outputPath string, skipValidate bool) error {
|
||||
func Upgrade(ctx context.Context, plan transform.Plan, doc *tomledit.Document, configPath, outputPath string, skipValidate bool) error {
|
||||
if configPath == "" {
|
||||
return errors.New("empty input configuration path")
|
||||
}
|
||||
|
||||
doc, err := LoadConfig(configPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading config: %v", err)
|
||||
}
|
||||
|
||||
// transforms doc and reports whether it succeeded.
|
||||
if err := plan.Apply(ctx, doc); err != nil {
|
||||
return fmt.Errorf("updating %q: %v", configPath, err)
|
||||
return fmt.Errorf("updating %q: %w", configPath, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := tomledit.Format(&buf, doc); err != nil {
|
||||
return fmt.Errorf("formatting config: %v", err)
|
||||
return fmt.Errorf("formatting config: %w", err)
|
||||
}
|
||||
|
||||
// allow to skip validation
|
||||
if !skipValidate {
|
||||
// verify that file is valid after applying fixes
|
||||
if err := CheckValid(configPath, buf.Bytes()); err != nil {
|
||||
return fmt.Errorf("updated config is invalid: %v", err)
|
||||
return fmt.Errorf("updated config is invalid: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
if outputPath == "" {
|
||||
_, err = os.Stdout.Write(buf.Bytes())
|
||||
} else {
|
||||
@ -77,14 +72,8 @@ func CheckValid(fileName string, data []byte) error {
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(fileName, AppConfig):
|
||||
var cfg srvcfg.Config
|
||||
if err := v.Unmarshal(&cfg); err != nil {
|
||||
return fmt.Errorf("failed to unmarshal as server config: %w", err)
|
||||
}
|
||||
|
||||
if err := cfg.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("server config invalid: %w", err)
|
||||
}
|
||||
// no validation of server config as v1 and v2 configs are both valid.
|
||||
// any app.toml is simply considered as a server config.
|
||||
case strings.HasSuffix(fileName, ClientConfig):
|
||||
var cfg clientcfg.ClientConfig
|
||||
if err := v.Unmarshal(&cfg); err != nil {
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func mustReadConfig(t *testing.T, path string) []byte {
|
||||
t.Helper()
|
||||
f, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open file: %v", err)
|
||||
@ -34,9 +35,6 @@ func TestCheckValid(t *testing.T) {
|
||||
err = confix.CheckValid("client.toml", []byte{})
|
||||
assert.Error(t, err, "client config invalid: chain-id is empty")
|
||||
|
||||
err = confix.CheckValid("app.toml", []byte{})
|
||||
assert.ErrorContains(t, err, "server config invalid")
|
||||
|
||||
err = confix.CheckValid("app.toml", mustReadConfig(t, "data/v0.45-app.toml"))
|
||||
assert.NilError(t, err)
|
||||
|
||||
|
||||
@ -36,28 +36,72 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## Features
|
||||
## v1.7.1 - 2025-01-12
|
||||
|
||||
* [#12457](https://github.com/cosmos/cosmos-sdk/issues/12457) Add `cosmovisor pre-upgrade` command to manually add an upgrade to cosmovisor.
|
||||
* [#15361](https://github.com/cosmos/cosmos-sdk/pull/15361) Add `cosmovisor config` command to display the configuration used by cosmovisor.
|
||||
### Bug Fixes
|
||||
|
||||
## Client Breaking Changes
|
||||
* [#23652](https://github.com/cosmos/cosmos-sdk/pull/23652) Fix issue with wrong directory placement when using `prepare-upgrade` for non archive.
|
||||
* [#23653](https://github.com/cosmos/cosmos-sdk/pull/23653) Remove duplicate binary downloads during auto-download process
|
||||
|
||||
* [#14881](https://github.com/cosmos/cosmos-sdk/pull/14881) Cosmovisor supports only upgrade plan with a checksum. This is enforced by the `x/upgrade` module for better security.
|
||||
## v1.7.0 - 2024-11-18
|
||||
|
||||
### Features
|
||||
|
||||
* [#21790](https://github.com/cosmos/cosmos-sdk/pull/21790) Add `add-batch-upgrade` command.
|
||||
* [#21972](https://github.com/cosmos/cosmos-sdk/pull/21972) Add `prepare-upgrade` command
|
||||
* [#21932](https://github.com/cosmos/cosmos-sdk/pull/21932) Add `cosmovisor show-upgrade-info` command to display the upgrade-info.json into stdout.
|
||||
|
||||
### Improvements
|
||||
|
||||
* [#21891](https://github.com/cosmos/cosmos-sdk/pull/21891) create `current` symlink as relative
|
||||
* [#21462](https://github.com/cosmos/cosmos-sdk/pull/21462) Pass `stdin` to binary.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* [#22528](https://github.com/cosmos/cosmos-sdk/pull/22528) Fix premature upgrades on restarting cosmovisor.
|
||||
|
||||
## v1.6.0 - 2024-08-12
|
||||
|
||||
## Improvements
|
||||
|
||||
* Bump `cosmossdk.io/x/upgrade` to v0.1.4 (including go-getter vulnerability fix)
|
||||
* [#19995](https://github.com/cosmos/cosmos-sdk/pull/19995):
|
||||
* `init command` writes the configuration to the config file only at the default path `DAEMON_HOME/cosmovisor/config.toml`.
|
||||
* Provide `--cosmovisor-config` flag with value as args to provide the path to the configuration file in the `run` command. `run --cosmovisor-config <path> (other cmds with flags) ...`.
|
||||
* Add `--cosmovisor-config` flag to provide `config.toml` path to the configuration file in root command used by `add-upgrade` and `config` subcommands.
|
||||
* `config command` now displays the configuration from the config file if it is provided. If the config file is not provided, it will display the configuration from the environment variables.
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
* [#20062](https://github.com/cosmos/cosmos-sdk/pull/20062) Fixed cosmovisor add-upgrade permissions
|
||||
* [#20585](https://github.com/cosmos/cosmos-sdk/pull/20585) Always parse stdout and stderr
|
||||
* [#20585](https://github.com/cosmos/cosmos-sdk/pull/20585) Pass right home to command `status`
|
||||
* [#20585](https://github.com/cosmos/cosmos-sdk/pull/20585) Fix upgrades applied automatically (check two casing of sync_info)
|
||||
|
||||
## v1.5.0 - 2023-07-17
|
||||
|
||||
## Features
|
||||
|
||||
* [#16413](https://github.com/cosmos/cosmos-sdk/issues/16413) Add `cosmovisor add-upgrade` command to manually add an upgrade to cosmovisor.
|
||||
* [#16573](https://github.com/cosmos/cosmos-sdk/pull/16573) Extend `cosmovisor` configuration with new log format options.
|
||||
* [#16550](https://github.com/cosmos/cosmos-sdk/pull/16550) Add COSMOVISOR_CUSTOM_PREUPGRADE to cosmovisor to execute custom pre-upgrade scripts (separate from daemon `pre-upgrade` command).
|
||||
* [#16963](https://github.com/cosmos/cosmos-sdk/pull/16963) Add DAEMON_SHUTDOWN_GRACE to send interrupt and wait before sending kill.
|
||||
* [#15361](https://github.com/cosmos/cosmos-sdk/pull/15361) Add `cosmovisor config` command to display the configuration used by cosmovisor.
|
||||
|
||||
## Improvements
|
||||
|
||||
* [#16919](https://github.com/cosmos/cosmos-sdk/pull/16919) Add COSMOVISOR_DISABLE_RECASE to cosmovisor to disable automatic case change for plan name.
|
||||
* [#14881](https://github.com/cosmos/cosmos-sdk/pull/14881) Refactor Cosmovisor to use `x/upgrade` validation logic.
|
||||
* [#14881](https://github.com/cosmos/cosmos-sdk/pull/14881) Refactor Cosmovisor to depend only on the `x/upgrade` module.
|
||||
* [#15362](https://github.com/cosmos/cosmos-sdk/pull/15362) Allow disabling Cosmovisor logs
|
||||
* [#15362](https://github.com/cosmos/cosmos-sdk/pull/15362) Allow disabling Cosmovisor logs.
|
||||
|
||||
## v1.4.0 2022-10-23
|
||||
## v1.4.0 - 2022-10-23
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* [#13603](https://github.com/cosmos-sdk/pull/13603) Rename cosmovisor package to `cosmossdk.io/tools/cosmovisor`.
|
||||
* [#13603](https://github.com/cosmos/cosmos-sdk/pull/13603) Rename cosmovisor package to `cosmossdk.io/tools/cosmovisor`.
|
||||
|
||||
## v1.3.0 2022-09-11
|
||||
## v1.3.0 - 2022-09-11
|
||||
|
||||
### Improvements
|
||||
|
||||
@ -68,7 +112,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* [#13221](https://github.com/cosmos/cosmos-sdk/pull/13221) Fix `go install`.
|
||||
|
||||
## v1.2.0 2022-07-26
|
||||
## v1.2.0 - 2022-07-26
|
||||
|
||||
### Features
|
||||
|
||||
@ -85,7 +129,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* [\#12188](https://github.com/cosmos/cosmos-sdk/pull/12188) Remove the possibility to set a time with only a number. `DAEMON_POLL_INTERVAL` env variable now only supports a duration (e.g. `100ms`, `30s`, `20m`).
|
||||
|
||||
## v1.1.0 2022-02-10
|
||||
## v1.1.0 - 2022-02-10
|
||||
|
||||
### Features
|
||||
|
||||
@ -100,7 +144,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* [\#10458](https://github.com/cosmos/cosmos-sdk/pull/10458) Fix version when using 'go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v1.0.0' to install cosmovisor.
|
||||
|
||||
## v1.0.0 2021-09-30
|
||||
## v1.0.0 - 2021-09-30
|
||||
|
||||
### Features
|
||||
|
||||
@ -119,7 +163,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
* [\#10128](https://github.com/cosmos/cosmos-sdk/pull/10128) Change default value of `DAEMON_RESTART_AFTER_UPGRADE` to `true`.
|
||||
|
||||
## v0.1 2021-08-06
|
||||
## v0.1.0 - 2021-08-06
|
||||
|
||||
This is the first release and we started this changelog on 2021-07-01. See the [README](https://github.com/cosmos/cosmos-sdk/blob/release/cosmovisor/v0.1.x/cosmovisor/CHANGELOG.md) file for the full list of features.
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ all: cosmovisor test
|
||||
|
||||
cosmovisor:
|
||||
go build -mod=readonly ./cmd/cosmovisor
|
||||
@echo "cosmovisor binary has been successfully built in tools/cosmovisor/cosmovisor"
|
||||
|
||||
test:
|
||||
go test -mod=readonly -race ./...
|
||||
|
||||
@ -7,21 +7,22 @@ sidebar_position: 1
|
||||
`cosmovisor` is a process manager for Cosmos SDK application binaries that automates application binary switch at chain upgrades.
|
||||
It polls the `upgrade-info.json` file that is created by the x/upgrade module at upgrade height, and then can automatically download the new binary, stop the current binary, switch from the old binary to the new one, and finally restart the node with the new binary.
|
||||
|
||||
* [Cosmovisor](#cosmovisor)
|
||||
* [Design](#design)
|
||||
* [Contributing](#contributing)
|
||||
* [Setup](#setup)
|
||||
* [Design](#design)
|
||||
* [Contributing](#contributing)
|
||||
* [Setup](#setup)
|
||||
* [Installation](#installation)
|
||||
* [Command Line Arguments And Environment Variables](#command-line-arguments-and-environment-variables)
|
||||
* [Folder Layout](#folder-layout)
|
||||
* [Usage](#usage)
|
||||
* [Usage](#usage)
|
||||
* [Initialization](#initialization)
|
||||
* [Detecting Upgrades](#detecting-upgrades)
|
||||
* [Adding Upgrade Binary](#adding-upgrade-binary)
|
||||
* [Auto-Download](#auto-download)
|
||||
* [Example: SimApp Upgrade](#example-simapp-upgrade)
|
||||
* [Preparing for an Upgrade](#preparing-for-an-upgrade)
|
||||
* [Example: SimApp Upgrade](#example-simapp-upgrade)
|
||||
* [Chain Setup](#chain-setup)
|
||||
* [Prepare Cosmovisor and Start the Chain](#prepare-cosmovisor-and-start-the-chain)
|
||||
* [Update App](#update-app)
|
||||
* [Prepare Cosmovisor and Start the Chain](#prepare-cosmovisor-and-start-the-chain)
|
||||
* [Update App](#update-app)
|
||||
|
||||
## Design
|
||||
|
||||
@ -35,7 +36,7 @@ Cosmovisor is designed to be used as a wrapper for a `Cosmos SDK` app:
|
||||
*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.*
|
||||
|
||||
:::tip
|
||||
Only the lastest version of cosmovisor is actively developed/maintained.
|
||||
Only the latest version of cosmovisor is actively developed/maintained.
|
||||
:::
|
||||
|
||||
:::warning
|
||||
@ -52,7 +53,7 @@ Release branches have the following format `release/cosmovisor/vA.B.x`, where A
|
||||
|
||||
### Installation
|
||||
|
||||
You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.3.0).
|
||||
You can download Cosmovisor from the [GitHub releases](https://github.com/cosmos/cosmos-sdk/releases/tag/cosmovisor%2Fv1.5.0).
|
||||
|
||||
To install the latest version of `cosmovisor`, run the following command:
|
||||
|
||||
@ -60,10 +61,10 @@ To install the latest version of `cosmovisor`, run the following command:
|
||||
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest
|
||||
```
|
||||
|
||||
To install a previous version, you can specify the version. IMPORTANT: Chains that use Cosmos SDK v0.44.3 or earlier (eg v0.44.2) and want to use auto-download feature MUST use `cosmovisor v0.1.0`
|
||||
To install a specific version, you can specify the version:
|
||||
|
||||
```shell
|
||||
go install github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor@v0.1.0
|
||||
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@v1.5.0
|
||||
```
|
||||
|
||||
Run `cosmovisor version` to check the cosmovisor version.
|
||||
@ -71,7 +72,8 @@ Run `cosmovisor version` to check the cosmovisor version.
|
||||
Alternatively, for building from source, simply run `make cosmovisor`. The binary will be located in `tools/cosmovisor`.
|
||||
|
||||
:::warning
|
||||
Building from source using `make cosmovisor` won't display the correct `cosmovisor` version.
|
||||
Installing cosmovisor using `go install` will display the correct `cosmovisor` version.
|
||||
Building from source (`make cosmovisor`) or installing `cosmovisor` by other means won't display the correct version.
|
||||
:::
|
||||
|
||||
### Command Line Arguments And Environment Variables
|
||||
@ -82,26 +84,28 @@ The first argument passed to `cosmovisor` is the action for `cosmovisor` to take
|
||||
* `run` - Run the configured binary using the rest of the provided arguments.
|
||||
* `version` - Output the `cosmovisor` version and also run the binary with the `version` argument.
|
||||
* `config` - Display the current `cosmovisor` configuration, that means displaying the environment variables value that `cosmovisor` is using.
|
||||
* `add-upgrade` - Add an upgrade manually to `cosmovisor`.
|
||||
* `add-upgrade` - Add an upgrade manually to `cosmovisor`. This command allow you to easily add the binary corresponding to an upgrade in cosmovisor.
|
||||
|
||||
All arguments passed to `cosmovisor run` will be passed to the application binary (as a subprocess). `cosmovisor` will return `/dev/stdout` and `/dev/stderr` of the subprocess as its own. For this reason, `cosmovisor run` cannot accept any command-line arguments other than those available to the application binary.
|
||||
|
||||
:::warning
|
||||
Use of `cosmovisor` without one of the action arguments is deprecated. For backwards compatibility, if the first argument is not an action argument, `run` is assumed. However, this fallback might be removed in future versions, so it is recommended that you always provide `run`.
|
||||
:::
|
||||
|
||||
`cosmovisor` reads its configuration from environment variables:
|
||||
`cosmovisor` reads its configuration from environment variables, or its configuration file (use `--cosmovisor-config <path>`):
|
||||
|
||||
* `DAEMON_HOME` is the location where the `cosmovisor/` directory is kept that contains the genesis binary, the upgrade binaries, and any additional auxiliary files associated with each binary (e.g. `$HOME/.gaiad`, `$HOME/.regend`, `$HOME/.simd`, etc.).
|
||||
* `DAEMON_NAME` is the name of the binary itself (e.g. `gaiad`, `regend`, `simd`, etc.).
|
||||
* `DAEMON_ALLOW_DOWNLOAD_BINARIES` (*optional*), if set to `true`, will enable auto-downloading of new binaries (for security reasons, this is intended for full nodes rather than validators). By default, `cosmovisor` will not auto-download new binaries.
|
||||
* `DAEMON_DOWNLOAD_MUST_HAVE_CHECKSUM` (*optional*, default = `false`), if `true` cosmovisor will require that a checksum is provided in the upgrade plan for the binary to be downloaded. If `false`, cosmovisor will not require a checksum to be provided, but still check the checksum if one is provided.
|
||||
* `DAEMON_RESTART_AFTER_UPGRADE` (*optional*, default = `true`), if `true`, restarts the subprocess with the same command-line arguments and flags (but with the new binary) after a successful upgrade. Otherwise (`false`), `cosmovisor` stops running after an upgrade and requires the system administrator to manually restart it. Note restart is only after the upgrade and does not auto-restart the subprocess after an error occurs.
|
||||
* `DAEMON_RESTART_DELAY` (*optional*, default none), allow a node operator to define a delay between the node halt (for upgrade) and backup by the specified time. The value must be a duration (e.g. `1s`).
|
||||
* `DAEMON_SHUTDOWN_GRACE` (*optional*, default none), if set, send interrupt to binary and wait the specified time to allow for cleanup/cache flush to disk before sending the kill signal. The value must be a duration (e.g. `1s`).
|
||||
* `DAEMON_POLL_INTERVAL` (*optional*, default 300 milliseconds), is the interval length for polling the upgrade plan file. The value must be a duration (e.g. `1s`).
|
||||
* `DAEMON_DATA_BACKUP_DIR` option to set a custom backup directory. If not set, `DAEMON_HOME` is used.
|
||||
* `UNSAFE_SKIP_BACKUP` (defaults to `false`), if set to `true`, upgrades directly without performing a backup. Otherwise (`false`, default) backs up the data before trying the upgrade. The default value of false is useful and recommended in case of failures and when a backup needed to rollback. We recommend using the default backup option `UNSAFE_SKIP_BACKUP=false`.
|
||||
* `DAEMON_PREUPGRADE_MAX_RETRIES` (defaults to `0`). The maximum number of times to call [`pre-upgrade`](https://docs.cosmos.network/main/building-apps/app-upgrade#pre-upgrade-handling) in the application after exit status of `31`. After the maximum number of retries, Cosmovisor fails the upgrade.
|
||||
* `DAEMON_PREUPGRADE_MAX_RETRIES` (defaults to `0`). The maximum number of times to call [`pre-upgrade`](https://docs.cosmos.network/main/build/building-apps/app-upgrade#pre-upgrade-handling) in the application after exit status of `31`. After the maximum number of retries, Cosmovisor fails the upgrade.
|
||||
* `COSMOVISOR_DISABLE_LOGS` (defaults to `false`). If set to true, this will disable Cosmovisor logs (but not the underlying process) completely. This may be useful, for example, when a Cosmovisor subcommand you are executing returns a valid JSON you are then parsing, as logs added by Cosmovisor make this output not a valid JSON.
|
||||
* `COSMOVISOR_COLOR_LOGS` (defaults to `true`). If set to true, this will colorise Cosmovisor logs (but not the underlying process).
|
||||
* `COSMOVISOR_TIMEFORMAT_LOGS` (defaults to `kitchen`). If set to a value (`layout|ansic|unixdate|rubydate|rfc822|rfc822z|rfc850|rfc1123|rfc1123z|rfc3339|rfc3339nano|kitchen`), this will add timestamp prefix to Cosmovisor logs (but not the underlying process).
|
||||
* `COSMOVISOR_CUSTOM_PREUPGRADE` (defaults to ``). If set, this will run $DAEMON_HOME/cosmovisor/$COSMOVISOR_CUSTOM_PREUPGRADE prior to upgrade with the arguments [ upgrade.Name, upgrade.Height ]. Executes a custom script (separate and prior to the chain daemon pre-upgrade command)
|
||||
* `COSMOVISOR_DISABLE_RECASE` (defaults to `false`). If set to true, the upgrade directory will expected to match the upgrade plan name without any case changes
|
||||
|
||||
### Folder Layout
|
||||
|
||||
@ -114,15 +118,16 @@ Use of `cosmovisor` without one of the action arguments is deprecated. For backw
|
||||
│ └── bin
|
||||
│ └── $DAEMON_NAME
|
||||
└── upgrades
|
||||
└── <name>
|
||||
├── bin
|
||||
│ └── $DAEMON_NAME
|
||||
└── upgrade-info.json
|
||||
│ └── <name>
|
||||
│ ├── bin
|
||||
│ │ └── $DAEMON_NAME
|
||||
│ └── upgrade-info.json
|
||||
└── preupgrade.sh (optional)
|
||||
```
|
||||
|
||||
The `cosmovisor/` directory incudes a subdirectory for each version of the application (i.e. `genesis` or `upgrades/<name>`). Within each subdirectory is the application binary (i.e. `bin/$DAEMON_NAME`) and any additional auxiliary files associated with each binary. `current` is a symbolic link to the currently active directory (i.e. `genesis` or `upgrades/<name>`). The `name` variable in `upgrades/<name>` is the lowercased URI-encoded name of the upgrade as specified in the upgrade module plan. Note that the upgrade name path are normalized to be lowercased: for instance, `MyUpgrade` is normalized to `myupgrade`, and its path is `upgrades/myupgrade`.
|
||||
The `cosmovisor/` directory includes a subdirectory for each version of the application (i.e. `genesis` or `upgrades/<name>`). Within each subdirectory is the application binary (i.e. `bin/$DAEMON_NAME`) and any additional auxiliary files associated with each binary. `current` is a symbolic link to the currently active directory (i.e. `genesis` or `upgrades/<name>`). The `name` variable in `upgrades/<name>` is the lowercased URI-encoded name of the upgrade as specified in the upgrade module plan. Note that the upgrade name path are normalized to be lowercased: for instance, `MyUpgrade` is normalized to `myupgrade`, and its path is `upgrades/myupgrade`.
|
||||
|
||||
Please note that `$DAEMON_HOME/cosmovisor` only stores the *application binaries*. The `cosmovisor` binary itself can be stored in any typical location (e.g. `/usr/local/bin`). The application will continue to store its data in the default data directory (e.g. `$HOME/.simapp`) or the data directory specified with the `--home` flag. `$DAEMON_HOME` is independent of the data directory and can be set to any location. If you set `$DAEMON_HOME` to the same directory as the data directory, you will end up with a configuation like the following:
|
||||
Please note that `$DAEMON_HOME/cosmovisor` only stores the *application binaries*. The `cosmovisor` binary itself can be stored in any typical location (e.g. `/usr/local/bin`). The application will continue to store its data in the default data directory (e.g. `$HOME/.simapp`) or the data directory specified with the `--home` flag. `$DAEMON_HOME` is dependent of the data directory and must be set to the same directory as the data directory, you will end up with a configuration like the following:
|
||||
|
||||
```text
|
||||
.simapp
|
||||
@ -179,6 +184,18 @@ When the upgrade mechanism is triggered, `cosmovisor` will:
|
||||
1. if `DAEMON_ALLOW_DOWNLOAD_BINARIES` is enabled, start by auto-downloading a new binary into `cosmovisor/<name>/bin` (where `<name>` is the `upgrade-info.json:name` attribute);
|
||||
2. update the `current` symbolic link to point to the new directory and save `data/upgrade-info.json` to `cosmovisor/current/upgrade-info.json`.
|
||||
|
||||
### Adding Upgrade Binary
|
||||
|
||||
`cosmovisor` has an `add-upgrade` command that allows to easily link a binary to an upgrade. It creates a new folder in `cosmovisor/upgrades/<name>` and copies the provided executable file to `cosmovisor/upgrades/<name>/bin/<DAEMON_NAME>`.
|
||||
|
||||
Using the `--upgrade-height` flag allows to specify at which height the binary should be switched, without going via a gorvernance proposal.
|
||||
This enables support for an emergency coordinated upgrades where the binary must be switched at a specific height, but there is no time to go through a governance proposal.
|
||||
|
||||
:::warning
|
||||
`--upgrade-height` creates an `upgrade-info.json` file. This means if a chain upgrade via governance proposal is executed before the specified height with `--upgrade-height`, the governance proposal will overwrite the `upgrade-info.json` plan created by `add-upgrade --upgrade-height <height>`.
|
||||
Take this into consideration when using `--upgrade-height`.
|
||||
:::
|
||||
|
||||
### 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 automated setup (maybe they are syncing a non-validating fullnode and want to do little maintenance), there is another option.
|
||||
@ -246,31 +263,63 @@ The result will look something like the following: `29139e1381b8177aec909fab9a75
|
||||
|
||||
You can also use `sha512sum` if you would prefer to use longer hashes, or `md5sum` if you would prefer to use broken hashes. Whichever you choose, make sure to set the hash algorithm properly in the checksum argument to the URL.
|
||||
|
||||
### Preparing for an Upgrade
|
||||
|
||||
To prepare for an upgrade, use the `prepare-upgrade` command:
|
||||
|
||||
```shell
|
||||
cosmovisor prepare-upgrade
|
||||
```
|
||||
|
||||
This command performs the following actions:
|
||||
|
||||
1. Retrieves upgrade information directly from the blockchain about the next scheduled upgrade.
|
||||
2. Downloads the new binary specified in the upgrade plan.
|
||||
3. Verifies the binary's checksum (if required by configuration).
|
||||
4. Places the new binary in the appropriate directory for Cosmovisor to use during the upgrade.
|
||||
|
||||
The `prepare-upgrade` command provides detailed logging throughout the process, including:
|
||||
|
||||
* The name and height of the upcoming upgrade
|
||||
* The URL from which the new binary is being downloaded
|
||||
* Confirmation of successful download and verification
|
||||
* The path where the new binary has been placed
|
||||
|
||||
Example output:
|
||||
|
||||
```bash
|
||||
INFO Preparing for upgrade name=v1.0.0 height=1000000
|
||||
INFO Downloading upgrade binary url=https://example.com/binary/v1.0.0?checksum=sha256:339911508de5e20b573ce902c500ee670589073485216bee8b045e853f24bce8
|
||||
INFO Upgrade preparation complete name=v1.0.0 height=1000000
|
||||
```
|
||||
|
||||
*Note: The current way of downloading manually and placing the binary at the right place would still work.*
|
||||
|
||||
## Example: SimApp Upgrade
|
||||
|
||||
The following instructions provide a demonstration of `cosmovisor` using the simulation application (`simapp`) shipped with the Cosmos SDK's source code. The following commands are to be run from within the `cosmos-sdk` repository.
|
||||
|
||||
### Chain Setup
|
||||
|
||||
Let's create a new chain using the `v0.44` version of simapp (the Cosmos SDK demo app):
|
||||
Let's create a new chain using the `v0.47.4` version of simapp (the Cosmos SDK demo app):
|
||||
|
||||
```shell
|
||||
git checkout v0.44.6
|
||||
git checkout v0.47.4
|
||||
make build
|
||||
```
|
||||
|
||||
Clean `~/.simapp` (never do this in a production environment):
|
||||
|
||||
```shell
|
||||
./build/simd unsafe-reset-all
|
||||
./build/simd tendermint unsafe-reset-all
|
||||
```
|
||||
|
||||
Set up app config:
|
||||
|
||||
```shell
|
||||
./build/simd config set client chain-id test
|
||||
./build/simd config set client keyring-backend test
|
||||
./build/simd config set client broadcast-mode sync
|
||||
./build/simd config chain-id test
|
||||
./build/simd config keyring-backend test
|
||||
./build/simd config broadcast-mode sync
|
||||
```
|
||||
|
||||
Initialize the node and overwrite any previous genesis file (never do this in a production environment):
|
||||
@ -279,16 +328,10 @@ Initialize the node and overwrite any previous genesis file (never do this in a
|
||||
./build/simd init test --chain-id test --overwrite
|
||||
```
|
||||
|
||||
Set the minimum gas price to `0stake` in `~/.simapp/config/app.toml`:
|
||||
|
||||
```shell
|
||||
minimum-gas-prices = "0stake"
|
||||
```
|
||||
|
||||
For the sake of this demonstration, amend `voting_period` in `genesis.json` to a reduced time of 20 seconds (`20s`):
|
||||
|
||||
```shell
|
||||
cat <<< $(jq '.app_state.gov.voting_params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
|
||||
cat <<< $(jq '.app_state.gov.params.voting_period = "20s"' $HOME/.simapp/config/genesis.json) > $HOME/.simapp/config/genesis.json
|
||||
```
|
||||
|
||||
Create a validator, and setup genesis transaction:
|
||||
@ -315,24 +358,29 @@ Set the optional environment variable to trigger an automatic app restart:
|
||||
export DAEMON_RESTART_AFTER_UPGRADE=true
|
||||
```
|
||||
|
||||
Create the folder for the genesis binary and copy the `simd` binary:
|
||||
Initialize cosmovisor with the current binary:
|
||||
|
||||
```shell
|
||||
mkdir -p $DAEMON_HOME/cosmovisor/genesis/bin
|
||||
cp ./build/simd $DAEMON_HOME/cosmovisor/genesis/bin
|
||||
cosmovisor init ./build/simd
|
||||
```
|
||||
|
||||
Now you can run cosmovisor with simapp v0.44:
|
||||
Now you can run cosmovisor with simapp v0.47.4:
|
||||
|
||||
```shell
|
||||
cosmovisor run start
|
||||
```
|
||||
|
||||
#### Update App
|
||||
### Update App
|
||||
|
||||
Update app to the latest version (e.g. v0.45).
|
||||
Update app to the latest version (e.g. v0.50.0).
|
||||
|
||||
Next, we can add a migration - which is defined using `x/upgrade` [upgrade plan](https://github.com/cosmos/cosmos-sdk/blob/main/docs/core/upgrade.md) (you may refer to a past version if you are using an older Cosmos SDK release). In a migration we can do any deterministic state change.
|
||||
:::note
|
||||
|
||||
Migration plans are defined using the `x/upgrade` module and described in [In-Place Store Migrations](https://github.com/cosmos/cosmos-sdk/blob/main/docs/learn/advanced/15-upgrade.md). Migrations can perform any deterministic state change.
|
||||
|
||||
The migration plan to upgrade the simapp from v0.47 to v0.50 is defined in `simapp/upgrade.go`.
|
||||
|
||||
:::
|
||||
|
||||
Build the new version `simd` binary:
|
||||
|
||||
@ -349,32 +397,13 @@ The migration name must match the one defined in the migration plan.
|
||||
:::
|
||||
|
||||
```shell
|
||||
mkdir -p $DAEMON_HOME/cosmovisor/upgrades/test1/bin
|
||||
cp ./build/simd $DAEMON_HOME/cosmovisor/upgrades/test1/bin
|
||||
cosmovisor add-upgrade v047-to-v050 ./build/simd
|
||||
```
|
||||
|
||||
Open a new terminal window and submit an upgrade proposal along with a deposit and a vote (these commands must be run within 20 seconds of each other):
|
||||
|
||||
**<= v0.45**:
|
||||
|
||||
```shell
|
||||
./build/simd tx gov submit-proposal software-upgrade test1 --title upgrade --description upgrade --upgrade-height 200 --from validator --yes
|
||||
./build/simd tx gov deposit 1 10000000stake --from validator --yes
|
||||
./build/simd tx gov vote 1 yes --from validator --yes
|
||||
```
|
||||
|
||||
**v0.46, v0.47**:
|
||||
|
||||
```shell
|
||||
./build/simd tx gov submit-legacy-proposal software-upgrade test1 --title upgrade --description upgrade --upgrade-height 200 --from validator --yes
|
||||
./build/simd tx gov deposit 1 10000000stake --from validator --yes
|
||||
./build/simd tx gov vote 1 yes --from validator --yes
|
||||
```
|
||||
|
||||
**>= v0.50+**:
|
||||
|
||||
```shell
|
||||
./build/simd tx upgrade software-upgrade test1 --title upgrade --summary upgrade --upgrade-height 200 --from validator --yes
|
||||
./build/simd tx upgrade software-upgrade v047-to-v050 --title upgrade --summary upgrade --upgrade-height 200 --upgrade-info "{}" --no-validate --from validator --yes
|
||||
./build/simd tx gov deposit 1 10000000stake --from validator --yes
|
||||
./build/simd tx gov vote 1 yes --from validator --yes
|
||||
```
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
# Cosmovisor v1.4.0 Release Notes
|
||||
# Cosmovisor v1.5.0 Release Notes
|
||||
|
||||
* Rename cosmovisor package to `cosmossdk.io/tools/cosmovisor`.
|
||||
See the [CHANGELOG](https://github.com/cosmos/cosmos-sdk/blob/tools/cosmovisor/v1.5.0/tools/cosmovisor/CHANGELOG.md) for details on the changes in v1.5.0.
|
||||
|
||||
## Changelog
|
||||
## Installation instructions
|
||||
|
||||
For more details, please see the [CHANGELOG](https://github.com/cosmos/cosmos-sdk/blob/tools/cosmovisor/v1.4.0/tools/cosmovisor/CHANGELOG.md).
|
||||
```go
|
||||
go install cosmossdk.io/tools/cosmovisor/cmd/cosmovisor@latest
|
||||
```
|
||||
|
||||
@ -2,7 +2,9 @@ package cosmovisor
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@ -10,24 +12,33 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
cverrors "cosmossdk.io/tools/cosmovisor/errors"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
)
|
||||
|
||||
// environment variable names
|
||||
const (
|
||||
EnvHome = "DAEMON_HOME"
|
||||
EnvName = "DAEMON_NAME"
|
||||
EnvDownloadBin = "DAEMON_ALLOW_DOWNLOAD_BINARIES"
|
||||
EnvRestartUpgrade = "DAEMON_RESTART_AFTER_UPGRADE"
|
||||
EnvRestartDelay = "DAEMON_RESTART_DELAY"
|
||||
EnvSkipBackup = "UNSAFE_SKIP_BACKUP"
|
||||
EnvDataBackupPath = "DAEMON_DATA_BACKUP_DIR"
|
||||
EnvInterval = "DAEMON_POLL_INTERVAL"
|
||||
EnvPreupgradeMaxRetries = "DAEMON_PREUPGRADE_MAX_RETRIES"
|
||||
EnvDisableLogs = "COSMOVISOR_DISABLE_LOGS"
|
||||
EnvHome = "DAEMON_HOME"
|
||||
EnvName = "DAEMON_NAME"
|
||||
EnvDownloadBin = "DAEMON_ALLOW_DOWNLOAD_BINARIES"
|
||||
EnvDownloadMustHaveChecksum = "DAEMON_DOWNLOAD_MUST_HAVE_CHECKSUM"
|
||||
EnvRestartUpgrade = "DAEMON_RESTART_AFTER_UPGRADE"
|
||||
EnvRestartDelay = "DAEMON_RESTART_DELAY"
|
||||
EnvShutdownGrace = "DAEMON_SHUTDOWN_GRACE"
|
||||
EnvSkipBackup = "UNSAFE_SKIP_BACKUP"
|
||||
EnvDataBackupPath = "DAEMON_DATA_BACKUP_DIR"
|
||||
EnvInterval = "DAEMON_POLL_INTERVAL"
|
||||
EnvPreupgradeMaxRetries = "DAEMON_PREUPGRADE_MAX_RETRIES"
|
||||
EnvGRPCAddress = "DAEMON_GRPC_ADDRESS"
|
||||
EnvDisableLogs = "COSMOVISOR_DISABLE_LOGS"
|
||||
EnvColorLogs = "COSMOVISOR_COLOR_LOGS"
|
||||
EnvTimeFormatLogs = "COSMOVISOR_TIMEFORMAT_LOGS"
|
||||
EnvCustomPreupgrade = "COSMOVISOR_CUSTOM_PREUPGRADE"
|
||||
EnvDisableRecase = "COSMOVISOR_DISABLE_RECASE"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -35,23 +46,30 @@ const (
|
||||
genesisDir = "genesis"
|
||||
upgradesDir = "upgrades"
|
||||
currentLink = "current"
|
||||
)
|
||||
|
||||
// must be the same as x/upgrade/types.UpgradeInfoFilename
|
||||
const defaultFilename = "upgrade-info.json"
|
||||
cfgFileName = "config"
|
||||
cfgExtension = "toml"
|
||||
)
|
||||
|
||||
// Config is the information passed in to control the daemon
|
||||
type Config struct {
|
||||
Home string
|
||||
Name string
|
||||
AllowDownloadBinaries bool
|
||||
RestartAfterUpgrade bool
|
||||
RestartDelay time.Duration
|
||||
PollInterval time.Duration
|
||||
UnsafeSkipBackup bool
|
||||
DataBackupPath string
|
||||
PreupgradeMaxRetries int
|
||||
DisableLogs bool
|
||||
Home string `toml:"daemon_home" mapstructure:"daemon_home"`
|
||||
Name string `toml:"daemon_name" mapstructure:"daemon_name"`
|
||||
AllowDownloadBinaries bool `toml:"daemon_allow_download_binaries" mapstructure:"daemon_allow_download_binaries" default:"false"`
|
||||
DownloadMustHaveChecksum bool `toml:"daemon_download_must_have_checksum" mapstructure:"daemon_download_must_have_checksum" default:"false"`
|
||||
RestartAfterUpgrade bool `toml:"daemon_restart_after_upgrade" mapstructure:"daemon_restart_after_upgrade" default:"true"`
|
||||
RestartDelay time.Duration `toml:"daemon_restart_delay" mapstructure:"daemon_restart_delay"`
|
||||
ShutdownGrace time.Duration `toml:"daemon_shutdown_grace" mapstructure:"daemon_shutdown_grace"`
|
||||
PollInterval time.Duration `toml:"daemon_poll_interval" mapstructure:"daemon_poll_interval" default:"300ms"`
|
||||
UnsafeSkipBackup bool `toml:"unsafe_skip_backup" mapstructure:"unsafe_skip_backup" default:"false"`
|
||||
DataBackupPath string `toml:"daemon_data_backup_dir" mapstructure:"daemon_data_backup_dir"`
|
||||
PreUpgradeMaxRetries int `toml:"daemon_preupgrade_max_retries" mapstructure:"daemon_preupgrade_max_retries" default:"0"`
|
||||
GRPCAddress string `toml:"daemon_grpc_address" mapstructure:"daemon_grpc_address"`
|
||||
DisableLogs bool `toml:"cosmovisor_disable_logs" mapstructure:"cosmovisor_disable_logs" default:"false"`
|
||||
ColorLogs bool `toml:"cosmovisor_color_logs" mapstructure:"cosmovisor_color_logs" default:"true"`
|
||||
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"`
|
||||
|
||||
// currently running upgrade
|
||||
currentUpgrade upgradetypes.Plan
|
||||
@ -62,6 +80,11 @@ func (cfg *Config) Root() string {
|
||||
return filepath.Join(cfg.Home, rootName)
|
||||
}
|
||||
|
||||
// DefaultCfgPath returns the default path to the configuration file.
|
||||
func (cfg *Config) DefaultCfgPath() string {
|
||||
return filepath.Join(cfg.Root(), cfgFileName+"."+cfgExtension)
|
||||
}
|
||||
|
||||
// GenesisBin is the path to the genesis binary - must be in place to start manager
|
||||
func (cfg *Config) GenesisBin() string {
|
||||
return filepath.Join(cfg.Root(), genesisDir, "bin", cfg.Name)
|
||||
@ -85,19 +108,29 @@ func (cfg *Config) BaseUpgradeDir() string {
|
||||
|
||||
// UpgradeInfoFilePath is the expected upgrade-info filename created by `x/upgrade/keeper`.
|
||||
func (cfg *Config) UpgradeInfoFilePath() string {
|
||||
return filepath.Join(cfg.Home, "data", defaultFilename)
|
||||
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"
|
||||
}
|
||||
|
||||
// SymLinkToGenesis creates a symbolic link from "./current" to the genesis directory.
|
||||
func (cfg *Config) SymLinkToGenesis() (string, error) {
|
||||
genesis := filepath.Join(cfg.Root(), genesisDir)
|
||||
link := filepath.Join(cfg.Root(), currentLink)
|
||||
|
||||
if err := os.Symlink(genesis, link); err != nil {
|
||||
// workdir is set to cosmovisor directory so relative
|
||||
// symlinks are getting resolved correctly
|
||||
if err := os.Symlink(genesisDir, currentLink); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// and return the genesis binary
|
||||
return cfg.GenesisBin(), nil
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// WaitRestartDelay will block and wait until the RestartDelay has elapsed.
|
||||
@ -111,38 +144,82 @@ func (cfg *Config) WaitRestartDelay() {
|
||||
// This will resolve the symlink to the underlying directory to make it easier to debug
|
||||
func (cfg *Config) CurrentBin() (string, error) {
|
||||
cur := filepath.Join(cfg.Root(), currentLink)
|
||||
|
||||
// if nothing here, fallback to genesis
|
||||
info, err := os.Lstat(cur)
|
||||
if err != nil {
|
||||
// Create symlink to the genesis
|
||||
return cfg.SymLinkToGenesis()
|
||||
}
|
||||
// if it is there, ensure it is a symlink
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
info, err := os.Lstat(cur)
|
||||
if err != nil || (info.Mode()&os.ModeSymlink == 0) {
|
||||
// Create symlink to the genesis
|
||||
return cfg.SymLinkToGenesis()
|
||||
}
|
||||
|
||||
// resolve it
|
||||
dest, err := os.Readlink(cur)
|
||||
res, err := filepath.EvalSymlinks(cur)
|
||||
if err != nil {
|
||||
// Create symlink to the genesis
|
||||
return cfg.SymLinkToGenesis()
|
||||
}
|
||||
|
||||
// and return the binary
|
||||
binpath := filepath.Join(dest, "bin", cfg.Name)
|
||||
binpath := filepath.Join(res, "bin", cfg.Name)
|
||||
|
||||
return binpath, nil
|
||||
}
|
||||
|
||||
// GetConfigFromFile will read the configuration from the config file at the given path.
|
||||
// If the file path is not provided, it will read the configuration from the ENV variables.
|
||||
// If a file path is provided and ENV variables are set, they will override the values in the file.
|
||||
func GetConfigFromFile(filePath string) (*Config, error) {
|
||||
if filePath == "" {
|
||||
return GetConfigFromEnv(false)
|
||||
}
|
||||
|
||||
// ensure the file exist
|
||||
if _, err := os.Stat(filePath); err != nil {
|
||||
return nil, fmt.Errorf("config not found: at %s : %w", filePath, err)
|
||||
}
|
||||
|
||||
v := viper.New()
|
||||
// read the configuration from the file
|
||||
v.SetConfigFile(filePath)
|
||||
// load the env variables
|
||||
// if the env variable is set, it will override the value provided by the config
|
||||
v.AutomaticEnv()
|
||||
|
||||
if err := v.ReadInConfig(); err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
cfg := &Config{}
|
||||
if err := v.Unmarshal(cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal configuration: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
errs []error
|
||||
)
|
||||
|
||||
if cfg.TimeFormatLogs, err = getTimeFormatOption(cfg.TimeFormatLogs); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
errs = append(errs, cfg.validate()...)
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// GetConfigFromEnv will read the environmental variables into a config
|
||||
// and then validate it is reasonable
|
||||
func GetConfigFromEnv() (*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),
|
||||
Home: os.Getenv(EnvHome),
|
||||
Name: os.Getenv(EnvName),
|
||||
DataBackupPath: os.Getenv(EnvDataBackupPath),
|
||||
CustomPreUpgrade: os.Getenv(EnvCustomPreupgrade),
|
||||
}
|
||||
|
||||
if cfg.DataBackupPath == "" {
|
||||
@ -150,16 +227,28 @@ func GetConfigFromEnv() (*Config, error) {
|
||||
}
|
||||
|
||||
var err error
|
||||
if cfg.AllowDownloadBinaries, err = booleanOption(EnvDownloadBin, false); err != nil {
|
||||
if cfg.AllowDownloadBinaries, err = BooleanOption(EnvDownloadBin, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.RestartAfterUpgrade, err = booleanOption(EnvRestartUpgrade, true); err != nil {
|
||||
if cfg.DownloadMustHaveChecksum, err = BooleanOption(EnvDownloadMustHaveChecksum, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.UnsafeSkipBackup, err = booleanOption(EnvSkipBackup, false); err != nil {
|
||||
if cfg.RestartAfterUpgrade, err = BooleanOption(EnvRestartUpgrade, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.DisableLogs, err = booleanOption(EnvDisableLogs, false); err != nil {
|
||||
if cfg.UnsafeSkipBackup, err = BooleanOption(EnvSkipBackup, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.DisableLogs, err = BooleanOption(EnvDisableLogs, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.ColorLogs, err = BooleanOption(EnvColorLogs, true); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.TimeFormatLogs, err = TimeFormatOptionFromEnv(EnvTimeFormatLogs, time.Kitchen); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
if cfg.DisableRecase, err = BooleanOption(EnvDisableRecase, false); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
@ -186,19 +275,51 @@ func GetConfigFromEnv() (*Config, error) {
|
||||
}
|
||||
}
|
||||
|
||||
envPreupgradeMaxRetriesVal := os.Getenv(EnvPreupgradeMaxRetries)
|
||||
if cfg.PreupgradeMaxRetries, err = strconv.Atoi(envPreupgradeMaxRetriesVal); err != nil && envPreupgradeMaxRetriesVal != "" {
|
||||
cfg.ShutdownGrace = 0 // default value but makes it explicit
|
||||
shutdownGrace := os.Getenv(EnvShutdownGrace)
|
||||
if shutdownGrace != "" {
|
||||
val, err := parseEnvDuration(shutdownGrace)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("invalid: %s: %w", EnvShutdownGrace, err))
|
||||
} else {
|
||||
cfg.ShutdownGrace = val
|
||||
}
|
||||
}
|
||||
|
||||
envPreUpgradeMaxRetriesVal := os.Getenv(EnvPreupgradeMaxRetries)
|
||||
if cfg.PreUpgradeMaxRetries, err = strconv.Atoi(envPreUpgradeMaxRetriesVal); err != nil && envPreUpgradeMaxRetriesVal != "" {
|
||||
errs = append(errs, fmt.Errorf("%s could not be parsed to int: %w", EnvPreupgradeMaxRetries, err))
|
||||
}
|
||||
|
||||
errs = append(errs, cfg.validate()...)
|
||||
|
||||
if len(errs) > 0 {
|
||||
return nil, cverrors.FlattenErrors(errs...)
|
||||
cfg.GRPCAddress = os.Getenv(EnvGRPCAddress)
|
||||
if cfg.GRPCAddress == "" {
|
||||
cfg.GRPCAddress = "localhost:9090"
|
||||
}
|
||||
|
||||
if !skipValidate {
|
||||
errs = append(errs, cfg.validate()...)
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (cfg *Config) Logger(dst io.Writer) log.Logger {
|
||||
var logger log.Logger
|
||||
|
||||
if cfg.DisableLogs {
|
||||
logger = log.NewNopLogger()
|
||||
} else {
|
||||
logger = log.NewLogger(dst,
|
||||
log.ColorOption(cfg.ColorLogs),
|
||||
log.TimeFormatOption(cfg.TimeFormatLogs)).With(log.ModuleKey, "cosmovisor")
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
func parseEnvDuration(input string) (time.Duration, error) {
|
||||
duration, err := time.ParseDuration(input)
|
||||
if err != nil {
|
||||
@ -206,26 +327,12 @@ func parseEnvDuration(input string) (time.Duration, error) {
|
||||
}
|
||||
|
||||
if duration <= 0 {
|
||||
return 0, fmt.Errorf("must be greater than 0")
|
||||
return 0, errors.New("must be greater than 0")
|
||||
}
|
||||
|
||||
return duration, nil
|
||||
}
|
||||
|
||||
// LogConfigOrError logs either the config details or the error.
|
||||
func LogConfigOrError(logger log.Logger, cfg *Config, err error) {
|
||||
if cfg == nil && err == nil {
|
||||
return
|
||||
}
|
||||
logger.Info("configuration:")
|
||||
switch {
|
||||
case err != nil:
|
||||
cverrors.LogErrors(logger, "configuration errors found", err)
|
||||
case cfg != nil:
|
||||
logger.Info(cfg.DetailString())
|
||||
}
|
||||
}
|
||||
|
||||
// validate returns an error if this config is invalid.
|
||||
// it enforces Home/cosmovisor is a valid directory and exists,
|
||||
// and that Name is set
|
||||
@ -285,24 +392,23 @@ func (cfg *Config) SetCurrentUpgrade(u upgradetypes.Plan) (rerr error) {
|
||||
}
|
||||
|
||||
// set a symbolic link
|
||||
link := filepath.Join(cfg.Root(), currentLink)
|
||||
safeName := url.PathEscape(u.Name)
|
||||
upgrade := filepath.Join(cfg.Root(), upgradesDir, safeName)
|
||||
upgrade := filepath.Join(upgradesDir, safeName)
|
||||
|
||||
// remove link if it exists
|
||||
if _, err := os.Stat(link); err == nil {
|
||||
if err := os.Remove(link); err != nil {
|
||||
if _, err := os.Stat(currentLink); err == nil {
|
||||
if err := os.Remove(currentLink); err != nil {
|
||||
return fmt.Errorf("failed to remove existing link: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// point to the new directory
|
||||
if err := os.Symlink(upgrade, link); err != nil {
|
||||
if err := os.Symlink(upgrade, currentLink); err != nil {
|
||||
return fmt.Errorf("creating current symlink: %w", err)
|
||||
}
|
||||
|
||||
cfg.currentUpgrade = u
|
||||
f, err := os.Create(filepath.Join(upgrade, upgradetypes.UpgradeInfoFilename))
|
||||
f, err := os.Create(filepath.Join(cfg.Root(), upgrade, upgradetypes.UpgradeInfoFilename))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -321,6 +427,7 @@ func (cfg *Config) SetCurrentUpgrade(u upgradetypes.Plan) (rerr error) {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpgradeInfo returns the current upgrade info
|
||||
func (cfg *Config) UpgradeInfo() (upgradetypes.Plan, error) {
|
||||
if cfg.currentUpgrade.Name != "" {
|
||||
return cfg.currentUpgrade, nil
|
||||
@ -347,8 +454,8 @@ returnError:
|
||||
return cfg.currentUpgrade, fmt.Errorf("failed to read %q: %w", filename, err)
|
||||
}
|
||||
|
||||
// checks and validates env option
|
||||
func booleanOption(name string, defaultVal bool) (bool, error) {
|
||||
// BooleanOption checks and validate env option
|
||||
func BooleanOption(name string, defaultVal bool) (bool, error) {
|
||||
p := strings.ToLower(os.Getenv(name))
|
||||
switch p {
|
||||
case "":
|
||||
@ -361,19 +468,99 @@ func booleanOption(name string, defaultVal bool) (bool, error) {
|
||||
return false, fmt.Errorf("env variable %q must have a boolean value (\"true\" or \"false\"), got %q", name, p)
|
||||
}
|
||||
|
||||
// TimeFormatOptionFromEnv checks and validates the time format option
|
||||
func TimeFormatOptionFromEnv(env, defaultVal string) (string, error) {
|
||||
val, set := os.LookupEnv(env)
|
||||
if !set {
|
||||
return defaultVal, nil
|
||||
}
|
||||
|
||||
return getTimeFormatOption(val)
|
||||
}
|
||||
|
||||
func getTimeFormatOption(val string) (string, error) {
|
||||
switch val {
|
||||
case "layout":
|
||||
return time.Layout, nil
|
||||
case "ansic":
|
||||
return time.ANSIC, nil
|
||||
case "unixdate":
|
||||
return time.UnixDate, nil
|
||||
case "rubydate":
|
||||
return time.RubyDate, nil
|
||||
case "rfc822":
|
||||
return time.RFC822, nil
|
||||
case "rfc822z":
|
||||
return time.RFC822Z, nil
|
||||
case "rfc850":
|
||||
return time.RFC850, nil
|
||||
case "rfc1123":
|
||||
return time.RFC1123, nil
|
||||
case "rfc1123z":
|
||||
return time.RFC1123Z, nil
|
||||
case "rfc3339":
|
||||
return time.RFC3339, nil
|
||||
case "rfc3339nano":
|
||||
return time.RFC3339Nano, nil
|
||||
case "kitchen":
|
||||
return time.Kitchen, nil
|
||||
case "":
|
||||
return "", nil
|
||||
}
|
||||
return "", fmt.Errorf("env variable %q must have a timeformat value (\"layout|ansic|unixdate|rubydate|rfc822|rfc822z|rfc850|rfc1123|rfc1123z|rfc3339|rfc3339nano|kitchen\"), got %q", EnvTimeFormatLogs, val)
|
||||
}
|
||||
|
||||
// ValueToTimeFormatOption converts the time format option to the env value
|
||||
func ValueToTimeFormatOption(format string) string {
|
||||
switch format {
|
||||
case time.Layout:
|
||||
return "layout"
|
||||
case time.ANSIC:
|
||||
return "ansic"
|
||||
case time.UnixDate:
|
||||
return "unixdate"
|
||||
case time.RubyDate:
|
||||
return "rubydate"
|
||||
case time.RFC822:
|
||||
return "rfc822"
|
||||
case time.RFC822Z:
|
||||
return "rfc822z"
|
||||
case time.RFC850:
|
||||
return "rfc850"
|
||||
case time.RFC1123:
|
||||
return "rfc1123"
|
||||
case time.RFC1123Z:
|
||||
return "rfc1123z"
|
||||
case time.RFC3339:
|
||||
return "rfc3339"
|
||||
case time.RFC3339Nano:
|
||||
return "rfc3339nano"
|
||||
case time.Kitchen:
|
||||
return "kitchen"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// DetailString returns a multi-line string with details about this config.
|
||||
func (cfg Config) DetailString() string {
|
||||
configEntries := []struct{ name, value string }{
|
||||
{EnvHome, cfg.Home},
|
||||
{EnvName, cfg.Name},
|
||||
{EnvDownloadBin, fmt.Sprintf("%t", cfg.AllowDownloadBinaries)},
|
||||
{EnvDownloadMustHaveChecksum, fmt.Sprintf("%t", cfg.DownloadMustHaveChecksum)},
|
||||
{EnvRestartUpgrade, fmt.Sprintf("%t", cfg.RestartAfterUpgrade)},
|
||||
{EnvRestartDelay, cfg.RestartDelay.String()},
|
||||
{EnvShutdownGrace, cfg.ShutdownGrace.String()},
|
||||
{EnvInterval, cfg.PollInterval.String()},
|
||||
{EnvSkipBackup, fmt.Sprintf("%t", cfg.UnsafeSkipBackup)},
|
||||
{EnvDataBackupPath, cfg.DataBackupPath},
|
||||
{EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreupgradeMaxRetries)},
|
||||
{EnvPreupgradeMaxRetries, fmt.Sprintf("%d", cfg.PreUpgradeMaxRetries)},
|
||||
{EnvDisableLogs, fmt.Sprintf("%t", cfg.DisableLogs)},
|
||||
{EnvColorLogs, fmt.Sprintf("%t", cfg.ColorLogs)},
|
||||
{EnvTimeFormatLogs, cfg.TimeFormatLogs},
|
||||
{EnvCustomPreupgrade, cfg.CustomPreUpgrade},
|
||||
{EnvDisableRecase, fmt.Sprintf("%t", cfg.DisableRecase)},
|
||||
}
|
||||
|
||||
derivedEntries := []struct{ name, value string }{
|
||||
@ -402,3 +589,48 @@ func (cfg Config) DetailString() string {
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// Export exports the configuration to a file at the cosmovisor root directory.
|
||||
func (cfg Config) Export() (string, error) {
|
||||
// always use the default path
|
||||
path := filepath.Clean(cfg.DefaultCfgPath())
|
||||
|
||||
// check if config file already exists ask user if they want to overwrite it
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
// ask user if they want to overwrite the file
|
||||
if !askForConfirmation(fmt.Sprintf("file %s already exists, do you want to overwrite it?", path)) {
|
||||
cfg.Logger(os.Stdout).Info("file already exists, not overriding")
|
||||
return path, nil
|
||||
}
|
||||
}
|
||||
|
||||
// create the file
|
||||
file, err := os.Create(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create configuration file: %w", err)
|
||||
}
|
||||
|
||||
// convert the time value to its format option
|
||||
cfg.TimeFormatLogs = ValueToTimeFormatOption(cfg.TimeFormatLogs)
|
||||
|
||||
defer file.Close()
|
||||
|
||||
// write the configuration to the file
|
||||
err = toml.NewEncoder(file).Encode(cfg)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to encode configuration: %w", err)
|
||||
}
|
||||
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func askForConfirmation(str string) bool {
|
||||
var response string
|
||||
fmt.Printf("%s [y/n]: ", str)
|
||||
_, err := fmt.Scanln(&response)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.ToLower(response) == "y"
|
||||
}
|
||||
|
||||
@ -1,21 +1,17 @@
|
||||
package cosmovisor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/tools/cosmovisor/errors"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
)
|
||||
|
||||
@ -29,31 +25,48 @@ func TestArgsTestSuite(t *testing.T) {
|
||||
|
||||
// cosmovisorEnv are the string values of environment variables used to configure Cosmovisor.
|
||||
type cosmovisorEnv struct {
|
||||
Home string
|
||||
Name string
|
||||
DownloadBin string
|
||||
RestartUpgrade string
|
||||
RestartDelay string
|
||||
SkipBackup string
|
||||
DataBackupPath string
|
||||
Interval string
|
||||
PreupgradeMaxRetries string
|
||||
DisableLogs string
|
||||
Home string
|
||||
Name string
|
||||
DownloadBin string
|
||||
DownloadMustHaveChecksum string
|
||||
RestartUpgrade string
|
||||
RestartDelay string
|
||||
SkipBackup string
|
||||
DataBackupPath string
|
||||
Interval string
|
||||
PreupgradeMaxRetries string
|
||||
DisableLogs string
|
||||
ColorLogs string
|
||||
TimeFormatLogs string
|
||||
CustomPreupgrade string
|
||||
DisableRecase string
|
||||
ShutdownGrace string
|
||||
}
|
||||
|
||||
type envMap struct {
|
||||
val string
|
||||
allowEmpty bool
|
||||
}
|
||||
|
||||
// ToMap creates a map of the cosmovisorEnv where the keys are the env var names.
|
||||
func (c cosmovisorEnv) ToMap() map[string]string {
|
||||
return map[string]string{
|
||||
EnvHome: c.Home,
|
||||
EnvName: c.Name,
|
||||
EnvDownloadBin: c.DownloadBin,
|
||||
EnvRestartUpgrade: c.RestartUpgrade,
|
||||
EnvRestartDelay: c.RestartDelay,
|
||||
EnvSkipBackup: c.SkipBackup,
|
||||
EnvDataBackupPath: c.DataBackupPath,
|
||||
EnvInterval: c.Interval,
|
||||
EnvPreupgradeMaxRetries: c.PreupgradeMaxRetries,
|
||||
EnvDisableLogs: c.DisableLogs,
|
||||
func (c cosmovisorEnv) ToMap() map[string]envMap {
|
||||
return map[string]envMap{
|
||||
EnvHome: {val: c.Home, allowEmpty: false},
|
||||
EnvName: {val: c.Name, allowEmpty: false},
|
||||
EnvDownloadBin: {val: c.DownloadBin, allowEmpty: false},
|
||||
EnvDownloadMustHaveChecksum: {val: c.DownloadMustHaveChecksum, allowEmpty: false},
|
||||
EnvRestartUpgrade: {val: c.RestartUpgrade, allowEmpty: false},
|
||||
EnvRestartDelay: {val: c.RestartDelay, allowEmpty: false},
|
||||
EnvShutdownGrace: {val: c.ShutdownGrace, allowEmpty: false},
|
||||
EnvSkipBackup: {val: c.SkipBackup, allowEmpty: false},
|
||||
EnvDataBackupPath: {val: c.DataBackupPath, allowEmpty: false},
|
||||
EnvInterval: {val: c.Interval, allowEmpty: false},
|
||||
EnvPreupgradeMaxRetries: {val: c.PreupgradeMaxRetries, allowEmpty: false},
|
||||
EnvDisableLogs: {val: c.DisableLogs, allowEmpty: false},
|
||||
EnvColorLogs: {val: c.ColorLogs, allowEmpty: false},
|
||||
EnvTimeFormatLogs: {val: c.TimeFormatLogs, allowEmpty: true},
|
||||
EnvCustomPreupgrade: {val: c.CustomPreupgrade, allowEmpty: true},
|
||||
EnvDisableRecase: {val: c.DisableRecase, allowEmpty: true},
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,10 +79,14 @@ func (c *cosmovisorEnv) Set(envVar, envVal string) {
|
||||
c.Name = envVal
|
||||
case EnvDownloadBin:
|
||||
c.DownloadBin = envVal
|
||||
case EnvDownloadMustHaveChecksum:
|
||||
c.DownloadMustHaveChecksum = envVal
|
||||
case EnvRestartUpgrade:
|
||||
c.RestartUpgrade = envVal
|
||||
case EnvRestartDelay:
|
||||
c.RestartDelay = envVal
|
||||
case EnvShutdownGrace:
|
||||
c.ShutdownGrace = envVal
|
||||
case EnvSkipBackup:
|
||||
c.SkipBackup = envVal
|
||||
case EnvDataBackupPath:
|
||||
@ -80,8 +97,16 @@ func (c *cosmovisorEnv) Set(envVar, envVal string) {
|
||||
c.PreupgradeMaxRetries = envVal
|
||||
case EnvDisableLogs:
|
||||
c.DisableLogs = envVal
|
||||
case EnvColorLogs:
|
||||
c.ColorLogs = envVal
|
||||
case EnvTimeFormatLogs:
|
||||
c.TimeFormatLogs = envVal
|
||||
case EnvCustomPreupgrade:
|
||||
c.CustomPreupgrade = envVal
|
||||
case EnvDisableRecase:
|
||||
c.DisableRecase = envVal
|
||||
default:
|
||||
panic(fmt.Errorf("Unknown environment variable [%s]. Ccannot set field to [%s]. ", envVar, envVal))
|
||||
panic(fmt.Errorf("Unknown environment variable [%s]. Cannot set field to [%s]. ", envVar, envVal))
|
||||
}
|
||||
}
|
||||
|
||||
@ -103,16 +128,16 @@ func (s *argsTestSuite) clearEnv() *cosmovisorEnv {
|
||||
// setEnv sets environment variables to the values provided.
|
||||
// If t is not nil, and there's a problem, the test will fail immediately.
|
||||
// If t is nil, problems will just be logged using s.T().
|
||||
func (s *argsTestSuite) setEnv(t *testing.T, env *cosmovisorEnv) {
|
||||
func (s *argsTestSuite) setEnv(t *testing.T, env *cosmovisorEnv) { //nolint:thelper // false positive
|
||||
if t == nil {
|
||||
s.T().Logf("Restoring environment variables.")
|
||||
}
|
||||
for envVar, envVal := range env.ToMap() {
|
||||
var err error
|
||||
var msg string
|
||||
if len(envVal) != 0 {
|
||||
err = os.Setenv(envVar, envVal)
|
||||
msg = fmt.Sprintf("setting %s to %s", envVar, envVal)
|
||||
if len(envVal.val) != 0 || envVal.allowEmpty {
|
||||
err = os.Setenv(envVar, envVal.val)
|
||||
msg = fmt.Sprintf("setting %s to %s", envVar, envVal.val)
|
||||
} else {
|
||||
err = os.Unsetenv(envVar)
|
||||
msg = fmt.Sprintf("unsetting %s", envVar)
|
||||
@ -284,7 +309,7 @@ func (s *argsTestSuite) TestBooleanOption() {
|
||||
name := "COSMOVISOR_TEST_VAL"
|
||||
|
||||
check := func(def, expected, isErr bool, msg string) {
|
||||
v, err := booleanOption(name, def)
|
||||
v, err := BooleanOption(name, def)
|
||||
if isErr {
|
||||
s.Require().Error(err)
|
||||
return
|
||||
@ -318,24 +343,77 @@ func (s *argsTestSuite) TestBooleanOption() {
|
||||
check(false, true, false, "should handle true value case not sensitive")
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestTimeFormat() {
|
||||
initialEnv := s.clearEnv()
|
||||
defer s.setEnv(nil, initialEnv)
|
||||
|
||||
name := "COSMOVISOR_TEST_VAL"
|
||||
|
||||
check := func(def, expected string, isErr bool, msg string) {
|
||||
v, err := TimeFormatOptionFromEnv(name, def)
|
||||
if isErr {
|
||||
s.Require().Error(err)
|
||||
return
|
||||
}
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(expected, v, msg)
|
||||
}
|
||||
|
||||
os.Unsetenv(name)
|
||||
check(time.Kitchen, time.Kitchen, false, "should correctly set default value")
|
||||
|
||||
os.Setenv(name, "")
|
||||
check(time.Kitchen, "", false, "should correctly set to a none")
|
||||
|
||||
os.Setenv(name, "wrong")
|
||||
check(time.Kitchen, "", true, "should error on wrong value")
|
||||
|
||||
os.Setenv(name, "layout")
|
||||
check(time.Kitchen, time.Layout, false, "should handle layout value")
|
||||
os.Setenv(name, "ansic")
|
||||
check(time.Kitchen, time.ANSIC, false, "should handle ansic value")
|
||||
os.Setenv(name, "unixdate")
|
||||
check(time.Kitchen, time.UnixDate, false, "should handle unixdate value")
|
||||
os.Setenv(name, "rubydate")
|
||||
check(time.Kitchen, time.RubyDate, false, "should handle rubydate value")
|
||||
os.Setenv(name, "rfc822")
|
||||
check(time.Kitchen, time.RFC822, false, "should handle rfc822 value")
|
||||
os.Setenv(name, "rfc822z")
|
||||
check(time.Kitchen, time.RFC822Z, false, "should handle rfc822z value")
|
||||
os.Setenv(name, "rfc850")
|
||||
check(time.Kitchen, time.RFC850, false, "should handle rfc850 value")
|
||||
os.Setenv(name, "rfc1123")
|
||||
check(time.Kitchen, time.RFC1123, false, "should handle rfc1123 value")
|
||||
os.Setenv(name, "rfc1123z")
|
||||
check(time.Kitchen, time.RFC1123Z, false, "should handle rfc1123z value")
|
||||
os.Setenv(name, "rfc3339")
|
||||
check(time.Kitchen, time.RFC3339, false, "should handle rfc3339 value")
|
||||
os.Setenv(name, "rfc3339nano")
|
||||
check(time.Kitchen, time.RFC3339Nano, false, "should handle rfc3339nano value")
|
||||
os.Setenv(name, "kitchen")
|
||||
check(time.Kitchen, time.Kitchen, false, "should handle kitchen value")
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestDetailString() {
|
||||
home := "/home"
|
||||
name := "test-name"
|
||||
allowDownloadBinaries := true
|
||||
downloadMustHaveChecksum := true
|
||||
restartAfterUpgrade := true
|
||||
pollInterval := 406 * time.Millisecond
|
||||
unsafeSkipBackup := false
|
||||
dataBackupPath := "/home"
|
||||
preupgradeMaxRetries := 8
|
||||
cfg := &Config{
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: allowDownloadBinaries,
|
||||
RestartAfterUpgrade: restartAfterUpgrade,
|
||||
PollInterval: pollInterval,
|
||||
UnsafeSkipBackup: unsafeSkipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreupgradeMaxRetries: preupgradeMaxRetries,
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: allowDownloadBinaries,
|
||||
DownloadMustHaveChecksum: downloadMustHaveChecksum,
|
||||
RestartAfterUpgrade: restartAfterUpgrade,
|
||||
PollInterval: pollInterval,
|
||||
UnsafeSkipBackup: unsafeSkipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreUpgradeMaxRetries: preupgradeMaxRetries,
|
||||
}
|
||||
|
||||
expectedPieces := []string{
|
||||
@ -343,11 +421,15 @@ func (s *argsTestSuite) TestDetailString() {
|
||||
fmt.Sprintf("%s: %s", EnvHome, home),
|
||||
fmt.Sprintf("%s: %s", EnvName, name),
|
||||
fmt.Sprintf("%s: %t", EnvDownloadBin, allowDownloadBinaries),
|
||||
fmt.Sprintf("%s: %t", EnvDownloadMustHaveChecksum, downloadMustHaveChecksum),
|
||||
fmt.Sprintf("%s: %t", EnvRestartUpgrade, restartAfterUpgrade),
|
||||
fmt.Sprintf("%s: %s", EnvInterval, pollInterval),
|
||||
fmt.Sprintf("%s: %t", EnvSkipBackup, unsafeSkipBackup),
|
||||
fmt.Sprintf("%s: %s", EnvDataBackupPath, home),
|
||||
fmt.Sprintf("%s: %d", EnvPreupgradeMaxRetries, preupgradeMaxRetries),
|
||||
fmt.Sprintf("%s: %t", EnvDisableLogs, cfg.DisableLogs),
|
||||
fmt.Sprintf("%s: %t", EnvColorLogs, cfg.ColorLogs),
|
||||
fmt.Sprintf("%s: %s", EnvTimeFormatLogs, cfg.TimeFormatLogs),
|
||||
"Derived Values:",
|
||||
fmt.Sprintf("Root Dir: %s", home),
|
||||
fmt.Sprintf("Upgrade Dir: %s", home),
|
||||
@ -363,6 +445,43 @@ func (s *argsTestSuite) TestDetailString() {
|
||||
}
|
||||
}
|
||||
|
||||
var newConfig = func(
|
||||
home, name string,
|
||||
downloadBin bool,
|
||||
downloadMustHaveChecksum bool,
|
||||
restartUpgrade bool,
|
||||
restartDelay int,
|
||||
skipBackup bool,
|
||||
dataBackupPath string,
|
||||
interval, preupgradeMaxRetries int,
|
||||
grpcAddress string,
|
||||
disableLogs, colorLogs bool,
|
||||
timeFormatLogs string,
|
||||
customPreUpgrade string,
|
||||
disableRecase bool,
|
||||
shutdownGrace int,
|
||||
) *Config {
|
||||
return &Config{
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: downloadBin,
|
||||
DownloadMustHaveChecksum: downloadMustHaveChecksum,
|
||||
RestartAfterUpgrade: restartUpgrade,
|
||||
RestartDelay: time.Millisecond * time.Duration(restartDelay),
|
||||
PollInterval: time.Millisecond * time.Duration(interval),
|
||||
UnsafeSkipBackup: skipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
GRPCAddress: grpcAddress,
|
||||
PreUpgradeMaxRetries: preupgradeMaxRetries,
|
||||
DisableLogs: disableLogs,
|
||||
ColorLogs: colorLogs,
|
||||
TimeFormatLogs: timeFormatLogs,
|
||||
CustomPreUpgrade: customPreUpgrade,
|
||||
DisableRecase: disableRecase,
|
||||
ShutdownGrace: time.Duration(shutdownGrace),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
initialEnv := s.clearEnv()
|
||||
defer s.setEnv(nil, initialEnv)
|
||||
@ -371,21 +490,6 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
absPath, perr := filepath.Abs(relPath)
|
||||
s.Require().NoError(perr)
|
||||
|
||||
newConfig := func(home, name string, downloadBin, restartUpgrade bool, restartDelay int, skipBackup bool, dataBackupPath string, interval, preupgradeMaxRetries int, disableLogs bool) *Config {
|
||||
return &Config{
|
||||
Home: home,
|
||||
Name: name,
|
||||
AllowDownloadBinaries: downloadBin,
|
||||
RestartAfterUpgrade: restartUpgrade,
|
||||
RestartDelay: time.Millisecond * time.Duration(restartDelay),
|
||||
PollInterval: time.Millisecond * time.Duration(interval),
|
||||
UnsafeSkipBackup: skipBackup,
|
||||
DataBackupPath: dataBackupPath,
|
||||
PreupgradeMaxRetries: preupgradeMaxRetries,
|
||||
DisableLogs: disableLogs,
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
envVals cosmovisorEnv
|
||||
@ -395,210 +499,269 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
{
|
||||
name: "all bad",
|
||||
envVals: cosmovisorEnv{
|
||||
Home: "",
|
||||
Name: "",
|
||||
DownloadBin: "bad",
|
||||
RestartUpgrade: "bad",
|
||||
RestartDelay: "bad",
|
||||
SkipBackup: "bad",
|
||||
DataBackupPath: "bad",
|
||||
Interval: "bad",
|
||||
PreupgradeMaxRetries: "bad",
|
||||
Home: "",
|
||||
Name: "",
|
||||
DownloadBin: "bad",
|
||||
DownloadMustHaveChecksum: "bad",
|
||||
RestartUpgrade: "bad",
|
||||
RestartDelay: "bad",
|
||||
SkipBackup: "bad",
|
||||
DataBackupPath: "bad",
|
||||
Interval: "bad",
|
||||
PreupgradeMaxRetries: "bad",
|
||||
TimeFormatLogs: "bad",
|
||||
CustomPreupgrade: "",
|
||||
DisableRecase: "bad",
|
||||
ShutdownGrace: "bad",
|
||||
},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 9,
|
||||
expectedErrCount: 13,
|
||||
},
|
||||
{
|
||||
name: "all good",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "true", "10s"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", true, 10000000000),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "nothing set",
|
||||
envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", "", "false"},
|
||||
envVals: cosmovisorEnv{"", "", "", "", "", "", "", "", "", "", "false", "false", "", "", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 3,
|
||||
},
|
||||
// Note: Home and Name tests are done in TestValidate
|
||||
// timeformat tests are done in the TestTimeFormat
|
||||
{
|
||||
name: "download bin bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "bad", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "bad", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "download bin not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "download bin true",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "download bin false",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "download ensure checksum true",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "restart upgrade bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "bad", "600ms", "true", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "bad", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "restart upgrade not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "restart upgrade true",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "true", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, true, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "restart upgrade true",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "skip unsafe backups bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "bad", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "bad", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "skip unsafe backups not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "skip unsafe backups true",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "true", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, true, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "true", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, true, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "skip unsafe backups false",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "false", "600ms", "false", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", true, false, 600, false, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "true", "true", "false", "600ms", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", true, true, false, 600, false, absPath, 303, 1, "localhost:9090", false, true, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "poll interval bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "bad", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "bad", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "poll interval 0",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "0", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "0", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "poll interval not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 300, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "", "1", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 300, 1, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "poll interval 600",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "600", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "600", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "poll interval 1s",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "1s", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 1000, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "1s", "1", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 1000, 1, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "poll interval -3m",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "-3m", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "-3m", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "restart delay bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "bad", "false", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "bad", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "restart delay 0",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "0", "false", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "0", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "restart delay not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "", "false", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 0, false, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 0, false, absPath, 303, 1, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "restart delay 600",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600", "false", "", "300ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600", "false", "", "300ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "restart delay 1s",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "1s", "false", "", "303ms", "1", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 1000, false, absPath, 303, 1, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "1s", "false", "", "303ms", "1", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 1000, false, absPath, 303, 1, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "restart delay -3m",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "-3m", "false", "", "303ms", "1", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "-3m", "false", "", "303ms", "1", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "prepupgrade max retries bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "bad", "false"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "bad", "false", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "prepupgrade max retries 0",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "0", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "0", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "prepupgrade max retries not set",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "prepupgrade max retries 5",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "5", "false"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 5, false),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "false", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 5, "localhost:9090", false, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "disable logs bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "5", "bad"},
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "bad", "true", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "disable logs good",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "false", "600ms", "false", "", "406ms", "", "true"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, false, 600, false, absPath, 406, 0, true),
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "disable logs color bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "5", "true", "bad", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "disable logs color good",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "kitchen", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, false, time.Kitchen, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "disable logs timestamp",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "false", "", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, false, "", "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "enable rf3339 logs timestamp",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, true, time.RFC3339, "preupgrade.sh", false, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid logs timestamp format",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "invalid", "preupgrade.sh", "", ""},
|
||||
expectedCfg: nil,
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "disable recase good",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "true", ""},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, true, time.RFC3339, "preupgrade.sh", true, 0),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
{
|
||||
name: "disable recase bad",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "bad", ""},
|
||||
expectedErrCount: 1,
|
||||
},
|
||||
{
|
||||
name: "shutdown grace good",
|
||||
envVals: cosmovisorEnv{absPath, "testname", "false", "true", "false", "600ms", "false", "", "406ms", "", "true", "true", "rfc3339", "preupgrade.sh", "true", "15s"},
|
||||
expectedCfg: newConfig(absPath, "testname", false, true, false, 600, false, absPath, 406, 0, "localhost:9090", true, true, time.RFC3339, "preupgrade.sh", true, 15000000000),
|
||||
expectedErrCount: 0,
|
||||
},
|
||||
}
|
||||
@ -606,13 +769,13 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
for _, tc := range tests {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
s.setEnv(t, &tc.envVals)
|
||||
cfg, err := GetConfigFromEnv()
|
||||
cfg, err := GetConfigFromEnv(false)
|
||||
if tc.expectedErrCount == 0 {
|
||||
assert.NoError(t, err)
|
||||
} else if assert.Error(t, err) {
|
||||
errCount := 1
|
||||
if multi, isMulti := err.(*errors.MultiError); isMulti {
|
||||
errCount = multi.Len()
|
||||
if errMulti, ok := err.(interface{ Unwrap() []error }); ok {
|
||||
errCount = len(errMulti.Unwrap())
|
||||
}
|
||||
assert.Equal(t, tc.expectedErrCount, errCount, "error count")
|
||||
}
|
||||
@ -621,87 +784,91 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestLogConfigOrError() {
|
||||
cfg := &Config{
|
||||
Home: "/no/place/like/it",
|
||||
Name: "cosmotestvisor",
|
||||
AllowDownloadBinaries: true,
|
||||
RestartAfterUpgrade: true,
|
||||
PollInterval: 999,
|
||||
UnsafeSkipBackup: false,
|
||||
DataBackupPath: "/no/place/like/it",
|
||||
PreupgradeMaxRetries: 20,
|
||||
}
|
||||
errNormal := fmt.Errorf("this is a single error")
|
||||
errs := []error{
|
||||
fmt.Errorf("multi-error error 1"),
|
||||
fmt.Errorf("multi-error error 2"),
|
||||
fmt.Errorf("multi-error error 3"),
|
||||
}
|
||||
errMulti := errors.FlattenErrors(errs...)
|
||||
func (s *argsTestSuite) setUpDir() string {
|
||||
s.T().Helper()
|
||||
|
||||
makeTestLogger := func(testName string, out io.Writer) log.Logger {
|
||||
output := zerolog.ConsoleWriter{Out: out, TimeFormat: time.Kitchen, NoColor: true}
|
||||
logger := zerolog.New(output).With().Str("test", testName).Timestamp().Logger()
|
||||
return log.NewCustomLogger(logger)
|
||||
}
|
||||
home := s.T().TempDir()
|
||||
err := os.MkdirAll(filepath.Join(home, rootName), 0o755)
|
||||
s.Require().NoError(err)
|
||||
return home
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *Config
|
||||
err error
|
||||
contains []string
|
||||
notcontains []string
|
||||
func (s *argsTestSuite) setupConfig(home string) string {
|
||||
s.T().Helper()
|
||||
|
||||
cfg := newConfig(home, "test", true, true, true, 406, false, home, 8, 0, "localhost:9090", false, true, "kitchen", "", true, 10000000000)
|
||||
path := filepath.Join(home, rootName, "config.toml")
|
||||
f, err := os.Create(path)
|
||||
s.Require().NoError(err)
|
||||
|
||||
enc := toml.NewEncoder(f)
|
||||
s.Require().NoError(enc.Encode(&cfg))
|
||||
|
||||
err = f.Close()
|
||||
s.Require().NoError(err)
|
||||
|
||||
return path
|
||||
}
|
||||
|
||||
func (s *argsTestSuite) TestConfigFromFile() {
|
||||
home := s.setUpDir()
|
||||
// create a config file
|
||||
cfgFilePath := s.setupConfig(home)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
config *Config
|
||||
expectedCfg func() *Config
|
||||
filePath string
|
||||
expectedError string
|
||||
malleate func()
|
||||
}{
|
||||
{
|
||||
name: "normal error",
|
||||
cfg: nil,
|
||||
err: errNormal,
|
||||
contains: []string{"configuration error", errNormal.Error()}, // TODO: Fix this.
|
||||
notcontains: nil,
|
||||
name: "valid config",
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "test", true, true, true, 406, false, home, 8, 0, "localhost:9090", false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
filePath: cfgFilePath,
|
||||
expectedError: "",
|
||||
malleate: func() {},
|
||||
},
|
||||
{
|
||||
name: "multi error",
|
||||
cfg: nil,
|
||||
err: errMulti,
|
||||
contains: []string{"configuration errors found", errs[0].Error(), errs[1].Error(), errs[2].Error()},
|
||||
notcontains: nil,
|
||||
name: "env variable will override config file fields",
|
||||
filePath: cfgFilePath,
|
||||
expectedError: "",
|
||||
malleate: func() {
|
||||
// set env variable different from the config file
|
||||
os.Setenv(EnvName, "env-name")
|
||||
},
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "env-name", true, true, true, 406, false, home, 8, 0, "localhost:9090", false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "config",
|
||||
cfg: cfg,
|
||||
err: nil,
|
||||
contains: []string{"Configurable Values", cfg.DetailString()},
|
||||
notcontains: nil,
|
||||
},
|
||||
{
|
||||
name: "error and config - no config details",
|
||||
cfg: cfg,
|
||||
err: errNormal,
|
||||
contains: []string{"error"},
|
||||
notcontains: []string{"Configuration is valid", EnvName, cfg.Home}, // Just some spot checks.
|
||||
},
|
||||
{
|
||||
name: "nil nil - no output",
|
||||
cfg: nil,
|
||||
err: nil,
|
||||
contains: nil,
|
||||
notcontains: []string{" "},
|
||||
name: "empty config file path will load config from ENV variables",
|
||||
expectedCfg: func() *Config {
|
||||
return newConfig(home, "test", true, true, true, 406, false, home, 8, 0, "localhost:9090", false, true, time.Kitchen, "", true, 10000000000)
|
||||
},
|
||||
filePath: "",
|
||||
expectedError: "",
|
||||
malleate: func() {
|
||||
s.setEnv(s.T(), &cosmovisorEnv{home, "test", "true", "true", "true", "406ms", "false", home, "8ms", "0", "false", "true", "kitchen", "", "true", "10s"})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
for _, tc := range testCases {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
var b bytes.Buffer
|
||||
logger := makeTestLogger(tc.name, &b)
|
||||
LogConfigOrError(logger, tc.cfg, tc.err)
|
||||
output := b.String()
|
||||
for _, expected := range tc.contains {
|
||||
assert.Contains(t, output, expected)
|
||||
}
|
||||
for _, unexpected := range tc.notcontains {
|
||||
assert.NotContains(t, output, unexpected)
|
||||
tc.malleate()
|
||||
actualCfg, err := GetConfigFromFile(tc.filePath)
|
||||
if tc.expectedError != "" {
|
||||
s.Require().NoError(err)
|
||||
s.Require().Contains(err.Error(), tc.expectedError)
|
||||
return
|
||||
}
|
||||
|
||||
s.Require().NoError(err)
|
||||
s.Require().EqualValues(tc.expectedCfg(), actualCfg)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -714,7 +881,7 @@ func BenchmarkDetailString(b *testing.B) {
|
||||
AllowDownloadBinaries: true,
|
||||
UnsafeSkipBackup: true,
|
||||
PollInterval: 450 * time.Second,
|
||||
PreupgradeMaxRetries: 1e7,
|
||||
PreUpgradeMaxRetries: 1e7,
|
||||
}
|
||||
|
||||
b.ReportAllocs()
|
||||
|
||||
@ -1,49 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
)
|
||||
|
||||
func NewAddUpgradeCmd() *cobra.Command {
|
||||
addUpgrade := &cobra.Command{
|
||||
Use: "add-upgrade [upgrade-name] [path to executable]",
|
||||
Short: "Manually add upgrade binary to Cosmovisor",
|
||||
Use: "add-upgrade <upgrade-name> <path to executable>",
|
||||
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")
|
||||
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
|
||||
}
|
||||
|
||||
// AddUpgrade adds upgrade info to manifest
|
||||
func AddUpgrade(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
if err != nil {
|
||||
return err
|
||||
// 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 {
|
||||
upgradeName = strings.ToLower(upgradeName)
|
||||
}
|
||||
|
||||
logger := cmd.Context().Value(log.ContextKey).(log.Logger)
|
||||
if cfg.DisableLogs {
|
||||
logger = log.NewCustomLogger(zerolog.Nop())
|
||||
}
|
||||
|
||||
upgradeName := args[0]
|
||||
if len(upgradeName) == 0 {
|
||||
return fmt.Errorf("upgrade name cannot be empty")
|
||||
}
|
||||
|
||||
executablePath := args[1]
|
||||
if _, err := os.Stat(executablePath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("invalid executable path: %w", err)
|
||||
@ -54,7 +46,7 @@ func AddUpgrade(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// create upgrade dir
|
||||
upgradeLocation := cfg.UpgradeDir(upgradeName)
|
||||
if err := os.MkdirAll(path.Join(upgradeLocation, "bin"), 0o750); err != nil {
|
||||
if err := os.MkdirAll(path.Join(upgradeLocation, "bin"), 0o755); err != nil {
|
||||
return fmt.Errorf("failed to create upgrade directory: %w", err)
|
||||
}
|
||||
|
||||
@ -64,22 +56,85 @@ func AddUpgrade(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("failed to read binary: %w", err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(cfg.UpgradeBin(upgradeName)); err == nil {
|
||||
if force, _ := cmd.Flags().GetBool(cosmovisor.FlagForce); !force {
|
||||
return fmt.Errorf("upgrade binary already exists at %s", cfg.UpgradeBin(upgradeName))
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("Overwriting %s for %s upgrade", executablePath, upgradeName))
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to check if upgrade binary exists: %w", err)
|
||||
}
|
||||
|
||||
if err := os.WriteFile(cfg.UpgradeBin(upgradeName), executableData, 0o600); err != nil {
|
||||
return fmt.Errorf("failed to write binary to location: %w", err)
|
||||
if err := saveOrAbort(cfg.UpgradeBin(upgradeName), executableData, force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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 > 0 {
|
||||
plan := upgradetypes.Plan{Name: upgradeName, Height: upgradeHeight}
|
||||
if err := plan.ValidateBasic(); err != nil {
|
||||
panic(fmt.Errorf("something is wrong with cosmovisor: %w", err))
|
||||
}
|
||||
|
||||
// create upgrade-info.json file
|
||||
planData, err := json.Marshal(plan)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal upgrade plan: %w", err)
|
||||
}
|
||||
|
||||
if err := saveOrAbort(upgradeInfoPath, planData, force); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info(fmt.Sprintf("%s created, %s upgrade binary will switch at height %d", upgradeInfoPath, upgradeName, upgradeHeight))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetConfig returns a Config using passed-in flag
|
||||
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)
|
||||
}
|
||||
|
||||
cfg, err := cosmovisor.GetConfigFromFile(configPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
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 {
|
||||
if !force {
|
||||
return fmt.Errorf("file already exists at %s", path)
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
return fmt.Errorf("failed to check if file exists: %w", err)
|
||||
}
|
||||
|
||||
//nolint:gosec // We need broader permissions to make it executable
|
||||
if err := os.WriteFile(path, data, 0o755); err != nil {
|
||||
return fmt.Errorf("failed to write binary to location: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
143
tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go
Normal file
143
tools/cosmovisor/cmd/cosmovisor/batch_upgrade.go
Normal file
@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
func NewBatchAddUpgradeCmd() *cobra.Command {
|
||||
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 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:
|
||||
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.csv`,
|
||||
SilenceUsage: true,
|
||||
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'")
|
||||
cmd.MarkFlagsMutuallyExclusive("upgrade-file", "upgrade-list")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
// 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 := getConfigFromCmd(cmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
upgradeFile, err := cmd.Flags().GetString("upgrade-file")
|
||||
if err == nil && 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")
|
||||
}
|
||||
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 {
|
||||
upgradeInfoPaths := []string{}
|
||||
for i, upgrade := range upgradeList {
|
||||
if len(upgrade) != 3 {
|
||||
return fmt.Errorf("argument at position %d (%s) is invalid", i, upgrade)
|
||||
}
|
||||
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, upgrade[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: %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)
|
||||
}
|
||||
|
||||
// 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: %w", err)
|
||||
}
|
||||
|
||||
// Write to output file
|
||||
err = os.WriteFile(cfg.UpgradeInfoBatchFilePath(), batchData, 0o600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing combined JSON to file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// processUpgradeFile takes in a CSV batch upgrade file, parses it and calls processUpgradeList
|
||||
func processUpgradeFile(cfg *cosmovisor.Config, upgradeFile string) error {
|
||||
file, err := os.Open(upgradeFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening upgrade CSV 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 nil
|
||||
}
|
||||
@ -7,11 +7,13 @@ import (
|
||||
)
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config",
|
||||
Short: "Display cosmovisor config (prints environment variables used by cosmovisor).",
|
||||
Use: "config",
|
||||
Short: "Display cosmovisor config.",
|
||||
Long: `Display cosmovisor config. If a config file is provided, it will display the config from the file,
|
||||
otherwise it will display the config from the environment variables.`,
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
cfg, err := cosmovisor.GetConfigFromFile(cmd.Flag(cosmovisor.FlagCosmovisorConfig).Value.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ the proposal. Cosmovisor interprets that data to perform an update: switch a cur
|
||||
and restart the App.
|
||||
|
||||
Configuration of Cosmovisor is done through environment variables, which are
|
||||
documented in: https://docs.cosmos.network/main/tooling/cosmovisor`,
|
||||
documented in: https://docs.cosmos.network/main/build/tooling/cosmovisor`,
|
||||
cosmovisor.EnvName, cosmovisor.EnvHome,
|
||||
)
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ func TestGetHelpText(t *testing.T) {
|
||||
expectedPieces := []string{
|
||||
"Cosmovisor",
|
||||
cosmovisor.EnvName, cosmovisor.EnvHome,
|
||||
"https://docs.cosmos.network/main/tooling/cosmovisor",
|
||||
"https://docs.cosmos.network/main/build/tooling/cosmovisor",
|
||||
}
|
||||
|
||||
actual := GetHelpText()
|
||||
|
||||
@ -11,18 +11,23 @@ import (
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
cverrors "cosmossdk.io/tools/cosmovisor/errors"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
)
|
||||
|
||||
var initCmd = &cobra.Command{
|
||||
Use: "init <path to executable>",
|
||||
Short: "Initialize a cosmovisor daemon home directory.",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
logger := cmd.Context().Value(log.ContextKey).(log.Logger)
|
||||
return InitializeCosmovisor(logger, args)
|
||||
},
|
||||
func NewInitCmd() *cobra.Command {
|
||||
initCmd := &cobra.Command{
|
||||
Use: "init <path to executable>",
|
||||
Short: "Initialize a cosmovisor daemon home directory.",
|
||||
Long: `Initialize a cosmovisor daemon home directory with the provided executable.
|
||||
Configuration file is initialized at the default path (<-home->/cosmovisor/config.toml).`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return InitializeCosmovisor(nil, args)
|
||||
},
|
||||
}
|
||||
|
||||
return initCmd
|
||||
}
|
||||
|
||||
// InitializeCosmovisor initializes the cosmovisor directories, current link, and initial executable.
|
||||
@ -39,11 +44,22 @@ func InitializeCosmovisor(logger log.Logger, args []string) error {
|
||||
case exeInfo.IsDir():
|
||||
return errors.New("invalid path to executable: must not be a directory")
|
||||
}
|
||||
cfg, err := getConfigForInitCmd()
|
||||
|
||||
// skipping validation to not check if directories exist
|
||||
cfg, err := cosmovisor.GetConfigFromEnv(true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// process to minimal validation
|
||||
if err := minConfigValidate(cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
logger = cfg.Logger(os.Stdout)
|
||||
}
|
||||
|
||||
logger.Info("checking on the genesis/bin directory")
|
||||
genBinExe := cfg.GenesisBin()
|
||||
genBinDir, _ := filepath.Split(genBinExe)
|
||||
@ -77,6 +93,12 @@ func InitializeCosmovisor(logger log.Logger, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// set current working directory to $DAEMON_NAME/cosmosvisor
|
||||
// to allow current symlink to be relative
|
||||
if err = os.Chdir(cfg.Root()); err != nil {
|
||||
return fmt.Errorf("failed to change directory to %s: %w", cfg.Root(), err)
|
||||
}
|
||||
|
||||
logger.Info("checking on the current symlink and creating it if needed")
|
||||
cur, curErr := cfg.CurrentBin()
|
||||
if curErr != nil {
|
||||
@ -84,31 +106,29 @@ func InitializeCosmovisor(logger log.Logger, args []string) error {
|
||||
}
|
||||
logger.Info(fmt.Sprintf("the current symlink points to: %q", cur))
|
||||
|
||||
filePath, err := cfg.Export()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to export configuration: %w", err)
|
||||
}
|
||||
logger.Info(fmt.Sprintf("cosmovisor config.toml created at: %s", filePath))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getConfigForInitCmd gets just the configuration elements needed to initialize cosmovisor.
|
||||
func getConfigForInitCmd() (*cosmovisor.Config, error) {
|
||||
func minConfigValidate(cfg *cosmovisor.Config) error {
|
||||
var errs []error
|
||||
// Note: Not using GetConfigFromEnv here because that checks that the directories already exist.
|
||||
// We also don't care about the rest of the configuration stuff in here.
|
||||
cfg := &cosmovisor.Config{
|
||||
Home: os.Getenv(cosmovisor.EnvHome),
|
||||
Name: os.Getenv(cosmovisor.EnvName),
|
||||
}
|
||||
if len(cfg.Name) == 0 {
|
||||
errs = append(errs, fmt.Errorf("%s is not set", cosmovisor.EnvName))
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(cfg.Home) == 0:
|
||||
errs = append(errs, fmt.Errorf("%s is not set", cosmovisor.EnvHome))
|
||||
case !filepath.IsAbs(cfg.Home):
|
||||
errs = append(errs, fmt.Errorf("%s must be an absolute path", cosmovisor.EnvHome))
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, cverrors.FlattenErrors(errs...)
|
||||
}
|
||||
return cfg, nil
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// copyFile copies the file at the given source to the given destination.
|
||||
|
||||
@ -9,7 +9,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
@ -18,6 +19,13 @@ import (
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
const (
|
||||
notset = " is not set"
|
||||
cosmovisorDirName = "cosmovisor"
|
||||
|
||||
cfgFileWithExt = "config.toml"
|
||||
)
|
||||
|
||||
type InitTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
@ -28,15 +36,24 @@ func TestInitTestSuite(t *testing.T) {
|
||||
|
||||
// cosmovisorInitEnv are some string values of environment variables used to configure Cosmovisor, and used by the init command.
|
||||
type cosmovisorInitEnv struct {
|
||||
Home string
|
||||
Name string
|
||||
Home string
|
||||
Name string
|
||||
ColorLogs string
|
||||
TimeFormatLogs string
|
||||
}
|
||||
|
||||
type envMap struct {
|
||||
val string
|
||||
allowEmpty bool
|
||||
}
|
||||
|
||||
// ToMap creates a map of the cosmovisorInitEnv where the keys are the env var names.
|
||||
func (c cosmovisorInitEnv) ToMap() map[string]string {
|
||||
return map[string]string{
|
||||
cosmovisor.EnvHome: c.Home,
|
||||
cosmovisor.EnvName: c.Name,
|
||||
func (c cosmovisorInitEnv) ToMap() map[string]envMap {
|
||||
return map[string]envMap{
|
||||
cosmovisor.EnvHome: {val: c.Home, allowEmpty: false},
|
||||
cosmovisor.EnvName: {val: c.Name, allowEmpty: false},
|
||||
cosmovisor.EnvColorLogs: {val: c.ColorLogs, allowEmpty: false},
|
||||
cosmovisor.EnvTimeFormatLogs: {val: c.TimeFormatLogs, allowEmpty: true},
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,6 +64,10 @@ func (c *cosmovisorInitEnv) Set(envVar, envVal string) {
|
||||
c.Home = envVal
|
||||
case cosmovisor.EnvName:
|
||||
c.Name = envVal
|
||||
case cosmovisor.EnvColorLogs:
|
||||
c.Name = envVal
|
||||
case cosmovisor.EnvTimeFormatLogs:
|
||||
c.Name = envVal
|
||||
default:
|
||||
panic(fmt.Errorf("Unknown environment variable [%s]. Cannot set field to [%s]. ", envVar, envVal))
|
||||
}
|
||||
@ -63,6 +84,7 @@ func (s *InitTestSuite) clearEnv() *cosmovisorInitEnv {
|
||||
for envVar := range rv.ToMap() {
|
||||
rv.Set(envVar, os.Getenv(envVar))
|
||||
s.Require().NoError(os.Unsetenv(envVar))
|
||||
viper.Reset()
|
||||
}
|
||||
return &rv
|
||||
}
|
||||
@ -70,16 +92,16 @@ func (s *InitTestSuite) clearEnv() *cosmovisorInitEnv {
|
||||
// setEnv sets environment variables to the values provided.
|
||||
// If t is not nil, and there's a problem, the test will fail immediately.
|
||||
// If t is nil, problems will just be logged using s.T().
|
||||
func (s *InitTestSuite) setEnv(t *testing.T, env *cosmovisorInitEnv) {
|
||||
func (s *InitTestSuite) setEnv(t *testing.T, env *cosmovisorInitEnv) { //nolint:thelper // false psotive
|
||||
if t == nil {
|
||||
s.T().Logf("Restoring environment variables.")
|
||||
}
|
||||
for envVar, envVal := range env.ToMap() {
|
||||
var err error
|
||||
var msg string
|
||||
if len(envVal) != 0 {
|
||||
err = os.Setenv(envVar, envVal)
|
||||
msg = fmt.Sprintf("setting %s to %s", envVar, envVal)
|
||||
if len(envVal.val) != 0 || envVal.allowEmpty {
|
||||
err = os.Setenv(envVar, envVal.val)
|
||||
msg = fmt.Sprintf("setting %s to %s", envVar, envVal.val)
|
||||
} else {
|
||||
err = os.Unsetenv(envVar)
|
||||
msg = fmt.Sprintf("unsetting %s", envVar)
|
||||
@ -95,9 +117,29 @@ func (s *InitTestSuite) setEnv(t *testing.T, env *cosmovisorInitEnv) {
|
||||
}
|
||||
}
|
||||
|
||||
// readStdInpFromFile reads the provided data as if it were a standard input.
|
||||
func (s *InitTestSuite) readStdInpFromFile(data []byte) {
|
||||
// Create a temporary file and write the test input into it
|
||||
tmpfile, err := os.CreateTemp("", "test")
|
||||
if err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
// write the test input into the temporary file
|
||||
if _, err := tmpfile.Write(data); err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
if _, err := tmpfile.Seek(0, 0); err != nil {
|
||||
s.T().Fatal(err)
|
||||
}
|
||||
|
||||
os.Stdin = tmpfile
|
||||
}
|
||||
|
||||
var (
|
||||
_ io.Reader = BufferedPipe{}
|
||||
_ io.Writer = BufferedPipe{}
|
||||
_ io.Reader = &BufferedPipe{}
|
||||
_ io.Writer = &BufferedPipe{}
|
||||
)
|
||||
|
||||
// BufferedPipe contains a connected read/write pair of files (a pipe),
|
||||
@ -125,8 +167,8 @@ type BufferedPipe struct {
|
||||
// NewBufferedPipe creates a new BufferedPipe with the given name.
|
||||
// Files must be closed once you are done with them (e.g. with .Close()).
|
||||
// Once ready, buffering must be started using .Start(). See also StartNewBufferedPipe.
|
||||
func NewBufferedPipe(name string, replicateTo ...io.Writer) (BufferedPipe, error) {
|
||||
p := BufferedPipe{Name: name}
|
||||
func NewBufferedPipe(name string, replicateTo ...io.Writer) (*BufferedPipe, error) {
|
||||
p := &BufferedPipe{Name: name}
|
||||
p.Reader, p.Writer, p.Error = os.Pipe()
|
||||
if p.Error != nil {
|
||||
return p, p.Error
|
||||
@ -142,7 +184,7 @@ func NewBufferedPipe(name string, replicateTo ...io.Writer) (BufferedPipe, error
|
||||
//
|
||||
// p, _ := NewBufferedPipe(name, replicateTo...)
|
||||
// p.Start()
|
||||
func StartNewBufferedPipe(name string, replicateTo ...io.Writer) (BufferedPipe, error) {
|
||||
func StartNewBufferedPipe(name string, replicateTo ...io.Writer) (*BufferedPipe, error) {
|
||||
p, err := NewBufferedPipe(name, replicateTo...)
|
||||
if err != nil {
|
||||
return p, err
|
||||
@ -172,6 +214,7 @@ func (p *BufferedPipe) Start() {
|
||||
if _, p.Error = io.Copy(&b, p.BufferReader); p.Error != nil {
|
||||
b.WriteString("buffer error: " + p.Error.Error())
|
||||
}
|
||||
|
||||
p.buffer <- b.Bytes()
|
||||
}()
|
||||
p.started = true
|
||||
@ -196,6 +239,7 @@ func (p *BufferedPipe) Collect() []byte {
|
||||
panic("buffered pipe " + p.Name + " has not been started: cannot collect")
|
||||
}
|
||||
_ = p.Writer.Close()
|
||||
|
||||
if p.buffer == nil {
|
||||
return []byte{}
|
||||
}
|
||||
@ -205,12 +249,12 @@ func (p *BufferedPipe) Collect() []byte {
|
||||
}
|
||||
|
||||
// Read implements the io.Reader interface on this BufferedPipe.
|
||||
func (p BufferedPipe) Read(bz []byte) (n int, err error) {
|
||||
func (p *BufferedPipe) Read(bz []byte) (n int, err error) {
|
||||
return p.Reader.Read(bz)
|
||||
}
|
||||
|
||||
// Write implements the io.Writer interface on this BufferedPipe.
|
||||
func (p BufferedPipe) Write(bz []byte) (n int, err error) {
|
||||
func (p *BufferedPipe) Write(bz []byte) (n int, err error) {
|
||||
return p.Writer.Write(bz)
|
||||
}
|
||||
|
||||
@ -231,9 +275,8 @@ func (p *BufferedPipe) panicIfStarted(msg string) {
|
||||
func (s *InitTestSuite) NewCapturingLogger() (*BufferedPipe, log.Logger) {
|
||||
bufferedStdOut, err := StartNewBufferedPipe("stdout", os.Stdout)
|
||||
s.Require().NoError(err, "creating stdout buffered pipe")
|
||||
output := zerolog.ConsoleWriter{Out: bufferedStdOut, TimeFormat: time.RFC3339Nano}
|
||||
logger := log.NewCustomLogger(zerolog.New(output).With().Str("module", "cosmovisor").Timestamp().Logger())
|
||||
return &bufferedStdOut, logger
|
||||
logger := log.NewLogger(bufferedStdOut, log.ColorOption(false), log.TimeFormatOption(time.RFC3339Nano)).With(log.ModuleKey, cosmovisorDirName)
|
||||
return bufferedStdOut, logger
|
||||
}
|
||||
|
||||
// CreateHelloWorld creates a shell script that outputs HELLO WORLD.
|
||||
@ -291,13 +334,13 @@ func (s *InitTestSuite) TestInitializeCosmovisorNegativeValidation() {
|
||||
name: "no name",
|
||||
env: cosmovisorInitEnv{Home: "/example", Name: ""},
|
||||
args: []string{tmpExe},
|
||||
inErr: []string{cosmovisor.EnvName + " is not set"},
|
||||
inErr: []string{cosmovisor.EnvName + notset},
|
||||
},
|
||||
{
|
||||
name: "no home",
|
||||
env: cosmovisorInitEnv{Home: "", Name: "foo"},
|
||||
args: []string{tmpExe},
|
||||
inErr: []string{cosmovisor.EnvHome + " is not set"},
|
||||
inErr: []string{cosmovisor.EnvHome + notset},
|
||||
},
|
||||
{
|
||||
name: "home is relative",
|
||||
@ -309,7 +352,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorNegativeValidation() {
|
||||
name: "no name and no home",
|
||||
env: cosmovisorInitEnv{Home: "", Name: ""},
|
||||
args: []string{tmpExe},
|
||||
inErr: []string{cosmovisor.EnvName + " is not set", cosmovisor.EnvHome + " is not set"},
|
||||
inErr: []string{cosmovisor.EnvName + notset, cosmovisor.EnvHome + notset},
|
||||
},
|
||||
}
|
||||
|
||||
@ -343,7 +386,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "pear",
|
||||
}
|
||||
genDir := filepath.Join(env.Home, "cosmovisor", "genesis")
|
||||
genDir := filepath.Join(env.Home, cosmovisorDirName, "genesis")
|
||||
genBin := filepath.Join(genDir, "bin")
|
||||
require.NoError(t, os.MkdirAll(genDir, 0o755), "creating genesis directory")
|
||||
require.NoError(t, copyFile(hwExe, genBin), "copying exe to genesis/bin")
|
||||
@ -363,7 +406,7 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
}
|
||||
// Create the genesis bin executable path fully as a directory (instead of a file).
|
||||
// That should get through all the other stuff, but error when EnsureBinary is called.
|
||||
genBinExe := filepath.Join(env.Home, "cosmovisor", "genesis", "bin", env.Name)
|
||||
genBinExe := filepath.Join(env.Home, cosmovisorDirName, "genesis", "bin", env.Name)
|
||||
require.NoError(t, os.MkdirAll(genBinExe, 0o755))
|
||||
expErr := fmt.Sprintf("%s is not a regular file", env.Name)
|
||||
// Check the log messages just to make sure it's erroring where expecting.
|
||||
@ -399,18 +442,16 @@ func (s *InitTestSuite) TestInitializeCosmovisorInvalidExisting() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "orange",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
require.NoError(t, os.MkdirAll(rootDir, 0o755))
|
||||
curLn := filepath.Join(rootDir, "current")
|
||||
genDir := filepath.Join(rootDir, "genesis")
|
||||
require.NoError(t, copyFile(hwExe, curLn))
|
||||
expErr := fmt.Sprintf("symlink %s %s: file exists", genDir, curLn)
|
||||
|
||||
s.setEnv(t, env)
|
||||
buffer, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
err := InitializeCosmovisor(logger, []string{hwExe})
|
||||
require.EqualError(t, err, expErr, "calling InitializeCosmovisor")
|
||||
require.EqualError(t, err, "symlink genesis current: file exists", "calling InitializeCosmovisor")
|
||||
bufferBz := buffer.Collect()
|
||||
bufferStr := string(bufferBz)
|
||||
assert.Contains(t, bufferStr, "checking on the current symlink and creating it if needed")
|
||||
@ -443,23 +484,11 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
hwExe := s.CreateHelloWorld(0o755)
|
||||
|
||||
s.T().Run("starting with blank slate", func(t *testing.T) {
|
||||
testDir := s.T().TempDir()
|
||||
env := &cosmovisorInitEnv{
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
env := s.prepareConfig(s.T(), cosmovisorInitEnv{
|
||||
Name: "blank",
|
||||
}
|
||||
curLn := filepath.Join(env.Home, "cosmovisor", "current")
|
||||
genBinDir := filepath.Join(env.Home, "cosmovisor", "genesis", "bin")
|
||||
genBinExe := filepath.Join(genBinDir, env.Name)
|
||||
expInLog := []string{
|
||||
"checking on the genesis/bin directory",
|
||||
fmt.Sprintf("creating directory (and any parents): %q", genBinDir),
|
||||
"checking on the genesis/bin executable",
|
||||
fmt.Sprintf("copying executable into place: %q", genBinExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinExe),
|
||||
"checking on the current symlink and creating it if needed",
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinExe),
|
||||
}
|
||||
})
|
||||
|
||||
curLn := filepath.Join(env.Home, cosmovisorDirName, "current")
|
||||
|
||||
s.setEnv(s.T(), env)
|
||||
buffer, logger := s.NewCapturingLogger()
|
||||
@ -467,12 +496,31 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
err := InitializeCosmovisor(logger, []string{hwNonExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
|
||||
_, err = os.Stat(genBinDir)
|
||||
assert.NoErrorf(t, err, "statting the genesis bin dir: %q", genBinDir)
|
||||
genDir := filepath.Join(env.Home, cosmovisorDirName, "genesis", "bin")
|
||||
genBinExe := filepath.Join(genDir, env.Name)
|
||||
|
||||
genBinDirEval, err := filepath.EvalSymlinks(genDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
genBinEvalExe := filepath.Join(genBinDirEval, env.Name)
|
||||
|
||||
expInLog := []string{
|
||||
"checking on the genesis/bin directory",
|
||||
fmt.Sprintf("creating directory (and any parents): %q", genDir),
|
||||
"checking on the genesis/bin executable",
|
||||
fmt.Sprintf("copying executable into place: %q", genBinExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinExe),
|
||||
"checking on the current symlink and creating it if needed",
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinEvalExe),
|
||||
fmt.Sprintf("cosmovisor config.toml created at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
_, err = os.Stat(genBinDirEval)
|
||||
assert.NoErrorf(t, err, "statting the genesis bin dir: %q", genBinDirEval)
|
||||
_, err = os.Stat(curLn)
|
||||
assert.NoError(t, err, "statting the current link: %q", curLn)
|
||||
exeInfo, exeErr := os.Stat(genBinExe)
|
||||
if assert.NoError(t, exeErr, "statting the executable: %q", genBinExe) {
|
||||
exeInfo, exeErr := os.Stat(genBinEvalExe)
|
||||
if assert.NoError(t, exeErr, "statting the executable: %q", genBinEvalExe) {
|
||||
assert.True(t, exeInfo.Mode().IsRegular(), "executable is regular file")
|
||||
// Check if the world-executable bit is set.
|
||||
exePermMask := exeInfo.Mode().Perm() & 0o001
|
||||
@ -491,11 +539,19 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "nocur",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
|
||||
genBinDir := filepath.Join(rootDir, "genesis", "bin")
|
||||
genBinDirExe := filepath.Join(genBinDir, env.Name)
|
||||
|
||||
require.NoError(t, os.MkdirAll(genBinDir, 0o755), "making genesis bin dir")
|
||||
require.NoError(t, copyFile(hwExe, genBinDirExe), "copying executable to genesis")
|
||||
|
||||
genBinDirEval, err := filepath.EvalSymlinks(genBinDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
genBinEvalExe := filepath.Join(genBinDirEval, env.Name)
|
||||
|
||||
upgradesDir := filepath.Join(rootDir, "upgrades")
|
||||
for i := 1; i <= 5; i++ {
|
||||
upgradeBinDir := filepath.Join(upgradesDir, fmt.Sprintf("upgrade-%02d", i), "bin")
|
||||
@ -510,13 +566,14 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
"checking on the genesis/bin executable",
|
||||
fmt.Sprintf("the %q file already exists", genBinDirExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinDirExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinDirExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinEvalExe),
|
||||
fmt.Sprintf("cosmovisor config.toml created at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
s.setEnv(t, env)
|
||||
buffer, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
err := InitializeCosmovisor(logger, []string{hwExe})
|
||||
err = InitializeCosmovisor(logger, []string{hwExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
bufferBz := buffer.Collect()
|
||||
bufferStr := string(bufferBz)
|
||||
@ -531,18 +588,47 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "emptygen",
|
||||
}
|
||||
rootDir := filepath.Join(env.Home, "cosmovisor")
|
||||
rootDir := filepath.Join(env.Home, cosmovisorDirName)
|
||||
genBinDir := filepath.Join(rootDir, "genesis", "bin")
|
||||
genBinExe := filepath.Join(genBinDir, env.Name)
|
||||
require.NoError(t, os.MkdirAll(genBinDir, 0o755), "making genesis bin dir")
|
||||
|
||||
s.setEnv(t, env)
|
||||
buffer, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
err := InitializeCosmovisor(logger, []string{hwExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
|
||||
genBinDirEval, err := filepath.EvalSymlinks(genBinDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
genBinEvalExe := filepath.Join(genBinDirEval, env.Name)
|
||||
|
||||
expInLog := []string{
|
||||
"checking on the genesis/bin directory",
|
||||
fmt.Sprintf("the %q directory already exists", genBinDir),
|
||||
"checking on the genesis/bin executable",
|
||||
fmt.Sprintf("copying executable into place: %q", genBinExe),
|
||||
fmt.Sprintf("making sure %q is executable", genBinExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinExe),
|
||||
fmt.Sprintf("the current symlink points to: %q", genBinEvalExe),
|
||||
fmt.Sprintf("cosmovisor config.toml created at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)),
|
||||
}
|
||||
|
||||
bufferBz := buffer.Collect()
|
||||
bufferStr := string(bufferBz)
|
||||
for _, exp := range expInLog {
|
||||
assert.Contains(t, bufferStr, exp)
|
||||
}
|
||||
})
|
||||
|
||||
s.T().Run("ask to override (y/n) the existing config file", func(t *testing.T) {
|
||||
})
|
||||
|
||||
s.T().Run("init command exports configs to default path", func(t *testing.T) {
|
||||
testDir := s.T().TempDir()
|
||||
env := &cosmovisorInitEnv{
|
||||
Home: filepath.Join(testDir, "home"),
|
||||
Name: "emptygen",
|
||||
}
|
||||
|
||||
s.setEnv(t, env)
|
||||
@ -552,8 +638,106 @@ func (s *InitTestSuite) TestInitializeCosmovisorValid() {
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
bufferBz := buffer.Collect()
|
||||
bufferStr := string(bufferBz)
|
||||
for _, exp := range expInLog {
|
||||
assert.Contains(t, bufferStr, exp)
|
||||
}
|
||||
assert.Contains(t, bufferStr, fmt.Sprintf("cosmovisor config.toml created at: %s", filepath.Join(env.Home, cosmovisorDirName, cfgFileWithExt)))
|
||||
})
|
||||
}
|
||||
|
||||
func (s *InitTestSuite) TestInitializeCosmovisorWithOverrideCfg() {
|
||||
initEnv := s.clearEnv()
|
||||
defer s.setEnv(nil, initEnv)
|
||||
|
||||
tmpExe := s.CreateHelloWorld(0o755)
|
||||
testDir := s.T().TempDir()
|
||||
homePath := filepath.Join(testDir, "backup")
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
cfg *cosmovisor.Config
|
||||
override bool
|
||||
}{
|
||||
{
|
||||
name: "yes override",
|
||||
input: "y\n",
|
||||
cfg: &cosmovisor.Config{
|
||||
Home: homePath,
|
||||
Name: "old_test",
|
||||
DataBackupPath: homePath,
|
||||
},
|
||||
override: true,
|
||||
},
|
||||
{
|
||||
name: "no override",
|
||||
input: "n\n",
|
||||
cfg: &cosmovisor.Config{
|
||||
Home: homePath,
|
||||
Name: "old_test",
|
||||
DataBackupPath: homePath,
|
||||
},
|
||||
override: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
// create a root cosmovisor directory
|
||||
require.NoError(t, os.MkdirAll(tc.cfg.Root(), 0o755), "making root dir")
|
||||
|
||||
// create a config file in the default location
|
||||
file, err := os.Create(tc.cfg.DefaultCfgPath())
|
||||
require.NoError(t, err)
|
||||
|
||||
// write the config to the file
|
||||
err = toml.NewEncoder(file).Encode(tc.cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
|
||||
s.readStdInpFromFile([]byte(tc.input))
|
||||
|
||||
_, logger := s.NewCapturingLogger()
|
||||
logger.Info(fmt.Sprintf("Calling InitializeCosmovisor: %s", t.Name()))
|
||||
|
||||
// override the daemon name in environment file
|
||||
// if override is true (y), then the name should be updated in the config file
|
||||
// otherwise (n), the name should not be updated in the config file
|
||||
s.setEnv(t, &cosmovisorInitEnv{
|
||||
Home: tc.cfg.Home,
|
||||
Name: "update_name",
|
||||
})
|
||||
|
||||
err = InitializeCosmovisor(logger, []string{tmpExe})
|
||||
require.NoError(t, err, "calling InitializeCosmovisor")
|
||||
|
||||
cfg := &cosmovisor.Config{}
|
||||
// read the config file
|
||||
cfgFile, err := os.Open(tc.cfg.DefaultCfgPath())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
_ = cfgFile.Close()
|
||||
}()
|
||||
|
||||
err = toml.NewDecoder(cfgFile).Decode(cfg)
|
||||
require.NoError(t, err)
|
||||
if tc.override {
|
||||
// check if the name is updated
|
||||
// basically, override the existing config file
|
||||
assert.Equal(t, "update_name", cfg.Name)
|
||||
} else {
|
||||
// daemon name should not be updated
|
||||
assert.Equal(t, tc.cfg.Name, cfg.Name)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *InitTestSuite) prepareConfig(t *testing.T, config cosmovisorInitEnv) *cosmovisorInitEnv {
|
||||
t.Helper()
|
||||
|
||||
config.Home = s.T().TempDir()
|
||||
|
||||
err := os.Chdir(config.Home)
|
||||
require.NoError(t, err)
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
@ -3,17 +3,10 @@ package main
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
cverrors "cosmossdk.io/tools/cosmovisor/errors"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logger := log.NewLogger(os.Stdout).With(log.ModuleKey, "cosmovisor")
|
||||
ctx := context.WithValue(context.Background(), log.ContextKey, logger)
|
||||
|
||||
if err := NewRootCmd().ExecuteContext(ctx); err != nil {
|
||||
cverrors.LogErrors(logger, "", err)
|
||||
if err := NewRootCmd().ExecuteContext(context.Background()); err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
124
tools/cosmovisor/cmd/cosmovisor/prepare_upgrade.go
Normal file
124
tools/cosmovisor/cmd/cosmovisor/prepare_upgrade.go
Normal file
@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
)
|
||||
|
||||
func NewPrepareUpgradeCmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "prepare-upgrade",
|
||||
Short: "Prepare for the next upgrade",
|
||||
Long: `Prepare for the next upgrade by downloading and verifying the upgrade binary.
|
||||
This command will query the chain for the current upgrade plan and download the specified binary.
|
||||
gRPC must be enabled on the node for this command to work.`,
|
||||
RunE: prepareUpgradeHandler,
|
||||
SilenceUsage: false,
|
||||
Args: cobra.NoArgs,
|
||||
}
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func prepareUpgradeHandler(cmd *cobra.Command, _ []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 fmt.Errorf("failed to get config: %w", err)
|
||||
}
|
||||
|
||||
logger := cfg.Logger(cmd.OutOrStdout())
|
||||
|
||||
grpcAddress := cfg.GRPCAddress
|
||||
logger.Info("Using gRPC address", "address", grpcAddress)
|
||||
|
||||
upgradeInfo, err := queryUpgradeInfoFromChain(grpcAddress)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to query upgrade info: %w", err)
|
||||
}
|
||||
|
||||
if upgradeInfo == nil {
|
||||
logger.Info("No active upgrade plan found")
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Info("Preparing for upgrade", "name", upgradeInfo.Name, "height", upgradeInfo.Height)
|
||||
|
||||
upgradeInfoParsed, err := plan.ParseInfo(upgradeInfo.Info, plan.ParseOptionEnforceChecksum(cfg.DownloadMustHaveChecksum))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse upgrade info: %w", err)
|
||||
}
|
||||
|
||||
binaryURL, err := cosmovisor.GetBinaryURL(upgradeInfoParsed.Binaries)
|
||||
if err != nil {
|
||||
return fmt.Errorf("binary URL not found in upgrade plan. Cannot prepare for upgrade: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Downloading upgrade binary", "url", binaryURL)
|
||||
|
||||
if err := plan.DownloadUpgrade(cfg.UpgradeDir(upgradeInfo.Name), binaryURL, cfg.Name); err != nil {
|
||||
return fmt.Errorf("failed to download and verify binary: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("Upgrade preparation complete", "name", upgradeInfo.Name, "height", upgradeInfo.Height)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func queryUpgradeInfoFromChain(grpcAddress string) (*upgradetypes.Plan, error) {
|
||||
if grpcAddress == "" {
|
||||
return nil, fmt.Errorf("gRPC address is empty")
|
||||
}
|
||||
|
||||
grpcConn, err := getClient(grpcAddress)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open gRPC client: %w", err)
|
||||
}
|
||||
defer grpcConn.Close()
|
||||
|
||||
queryClient := upgradetypes.NewQueryClient(grpcConn)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
res, err := queryClient.CurrentPlan(ctx, &upgradetypes.QueryCurrentPlanRequest{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query current upgrade plan: %w", err)
|
||||
}
|
||||
|
||||
return res.Plan, nil
|
||||
}
|
||||
|
||||
func getClient(endpoint string) (*grpc.ClientConn, error) {
|
||||
var creds credentials.TransportCredentials
|
||||
if strings.HasPrefix(endpoint, "https://") {
|
||||
tlsConfig := &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
creds = credentials.NewTLS(tlsConfig)
|
||||
} else {
|
||||
creds = insecure.NewCredentials()
|
||||
}
|
||||
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(creds),
|
||||
}
|
||||
|
||||
return grpc.NewClient(endpoint, opts...)
|
||||
}
|
||||
@ -2,6 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
func NewRootCmd() *cobra.Command {
|
||||
@ -12,12 +14,16 @@ func NewRootCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
rootCmd.AddCommand(
|
||||
initCmd,
|
||||
NewInitCmd(),
|
||||
runCmd,
|
||||
configCmd,
|
||||
NewVersionCmd(),
|
||||
NewAddUpgradeCmd(),
|
||||
NewShowUpgradeInfoCmd(),
|
||||
NewBatchAddUpgradeCmd(),
|
||||
NewPrepareUpgradeCmd(),
|
||||
)
|
||||
|
||||
rootCmd.PersistentFlags().StringP(cosmovisor.FlagCosmovisorConfig, "c", "", "path to cosmovisor config file")
|
||||
return rootCmd
|
||||
}
|
||||
|
||||
@ -1,51 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
var runCmd = &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "Run an APP command.",
|
||||
Use: "run",
|
||||
Short: "Run an APP command.",
|
||||
Long: `Run an APP command. This command is intended to be used by the cosmovisor binary.
|
||||
Provide '--cosmovisor-config' file path in command args or set env variables to load configuration.
|
||||
`,
|
||||
SilenceUsage: true,
|
||||
DisableFlagParsing: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return Run(cmd, args)
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
cfgPath, args, err := parseCosmovisorConfig(args)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse cosmovisor config: %w", err)
|
||||
}
|
||||
|
||||
return run(cfgPath, args)
|
||||
},
|
||||
}
|
||||
|
||||
// Run runs the configured program with the given args and monitors it for upgrades.
|
||||
func Run(cmd *cobra.Command, args []string, options ...RunOption) error {
|
||||
cfg, err := cosmovisor.GetConfigFromEnv()
|
||||
// run runs the configured program with the given args and monitors it for upgrades.
|
||||
func run(cfgPath string, args []string, options ...RunOption) error {
|
||||
cfg, err := cosmovisor.GetConfigFromFile(cfgPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger := cmd.Context().Value(log.ContextKey).(log.Logger)
|
||||
|
||||
if cfg.DisableLogs {
|
||||
logger = log.NewCustomLogger(zerolog.Nop())
|
||||
}
|
||||
|
||||
runCfg := DefaultRunConfig
|
||||
for _, opt := range options {
|
||||
opt(&runCfg)
|
||||
}
|
||||
|
||||
// set current working directory to $DAEMON_NAME/cosmosvisor
|
||||
// to allow current symlink to be relative
|
||||
if err = os.Chdir(cfg.Root()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
logger := cfg.Logger(runCfg.StdOut)
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
doUpgrade, err := launcher.Run(args, runCfg.StdOut, runCfg.StdErr)
|
||||
doUpgrade, err := launcher.Run(args, runCfg.StdIn, runCfg.StdOut, runCfg.StdErr)
|
||||
// if RestartAfterUpgrade, we launch after a successful upgrade (given that condition launcher.Run returns nil)
|
||||
for cfg.RestartAfterUpgrade && err == nil && doUpgrade {
|
||||
logger.Info("upgrade detected, relaunching", "app", cfg.Name)
|
||||
doUpgrade, err = launcher.Run(args, runCfg.StdOut, runCfg.StdErr)
|
||||
doUpgrade, err = launcher.Run(args, runCfg.StdIn, runCfg.StdOut, runCfg.StdErr)
|
||||
}
|
||||
|
||||
if doUpgrade && err == nil {
|
||||
@ -54,3 +65,24 @@ func Run(cmd *cobra.Command, args []string, options ...RunOption) error {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func parseCosmovisorConfig(args []string) (string, []string, error) {
|
||||
var configFilePath string
|
||||
for i, arg := range args {
|
||||
// Check if the argument is the config flag
|
||||
if strings.EqualFold(arg, fmt.Sprintf("--%s", cosmovisor.FlagCosmovisorConfig)) ||
|
||||
strings.EqualFold(arg, fmt.Sprintf("-%s", cosmovisor.FlagCosmovisorConfig)) {
|
||||
// Check if there is an argument after the flag which should be the config file path
|
||||
if i+1 >= len(args) {
|
||||
return "", nil, fmt.Errorf("--%s requires an argument", cosmovisor.FlagCosmovisorConfig)
|
||||
}
|
||||
|
||||
configFilePath = args[i+1]
|
||||
// Remove the flag and its value from the arguments
|
||||
args = append(args[:i], args[i+2:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return configFilePath, args, nil
|
||||
}
|
||||
|
||||
@ -7,18 +7,27 @@ import (
|
||||
|
||||
// DefaultRunConfig defintes a default RunConfig that writes to os.Stdout and os.Stderr
|
||||
var DefaultRunConfig = RunConfig{
|
||||
StdIn: os.Stdin,
|
||||
StdOut: os.Stdout,
|
||||
StdErr: os.Stderr,
|
||||
}
|
||||
|
||||
// RunConfig defines the configuration for running a command
|
||||
type RunConfig struct {
|
||||
StdIn io.Reader
|
||||
StdOut io.Writer
|
||||
StdErr io.Writer
|
||||
}
|
||||
|
||||
type RunOption func(*RunConfig)
|
||||
|
||||
// StdInRunOption sets the StdIn reader for the Run command
|
||||
func StdInRunOption(r io.Reader) RunOption {
|
||||
return func(cfg *RunConfig) {
|
||||
cfg.StdIn = r
|
||||
}
|
||||
}
|
||||
|
||||
// StdOutRunOption sets the StdOut writer for the Run command
|
||||
func StdOutRunOption(w io.Writer) RunOption {
|
||||
return func(cfg *RunConfig) {
|
||||
@ -26,7 +35,7 @@ func StdOutRunOption(w io.Writer) RunOption {
|
||||
}
|
||||
}
|
||||
|
||||
// SdErrRunOption sets the StdErr writer for the Run command
|
||||
// StdErrRunOption sets the StdErr writer for the Run command
|
||||
func StdErrRunOption(w io.Writer) RunOption {
|
||||
return func(cfg *RunConfig) {
|
||||
cfg.StdErr = w
|
||||
|
||||
42
tools/cosmovisor/cmd/cosmovisor/show_upgrade.go
Normal file
42
tools/cosmovisor/cmd/cosmovisor/show_upgrade.go
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
)
|
||||
|
||||
func NewShowUpgradeInfoCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "show-upgrade-info",
|
||||
Short: "Display current upgrade-info.json from <app> data directory",
|
||||
SilenceUsage: false,
|
||||
Args: cobra.NoArgs,
|
||||
RunE: func(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
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(cfg.UpgradeInfoFilePath())
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
cmd.Printf("No upgrade info found at %s\n", cfg.UpgradeInfoFilePath())
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("failed to read upgrade-info.json: %w", err)
|
||||
}
|
||||
|
||||
cmd.Println(string(data))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -13,10 +13,11 @@ import (
|
||||
|
||||
func NewVersionCmd() *cobra.Command {
|
||||
versionCmd := &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Display cosmovisor and APP version.",
|
||||
Use: "version",
|
||||
Short: "Display cosmovisor and APP version.",
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
noAppVersion, _ := cmd.Flags().GetBool(cosmovisor.FlagNoAppVersion)
|
||||
noAppVersion, _ := cmd.Flags().GetBool(cosmovisor.FlagCosmovisorOnly)
|
||||
if val, err := cmd.Flags().GetString(cosmovisor.FlagOutput); val == "json" && err == nil {
|
||||
return printVersionJSON(cmd, args, noAppVersion)
|
||||
}
|
||||
@ -26,7 +27,7 @@ func NewVersionCmd() *cobra.Command {
|
||||
}
|
||||
|
||||
versionCmd.Flags().StringP(cosmovisor.FlagOutput, "o", "text", "Output format (text|json)")
|
||||
versionCmd.Flags().Bool(cosmovisor.FlagNoAppVersion, false, "Don't print APP version")
|
||||
versionCmd.Flags().Bool(cosmovisor.FlagCosmovisorOnly, false, "Print cosmovisor version only")
|
||||
|
||||
return versionCmd
|
||||
}
|
||||
@ -46,7 +47,7 @@ func printVersion(cmd *cobra.Command, args []string, noAppVersion bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := Run(cmd, append([]string{"version"}, args...)); err != nil {
|
||||
if err := run("", append([]string{"version"}, args...)); err != nil {
|
||||
return fmt.Errorf("failed to run version command: %w", err)
|
||||
}
|
||||
|
||||
@ -60,8 +61,8 @@ func printVersionJSON(cmd *cobra.Command, args []string, noAppVersion bool) erro
|
||||
}
|
||||
|
||||
buf := new(strings.Builder)
|
||||
if err := Run(
|
||||
cmd,
|
||||
if err := run(
|
||||
"",
|
||||
[]string{"version", "--long", "--output", "json"},
|
||||
StdOutRunOption(buf),
|
||||
); err != nil {
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
)
|
||||
|
||||
// MultiError is an error combining multiple other errors.
|
||||
// It will never have 0 or 1 errors. It will always have two or more.
|
||||
type MultiError struct {
|
||||
errs []error
|
||||
}
|
||||
|
||||
// FlattenErrors possibly creates a MultiError.
|
||||
// Nil entries are ignored.
|
||||
// If all provided errors are nil (or nothing is provided), nil is returned.
|
||||
// If only one non-nil error is provided, it is returned unchanged.
|
||||
// If two or more non-nil errors are provided, the returned error will be of type *MultiError
|
||||
// and it will contain each non-nil error.
|
||||
func FlattenErrors(errs ...error) error {
|
||||
rv := MultiError{}
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
if merr, isMerr := err.(*MultiError); isMerr {
|
||||
rv.errs = append(rv.errs, merr.errs...)
|
||||
} else {
|
||||
rv.errs = append(rv.errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
switch rv.Len() {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return rv.errs[0]
|
||||
}
|
||||
return &rv
|
||||
}
|
||||
|
||||
// GetErrors gets all the errors that make up this MultiError.
|
||||
func (e MultiError) GetErrors() []error {
|
||||
// Return a copy of the errs slice to prevent alteration of the original slice.
|
||||
rv := make([]error, e.Len())
|
||||
copy(rv, e.errs)
|
||||
return rv
|
||||
}
|
||||
|
||||
// Len gets the number of errors in this MultiError.
|
||||
func (e MultiError) Len() int {
|
||||
return len(e.errs)
|
||||
}
|
||||
|
||||
// Error implements the error interface for a MultiError.
|
||||
func (e *MultiError) Error() string {
|
||||
var sb strings.Builder
|
||||
fmt.Fprintf(&sb, "%d errors: ", len(e.errs))
|
||||
for i, err := range e.errs {
|
||||
if i != 0 {
|
||||
sb.WriteString(", ")
|
||||
}
|
||||
fmt.Fprintf(&sb, "%d: %v", i+1, err)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
// String implements the string interface for a MultiError.
|
||||
func (e MultiError) String() string {
|
||||
return e.Error()
|
||||
}
|
||||
|
||||
func LogErrors(logger log.Logger, msg string, err error) {
|
||||
switch err := err.(type) {
|
||||
case *MultiError:
|
||||
if msg != "" {
|
||||
logger.Error(msg)
|
||||
}
|
||||
for i, e := range err.GetErrors() {
|
||||
logger.Error(fmt.Sprintf(" %d:", i+1), "error", e)
|
||||
}
|
||||
default:
|
||||
logger.Error(msg, "error", err)
|
||||
}
|
||||
}
|
||||
@ -1,190 +0,0 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type MultiErrorTestSuite struct {
|
||||
suite.Suite
|
||||
|
||||
err1 error
|
||||
err2 error
|
||||
err3 error
|
||||
err4 error
|
||||
}
|
||||
|
||||
func TestMultiErrorTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(MultiErrorTestSuite))
|
||||
}
|
||||
|
||||
func (s *MultiErrorTestSuite) SetupTest() {
|
||||
s.err1 = errors.New("expected error one")
|
||||
s.err2 = errors.New("expected error two")
|
||||
s.err3 = errors.New("expected error three")
|
||||
s.err3 = errors.New("expected error four")
|
||||
}
|
||||
|
||||
func (s *MultiErrorTestSuite) TestFlattenErrors() {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []error
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
name: "none in nil out",
|
||||
input: []error{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "nil in nil out",
|
||||
input: []error{nil},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "nils in nil out",
|
||||
input: []error{nil, nil, nil},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "one in same out",
|
||||
input: []error{s.err1},
|
||||
expected: s.err1,
|
||||
},
|
||||
{
|
||||
name: "nils and one in that one out",
|
||||
input: []error{nil, s.err2, nil},
|
||||
expected: s.err2,
|
||||
},
|
||||
{
|
||||
name: "two in multi out with both",
|
||||
input: []error{s.err1, s.err2},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2}},
|
||||
},
|
||||
{
|
||||
name: "two and nils in multi out with both",
|
||||
input: []error{nil, s.err1, nil, s.err2, nil},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2}},
|
||||
},
|
||||
{
|
||||
name: "lots in multi out",
|
||||
input: []error{s.err1, s.err2, s.err3, s.err2, s.err1},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2, s.err3, s.err2, s.err1}},
|
||||
},
|
||||
{
|
||||
name: "multi and non in one multi out with all",
|
||||
input: []error{&MultiError{errs: []error{s.err1, s.err2}}, s.err3},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2, s.err3}},
|
||||
},
|
||||
{
|
||||
name: "non and multi in one multi out with all",
|
||||
input: []error{s.err1, &MultiError{errs: []error{s.err2, s.err3}}},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2, s.err3}},
|
||||
},
|
||||
{
|
||||
name: "two multi in one multi out with all",
|
||||
input: []error{&MultiError{errs: []error{s.err1, s.err2}}, &MultiError{errs: []error{s.err3, s.err4}}},
|
||||
expected: &MultiError{errs: []error{s.err1, s.err2, s.err3, s.err4}},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
actual := FlattenErrors(tc.input...)
|
||||
require.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MultiErrorTestSuite) TestGetErrors() {
|
||||
tests := []struct {
|
||||
name string
|
||||
multi MultiError
|
||||
expected []error
|
||||
}{
|
||||
{
|
||||
name: "two",
|
||||
multi: MultiError{errs: []error{s.err3, s.err1}},
|
||||
expected: []error{s.err3, s.err1},
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
multi: MultiError{errs: []error{s.err3, s.err1, s.err2}},
|
||||
expected: []error{s.err3, s.err1, s.err2},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
// Make sure it's getting what's expected.
|
||||
actual1 := tc.multi.GetErrors()
|
||||
require.NotSame(t, tc.expected, actual1)
|
||||
require.Equal(t, tc.expected, actual1)
|
||||
// Make sure that changing what was given back doesn't alter the original.
|
||||
actual1[0] = errors.New("unexpected error")
|
||||
actual2 := tc.multi.GetErrors()
|
||||
require.NotEqual(t, actual1, actual2)
|
||||
require.Equal(t, tc.expected, actual2)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MultiErrorTestSuite) TestLen() {
|
||||
tests := []struct {
|
||||
name string
|
||||
multi MultiError
|
||||
expected int
|
||||
}{
|
||||
{
|
||||
name: "two",
|
||||
multi: MultiError{errs: []error{s.err3, s.err1}},
|
||||
expected: 2,
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
multi: MultiError{errs: []error{s.err3, s.err1, s.err2}},
|
||||
expected: 3,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s.T().Run(tc.name, func(t *testing.T) {
|
||||
actual := tc.multi.Len()
|
||||
require.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *MultiErrorTestSuite) TestErrorAndString() {
|
||||
tests := []struct {
|
||||
name string
|
||||
multi MultiError
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "two",
|
||||
multi: MultiError{errs: []error{s.err1, s.err2}},
|
||||
expected: fmt.Sprintf("2 errors: 1: %s, 2: %s", s.err1, s.err2),
|
||||
},
|
||||
{
|
||||
name: "three",
|
||||
multi: MultiError{errs: []error{s.err1, s.err2, s.err3}},
|
||||
expected: fmt.Sprintf("3 errors: 1: %s, 2: %s, 3: %s", s.err1, s.err2, s.err3),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
s.T().Run(tc.name+" Error", func(t *testing.T) {
|
||||
actual := tc.multi.Error()
|
||||
require.Equal(t, tc.expected, actual)
|
||||
})
|
||||
s.T().Run(tc.name+" String", func(t *testing.T) {
|
||||
actual := tc.multi.String()
|
||||
require.Equal(t, tc.expected, actual)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,8 @@ package cosmovisor
|
||||
const (
|
||||
FlagOutput = "output"
|
||||
FlagSkipUpgradeHeight = "unsafe-skip-upgrades"
|
||||
FlagNoAppVersion = "no-app-version"
|
||||
FlagCosmovisorOnly = "cosmovisor-only"
|
||||
FlagForce = "force"
|
||||
FlagUpgradeHeight = "upgrade-height"
|
||||
FlagCosmovisorConfig = "cosmovisor-config"
|
||||
)
|
||||
|
||||
@ -3,181 +3,203 @@ module cosmossdk.io/tools/cosmovisor
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
cosmossdk.io/log v1.4.1
|
||||
cosmossdk.io/x/upgrade v0.0.0-20230614103911-b3da8bb4e801
|
||||
github.com/otiai10/copy v1.11.0
|
||||
github.com/rs/zerolog v1.33.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
cosmossdk.io/log v1.5.0
|
||||
cosmossdk.io/x/upgrade v0.1.4
|
||||
github.com/cosmos/cosmos-sdk v0.50.11
|
||||
github.com/fsnotify/fsnotify v1.8.0
|
||||
github.com/otiai10/copy v1.14.1
|
||||
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.10.0
|
||||
google.golang.org/grpc v1.70.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.112.0 // indirect
|
||||
cloud.google.com/go/compute v1.24.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
cloud.google.com/go/storage v1.36.0 // indirect
|
||||
cosmossdk.io/api v0.7.5 // indirect
|
||||
cel.dev/expr v0.19.1 // indirect
|
||||
cloud.google.com/go v0.118.0 // indirect
|
||||
cloud.google.com/go/auth v0.14.0 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/iam v1.3.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.22.1 // indirect
|
||||
cloud.google.com/go/storage v1.50.0 // indirect
|
||||
cosmossdk.io/api v0.7.6 // indirect
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/core v0.11.0 // indirect
|
||||
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
|
||||
cosmossdk.io/core v0.11.1 // indirect
|
||||
cosmossdk.io/depinject v1.1.0 // indirect
|
||||
cosmossdk.io/errors v1.0.1 // indirect
|
||||
cosmossdk.io/math v1.3.0 // indirect
|
||||
cosmossdk.io/store v1.0.0 // indirect
|
||||
cosmossdk.io/x/tx v0.13.4 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
cosmossdk.io/math v1.5.0 // indirect
|
||||
cosmossdk.io/store v1.1.1 // indirect
|
||||
cosmossdk.io/x/tx v1.0.0-alpha.3 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.224 // indirect
|
||||
github.com/99designs/keyring v1.2.2 // indirect
|
||||
github.com/DataDog/datadog-go v4.8.3+incompatible // indirect
|
||||
github.com/DataDog/zstd v1.5.6 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.49.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.49.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go v1.55.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/cockroachdb/errors v1.11.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
github.com/cockroachdb/pebble v1.1.0 // indirect
|
||||
github.com/bgentry/speakeasy v0.2.0 // indirect
|
||||
github.com/bytedance/sonic v1.12.8 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.3 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3 // indirect
|
||||
github.com/cockroachdb/apd/v3 v3.2.1 // indirect
|
||||
github.com/cockroachdb/errors v1.11.3 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
|
||||
github.com/cockroachdb/pebble v1.1.2 // 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.11 // indirect
|
||||
github.com/cometbft/cometbft-db v0.9.1 // indirect
|
||||
github.com/cometbft/cometbft v0.38.17 // indirect
|
||||
github.com/cometbft/cometbft-db v0.14.1 // indirect
|
||||
github.com/cosmos/btcutil v1.0.5 // indirect
|
||||
github.com/cosmos/cosmos-db v1.0.2 // indirect
|
||||
github.com/cosmos/cosmos-db v1.1.1 // indirect
|
||||
github.com/cosmos/cosmos-proto v1.0.0-beta.5 // indirect
|
||||
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230614103911-b3da8bb4e801 // 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
|
||||
github.com/cosmos/iavl v1.0.1 // indirect
|
||||
github.com/cosmos/iavl v1.2.2 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/danieljoos/wincred v1.2.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/emicklei/dot v1.6.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
|
||||
github.com/emicklei/dot v1.6.2 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.3 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.1.0 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.12.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.30.0 // indirect
|
||||
github.com/go-kit/kit v0.13.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/glog v1.2.4 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/btree v1.1.3 // indirect
|
||||
github.com/google/flatbuffers v24.3.25+incompatible // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-getter v1.7.1 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-getter v1.7.7 // indirect
|
||||
github.com/hashicorp/go-hclog v1.6.3 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.5.2 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.4 // indirect
|
||||
github.com/hashicorp/go-plugin v1.6.2 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.2 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.2.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.1 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/linxGnu/grocksdb v1.8.12 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/linxGnu/grocksdb v1.9.7 // indirect
|
||||
github.com/magiconair/properties v1.8.9 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.47.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rs/cors v1.8.3 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.5 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.8 // indirect
|
||||
go.etcd.io/bbolt v1.4.0-alpha.1 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.47.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
|
||||
go.opentelemetry.io/otel v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.22.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.22.0 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
google.golang.org/api v0.162.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
google.golang.org/grpc v1.63.2 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.33.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.13.0 // indirect
|
||||
golang.org/x/crypto v0.32.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/api v0.216.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250122153221-138b5a5a4fd4 // indirect
|
||||
google.golang.org/protobuf v1.36.4 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
nhooyr.io/websocket v1.8.6 // indirect
|
||||
nhooyr.io/websocket v1.8.11 // indirect
|
||||
pgregory.net/rapid v1.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,46 +1,191 @@
|
||||
package cosmovisor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/otiai10/copy"
|
||||
"github.com/rs/zerolog"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/x/upgrade/plan"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/grpc/cmtservice"
|
||||
)
|
||||
|
||||
type Launcher struct {
|
||||
logger *zerolog.Logger
|
||||
logger log.Logger
|
||||
cfg *Config
|
||||
fw *fileWatcher
|
||||
}
|
||||
|
||||
func NewLauncher(logger log.Logger, cfg *Config) (Launcher, error) {
|
||||
fw, err := newUpgradeFileWatcher(logger, cfg.UpgradeInfoFilePath(), cfg.PollInterval)
|
||||
fw, err := newUpgradeFileWatcher(cfg)
|
||||
if err != nil {
|
||||
return Launcher{}, err
|
||||
}
|
||||
|
||||
zl := logger.Impl().(*zerolog.Logger)
|
||||
return Launcher{logger: zl, cfg: cfg, fw: fw}, nil
|
||||
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())
|
||||
if os.IsNotExist(err) {
|
||||
return uInfos, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("error while reading %s: %w", cfg.UpgradeInfoBatchFilePath(), err)
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(upgradeInfoFile, &uInfos); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Slice(uInfos, func(i, j int) bool {
|
||||
return uInfos[i].Height < uInfos[j].Height
|
||||
})
|
||||
return uInfos, nil
|
||||
}
|
||||
|
||||
// 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 BatchUpgradeWatcher(ctx context.Context, cfg *Config, logger log.Logger) {
|
||||
// load batch file in memory
|
||||
uInfos, err := loadBatchUpgradeFile(cfg)
|
||||
if err != nil {
|
||||
logger.Warn("failed to load batch upgrade file", "error", err)
|
||||
uInfos = []upgradetypes.Plan{}
|
||||
}
|
||||
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
logger.Warn("failed to init watcher", "error", err)
|
||||
return
|
||||
}
|
||||
defer watcher.Close()
|
||||
err = watcher.Add(filepath.Dir(cfg.UpgradeInfoBatchFilePath()))
|
||||
if err != nil {
|
||||
logger.Warn("watcher failed to add upgrade directory", "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
conn, grpcErr = grpc.NewClient(cfg.GRPCAddress, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if grpcErr == nil {
|
||||
break pollLoop
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
client := cmtservice.NewServiceClient(conn)
|
||||
|
||||
var prevUpgradeHeight int64 = -1
|
||||
|
||||
logger.Info("starting the batch watcher loop")
|
||||
for {
|
||||
select {
|
||||
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)
|
||||
continue
|
||||
}
|
||||
}
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
if len(uInfos) == 0 {
|
||||
// prevent spending extra CPU cycles
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
resp, err := client.GetLatestBlock(ctx, &cmtservice.GetLatestBlockRequest{})
|
||||
if err != nil {
|
||||
logger.Warn("error getting latest block", "error", err)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
h := resp.SdkBlock.Header.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 {
|
||||
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)
|
||||
continue
|
||||
}
|
||||
uInfos = uInfos[1:]
|
||||
|
||||
jsonBytes, err = json.Marshal(uInfos)
|
||||
if err != nil {
|
||||
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)
|
||||
// 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
|
||||
}
|
||||
|
||||
// Add a small delay to avoid hammering the gRPC endpoint
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
func (l Launcher) Run(args []string, stdin io.Reader, stdout, stderr io.Writer) (bool, error) {
|
||||
bin, err := l.cfg.CurrentBin()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("error creating symlink to genesis: %w", err)
|
||||
@ -50,20 +195,32 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) {
|
||||
return false, fmt.Errorf("current binary is invalid: %w", err)
|
||||
}
|
||||
|
||||
l.logger.Info().Str("path", bin).Strs("args", args).Msg("running app")
|
||||
l.logger.Info("running app", "path", bin, "args", args)
|
||||
cmd := exec.Command(bin, args...)
|
||||
cmd.Stdin = stdin
|
||||
cmd.Stdout = stdout
|
||||
cmd.Stderr = stderr
|
||||
if err := cmd.Start(); err != nil {
|
||||
return false, fmt.Errorf("launching process %s %s failed: %w", bin, strings.Join(args, " "), err)
|
||||
}
|
||||
|
||||
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.Fatal().Err(err).Str("bin", bin).Msg("terminated")
|
||||
l.logger.Error("terminated", "error", err, "bin", bin)
|
||||
os.Exit(1)
|
||||
}
|
||||
}()
|
||||
|
||||
@ -78,7 +235,11 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := UpgradeBinary(log.NewCustomLogger(*l.logger), l.cfg, l.fw.currentInfo); err != nil {
|
||||
if err := l.doCustomPreUpgrade(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := UpgradeBinary(l.logger, l.cfg, l.fw.currentInfo); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -89,6 +250,9 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
cancel()
|
||||
wg.Wait()
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
@ -96,13 +260,14 @@ func (l Launcher) Run(args []string, stdout, stderr io.Writer) (bool, error) {
|
||||
// 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, err) if the process died by itself
|
||||
// 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 ...`
|
||||
// to happen with "start" but may happen with short-lived commands like `simd genesis export ...`
|
||||
func (l Launcher) WaitForUpgradeOrExit(cmd *exec.Cmd) (bool, error) {
|
||||
currentUpgrade, err := l.cfg.UpgradeInfo()
|
||||
if err != nil {
|
||||
l.logger.Error().Err(err)
|
||||
// upgrade info not found do nothing
|
||||
currentUpgrade = upgradetypes.Plan{}
|
||||
}
|
||||
|
||||
cmdDone := make(chan error)
|
||||
@ -113,8 +278,34 @@ func (l Launcher) WaitForUpgradeOrExit(cmd *exec.Cmd) (bool, error) {
|
||||
select {
|
||||
case <-l.fw.MonitorUpdate(currentUpgrade):
|
||||
// upgrade - kill the process and restart
|
||||
l.logger.Info().Msg("daemon shutting down in an attempt to restart")
|
||||
_ = cmd.Process.Kill()
|
||||
l.logger.Info("daemon shutting down in an attempt to restart")
|
||||
|
||||
if l.cfg.ShutdownGrace > 0 {
|
||||
// Interrupt signal
|
||||
l.logger.Info("sent interrupt to app, waiting for exit")
|
||||
_ = cmd.Process.Signal(os.Interrupt)
|
||||
|
||||
// Wait app exit
|
||||
psChan := make(chan *os.ProcessState)
|
||||
go func() {
|
||||
pstate, _ := cmd.Process.Wait()
|
||||
psChan <- pstate
|
||||
}()
|
||||
|
||||
// Timeout and kill
|
||||
select {
|
||||
case <-psChan:
|
||||
// Normal Exit
|
||||
l.logger.Info("app exited normally")
|
||||
case <-time.After(l.cfg.ShutdownGrace):
|
||||
l.logger.Info("DAEMON_SHUTDOWN_GRACE exceeded, killing app")
|
||||
// Kill after grace period
|
||||
_ = cmd.Process.Kill()
|
||||
}
|
||||
} else {
|
||||
// Default: Immediate app kill
|
||||
_ = cmd.Process.Kill()
|
||||
}
|
||||
case err := <-cmdDone:
|
||||
l.fw.Stop()
|
||||
// no error -> command exits normally (eg. short command like `gaiad version`)
|
||||
@ -135,7 +326,7 @@ func (l Launcher) doBackup() error {
|
||||
if !l.cfg.UnsafeSkipBackup {
|
||||
// check if upgrade-info.json is not empty.
|
||||
var uInfo upgradetypes.Plan
|
||||
upgradeInfoFile, err := os.ReadFile(filepath.Join(l.cfg.Home, "data", "upgrade-info.json"))
|
||||
upgradeInfoFile, err := os.ReadFile(l.cfg.UpgradeInfoFilePath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while reading upgrade-info.json: %w", err)
|
||||
}
|
||||
@ -145,15 +336,15 @@ func (l Launcher) doBackup() error {
|
||||
}
|
||||
|
||||
if uInfo.Name == "" {
|
||||
return fmt.Errorf("upgrade-info.json is empty")
|
||||
return errors.New("upgrade-info.json is empty")
|
||||
}
|
||||
|
||||
// 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(l.cfg.DataBackupPath, fmt.Sprintf("data"+"-backup-%s", stStr))
|
||||
ymd := fmt.Sprintf("%d-%d-%d", st.Year(), st.Month(), st.Day())
|
||||
dst := filepath.Join(l.cfg.DataBackupPath, fmt.Sprintf("data"+"-backup-%s", ymd))
|
||||
|
||||
l.logger.Info().Time("backup start time", st).Msg("starting to take backup of data directory")
|
||||
l.logger.Info("starting to take backup of data directory", "backup start time", st)
|
||||
|
||||
// copy the $DAEMON_HOME/data to a backup dir
|
||||
if err = copy.Copy(filepath.Join(l.cfg.Home, "data"), dst); err != nil {
|
||||
@ -162,38 +353,100 @@ func (l Launcher) doBackup() error {
|
||||
|
||||
// backup is done, lets check endtime to calculate total time taken for backup process
|
||||
et := time.Now()
|
||||
l.logger.Info().Str("backup saved at", dst).Time("backup completion time", et).TimeDiff("time taken to complete backup", et, st).Msg("backup completed")
|
||||
l.logger.Info("backup completed", "backup saved at", dst, "backup completion time", et, "time taken to complete backup", et.Sub(st))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doCustomPreUpgrade executes the custom preupgrade script if provided.
|
||||
func (l Launcher) doCustomPreUpgrade() error {
|
||||
if l.cfg.CustomPreUpgrade == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if upgrade-info.json is not empty.
|
||||
var upgradePlan upgradetypes.Plan
|
||||
upgradeInfoFile, err := os.ReadFile(l.cfg.UpgradeInfoFilePath())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error while reading upgrade-info.json: %w", err)
|
||||
}
|
||||
|
||||
if err = json.Unmarshal(upgradeInfoFile, &upgradePlan); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = upgradePlan.ValidateBasic(); err != nil {
|
||||
return fmt.Errorf("invalid upgrade plan: %w", err)
|
||||
}
|
||||
|
||||
// check if preupgradeFile is executable file
|
||||
preupgradeFile := filepath.Join(l.cfg.Home, "cosmovisor", l.cfg.CustomPreUpgrade)
|
||||
l.logger.Info("looking for COSMOVISOR_CUSTOM_PREUPGRADE file", "file", preupgradeFile)
|
||||
info, err := os.Stat(preupgradeFile)
|
||||
if err != nil {
|
||||
l.logger.Error("COSMOVISOR_CUSTOM_PREUPGRADE file missing", "file", preupgradeFile)
|
||||
return err
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
_, f := filepath.Split(preupgradeFile)
|
||||
return fmt.Errorf("COSMOVISOR_CUSTOM_PREUPGRADE: %s is not a regular file", f)
|
||||
}
|
||||
|
||||
// Set the execute bit for only the current user
|
||||
// Given: Current user - Group - Everyone
|
||||
// 0o RWX - RWX - RWX
|
||||
oldMode := info.Mode().Perm()
|
||||
newMode := oldMode | 0o100
|
||||
if oldMode != newMode {
|
||||
if err := os.Chmod(preupgradeFile, newMode); err != nil {
|
||||
l.logger.Info("COSMOVISOR_CUSTOM_PREUPGRADE could not add execute permission")
|
||||
return errors.New("COSMOVISOR_CUSTOM_PREUPGRADE could not add execute permission")
|
||||
}
|
||||
}
|
||||
|
||||
// Run preupgradeFile
|
||||
cmd := exec.Command(preupgradeFile, upgradePlan.Name, fmt.Sprintf("%d", upgradePlan.Height))
|
||||
cmd.Dir = l.cfg.Home
|
||||
result, err := cmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.logger.Info("COSMOVISOR_CUSTOM_PREUPGRADE result", "command", preupgradeFile, "argv1", upgradePlan.Name, "argv2", fmt.Sprintf("%d", upgradePlan.Height), "result", result)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// doPreUpgrade runs the pre-upgrade command defined by the application and handles respective error codes.
|
||||
// cfg contains the cosmovisor config from env var.
|
||||
// doPreUpgrade runs the new APP binary in order to process the upgrade (post-upgrade for cosmovisor).
|
||||
func (l *Launcher) doPreUpgrade() error {
|
||||
counter := 0
|
||||
for {
|
||||
if counter > l.cfg.PreupgradeMaxRetries {
|
||||
return fmt.Errorf("pre-upgrade command failed. reached max attempt of retries - %d", l.cfg.PreupgradeMaxRetries)
|
||||
if counter > l.cfg.PreUpgradeMaxRetries {
|
||||
return fmt.Errorf("pre-upgrade command failed. reached max attempt of retries - %d", l.cfg.PreUpgradeMaxRetries)
|
||||
}
|
||||
|
||||
if err := l.executePreUpgradeCmd(); err != nil {
|
||||
counter++
|
||||
|
||||
switch err.(*exec.ExitError).ProcessState.ExitCode() {
|
||||
case 1:
|
||||
l.logger.Info().Msg("pre-upgrade command does not exist. continuing the upgrade.")
|
||||
return nil
|
||||
case 30:
|
||||
return fmt.Errorf("pre-upgrade command failed : %w", err)
|
||||
case 31:
|
||||
l.logger.Error().Err(err).Int("attempt", counter).Msg("pre-upgrade command failed. retrying")
|
||||
continue
|
||||
var exitErr *exec.ExitError
|
||||
if errors.As(err, &exitErr) {
|
||||
switch exitErr.ProcessState.ExitCode() {
|
||||
case 1:
|
||||
l.logger.Info("pre-upgrade command does not exist. continuing the upgrade.")
|
||||
return nil
|
||||
case 30:
|
||||
return fmt.Errorf("pre-upgrade command failed : %w", err)
|
||||
case 31:
|
||||
l.logger.Error("pre-upgrade command failed. retrying", "error", err, "attempt", counter)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
l.logger.Info().Msg("pre-upgrade successful. continuing the upgrade.")
|
||||
l.logger.Info("pre-upgrade successful. continuing the upgrade.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@ -211,7 +464,7 @@ func (l *Launcher) executePreUpgradeCmd() error {
|
||||
return err
|
||||
}
|
||||
|
||||
l.logger.Info().Bytes("result", result).Msg("pre-upgrade result")
|
||||
l.logger.Info("pre-upgrade result", "result", result)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -1,171 +1,468 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
//go:build linux || darwin
|
||||
|
||||
package cosmovisor_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
"cosmossdk.io/tools/cosmovisor"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
)
|
||||
|
||||
type processTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
var workDir string
|
||||
|
||||
func TestProcessTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(processTestSuite))
|
||||
func init() {
|
||||
workDir, _ = os.Getwd()
|
||||
}
|
||||
|
||||
// TestLaunchProcess will try running the script a few times and watch upgrades work properly
|
||||
// and args are passed through
|
||||
func (s *processTestSuite) TestLaunchProcess() {
|
||||
func TestLaunchProcess(t *testing.T) {
|
||||
// binaries from testdata/validate directory
|
||||
require := s.Require()
|
||||
home := copyTestData(s.T(), "validate")
|
||||
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", PollInterval: 20, UnsafeSkipBackup: true}
|
||||
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/validate"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
PollInterval: 15,
|
||||
UnsafeSkipBackup: true,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmosvisor")
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.GenesisBin(), currentBin)
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(err)
|
||||
require.NoError(t, err)
|
||||
|
||||
upgradeFile := cfg.UpgradeInfoFilePath()
|
||||
|
||||
args := []string{"foo", "bar", "1234", upgradeFile}
|
||||
doUpgrade, err := launcher.Run(args, stdout, stderr)
|
||||
require.NoError(err)
|
||||
require.True(doUpgrade)
|
||||
require.Equal("", stderr.String())
|
||||
require.Equal(fmt.Sprintf("Genesis foo bar 1234 %s\nUPGRADE \"chain2\" NEEDED at height: 49: {}\n", upgradeFile), stdout.String())
|
||||
doUpgrade, err := launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, fmt.Sprintf("Genesis foo bar 1234 %s\nUPGRADE \"chain2\" NEEDED at height: 49: {}\n", upgradeFile), stdout.String())
|
||||
|
||||
// ensure this is upgraded now and produces new output
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(cfg.UpgradeBin("chain2"), currentBin)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rPath, 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 = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.False(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Chain 2 is live!\nArgs: second run --verbose\nFinished successfully\n", stdout.String())
|
||||
|
||||
// ended without other upgrade
|
||||
require.Equal(cfg.UpgradeBin("chain2"), currentBin)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rPath, currentBin)
|
||||
}
|
||||
|
||||
func (s *processTestSuite) TestLaunchProcessWithRestartDelay() {
|
||||
// TestPlanDisableRecase will test upgrades without lower case plan names
|
||||
func TestPlanDisableRecase(t *testing.T) {
|
||||
// binaries from testdata/validate directory
|
||||
require := s.Require()
|
||||
home := copyTestData(s.T(), "validate")
|
||||
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", RestartDelay: 5 * time.Second, PollInterval: 20, UnsafeSkipBackup: true}
|
||||
logger := log.NewTestLogger(s.T()).With(log.ModuleKey, "cosmosvisor")
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/norecase"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
PollInterval: 20,
|
||||
UnsafeSkipBackup: true,
|
||||
DisableRecase: true,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmosvisor")
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.GenesisBin(), currentBin)
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(err)
|
||||
require.NoError(t, err)
|
||||
|
||||
upgradeFile := cfg.UpgradeInfoFilePath()
|
||||
|
||||
args := []string{"foo", "bar", "1234", upgradeFile}
|
||||
doUpgrade, err := launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, fmt.Sprintf("Genesis foo bar 1234 %s\nUPGRADE \"Chain2\" NEEDED at height: 49: {}\n", upgradeFile), stdout.String())
|
||||
|
||||
// ensure this is upgraded now and produces new output
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("Chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
args = []string{"second", "run", "--verbose"}
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
|
||||
doUpgrade, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.False(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Chain 2 is live!\nArgs: second run --verbose\nFinished successfully\n", stdout.String())
|
||||
|
||||
// ended without other upgrade
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("Chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
}
|
||||
|
||||
func TestLaunchProcessWithRestartDelay(t *testing.T) {
|
||||
// binaries from testdata/validate directory
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/validate"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
RestartDelay: 5 * time.Second,
|
||||
PollInterval: 20,
|
||||
UnsafeSkipBackup: true,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmosvisor")
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
upgradeFile := cfg.UpgradeInfoFilePath()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
doUpgrade, err := launcher.Run([]string{"foo", "bar", "1234", upgradeFile}, stdout, stderr)
|
||||
require.NoError(err)
|
||||
require.True(doUpgrade)
|
||||
doUpgrade, err := launcher.Run([]string{"foo", "bar", "1234", upgradeFile}, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
|
||||
// may not be the best way but the fastest way to check we meet the delay
|
||||
// in addition to comparing both the runtime of this test and TestLaunchProcess in addition
|
||||
if time.Since(start) < cfg.RestartDelay {
|
||||
require.FailNow("restart delay not met")
|
||||
require.FailNow(t, "restart delay not met")
|
||||
}
|
||||
}
|
||||
|
||||
// TestPlanShutdownGrace will test upgrades without lower case plan names
|
||||
func TestPlanShutdownGrace(t *testing.T) {
|
||||
// binaries from testdata/validate directory
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/dontdie"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
PollInterval: 15,
|
||||
UnsafeSkipBackup: true,
|
||||
ShutdownGrace: 2 * time.Second,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmosvisor")
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
upgradeFile := cfg.UpgradeInfoFilePath()
|
||||
|
||||
args := []string{"foo", "bar", "1234", upgradeFile}
|
||||
doUpgrade, err := launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, fmt.Sprintf("Genesis foo bar 1234 %s\nUPGRADE \"Chain2\" NEEDED at height: 49: {}\nWARN Need Flush\nFlushed\n", upgradeFile), stdout.String())
|
||||
|
||||
// ensure this is upgraded now and produces new output
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
args = []string{"second", "run", "--verbose"}
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
|
||||
doUpgrade, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.False(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Chain 2 is live!\nArgs: second run --verbose\nFinished successfully\n", stdout.String())
|
||||
|
||||
// ended without other upgrade
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
}
|
||||
|
||||
// TestLaunchProcess will try running the script a few times and watch upgrades work properly
|
||||
// and args are passed through
|
||||
func (s *processTestSuite) TestLaunchProcessWithDownloads() {
|
||||
func TestLaunchProcessWithDownloads(t *testing.T) {
|
||||
// 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()
|
||||
home := copyTestData(s.T(), "download")
|
||||
cfg := &cosmovisor.Config{Home: home, Name: "autod", AllowDownloadBinaries: true, PollInterval: 100, UnsafeSkipBackup: true}
|
||||
logger := log.NewLogger(os.Stdout).With(log.ModuleKey, "cosmovisor")
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/download"),
|
||||
cosmovisor.Config{
|
||||
Name: "autod",
|
||||
AllowDownloadBinaries: true,
|
||||
PollInterval: 100,
|
||||
UnsafeSkipBackup: true,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmovisor")
|
||||
upgradeFilename := cfg.UpgradeInfoFilePath()
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.GenesisBin(), currentBin)
|
||||
require.NoError(t, err)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(err)
|
||||
require.NoError(t, err)
|
||||
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
args := []string{"some", "args", upgradeFilename}
|
||||
doUpgrade, err := launcher.Run(args, stdout, stderr)
|
||||
|
||||
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())
|
||||
doUpgrade, err := launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Genesis autod. Args: some args "+upgradeFilename+"\n"+`ERROR: UPGRADE "chain2" NEEDED at height: 49: zip_binary`+"\n", stdout.String())
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.UpgradeBin("chain2"), currentBin)
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
// start chain2
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
args = []string{"run", "--fast", upgradeFilename}
|
||||
doUpgrade, err = launcher.Run(args, stdout, stderr)
|
||||
require.NoError(err)
|
||||
doUpgrade, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
|
||||
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())
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "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)
|
||||
require.True(t, doUpgrade)
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.UpgradeBin("chain3"), currentBin)
|
||||
require.NoError(t, err)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain3"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
// run the last chain
|
||||
args = []string{"end", "--halt", upgradeFilename}
|
||||
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 = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.False(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Chain 3 from zipped directory\nArgs: end --halt "+upgradeFilename+"\n", stdout.String())
|
||||
|
||||
// and this doesn't upgrade
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(err)
|
||||
require.Equal(cfg.UpgradeBin("chain3"), currentBin)
|
||||
require.NoError(t, err)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain3"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
}
|
||||
|
||||
// TestLaunchProcessWithDownloadsAndMissingPreupgrade will try running the script a few times and watch upgrades work properly
|
||||
// and args are passed through
|
||||
func TestLaunchProcessWithDownloadsAndMissingPreupgrade(t *testing.T) {
|
||||
// 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
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/download"),
|
||||
cosmovisor.Config{
|
||||
Name: "autod",
|
||||
AllowDownloadBinaries: true,
|
||||
PollInterval: 100,
|
||||
UnsafeSkipBackup: true,
|
||||
CustomPreUpgrade: "missing.sh",
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewTestLogger(t).With(log.ModuleKey, "cosmovisor")
|
||||
upgradeFilename := cfg.UpgradeInfoFilePath()
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Missing Preupgrade Script
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
args := []string{"some", "args", upgradeFilename}
|
||||
_, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
|
||||
require.ErrorContains(t, err, "missing.sh")
|
||||
require.ErrorIs(t, err, fs.ErrNotExist)
|
||||
}
|
||||
|
||||
// TestLaunchProcessWithDownloadsAndPreupgrade will try running the script a few times and watch upgrades work properly
|
||||
// and args are passed through
|
||||
func TestLaunchProcessWithDownloadsAndPreupgrade(t *testing.T) {
|
||||
// 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
|
||||
cfg := prepareConfig(
|
||||
t,
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/download"),
|
||||
cosmovisor.Config{
|
||||
Name: "autod",
|
||||
AllowDownloadBinaries: true,
|
||||
PollInterval: 100,
|
||||
UnsafeSkipBackup: true,
|
||||
CustomPreUpgrade: "preupgrade.sh",
|
||||
},
|
||||
)
|
||||
|
||||
buf := newBuffer() // inspect output using buf.String()
|
||||
logger := log.NewLogger(buf).With(log.ModuleKey, "cosmovisor")
|
||||
upgradeFilename := cfg.UpgradeInfoFilePath()
|
||||
|
||||
// should run the genesis binary and produce expected output
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
launcher, err := cosmovisor.NewLauncher(logger, cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
stdin, _ := os.Open(os.DevNull)
|
||||
stdout, stderr := newBuffer(), newBuffer()
|
||||
args := []string{"some", "args", upgradeFilename}
|
||||
doUpgrade, err := launcher.Run(args, stdin, stdout, stderr)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Genesis autod. Args: some args "+upgradeFilename+"\n"+`ERROR: UPGRADE "chain2" NEEDED at height: 49: zip_binary`+"\n", stdout.String())
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
// should have preupgrade.sh results
|
||||
require.FileExists(t, filepath.Join(cfg.Home, "upgrade_name_chain2_height_49"))
|
||||
|
||||
// start chain2
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
args = []string{"run", "--fast", upgradeFilename}
|
||||
doUpgrade, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "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(t, doUpgrade)
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain3"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
|
||||
// should have preupgrade.sh results
|
||||
require.FileExists(t, filepath.Join(cfg.Home, "upgrade_name_chain3_height_936"))
|
||||
|
||||
// run the last chain
|
||||
args = []string{"end", "--halt", upgradeFilename}
|
||||
stdout.Reset()
|
||||
stderr.Reset()
|
||||
doUpgrade, err = launcher.Run(args, stdin, stdout, stderr)
|
||||
require.NoError(t, err)
|
||||
require.False(t, doUpgrade)
|
||||
require.Empty(t, stderr.String())
|
||||
require.Equal(t, "Chain 3 from zipped directory\nArgs: end --halt "+upgradeFilename+"\n", stdout.String())
|
||||
|
||||
// and this doesn't upgrade
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
require.NoError(t, err)
|
||||
rPath, err = filepath.EvalSymlinks(cfg.UpgradeBin("chain3"))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, rPath, currentBin)
|
||||
}
|
||||
|
||||
// TestSkipUpgrade tests heights that are identified to be skipped and return if upgrade height matches the skip heights
|
||||
|
||||
@ -5,69 +5,78 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
|
||||
"cosmossdk.io/log"
|
||||
upgradetypes "cosmossdk.io/x/upgrade/types"
|
||||
)
|
||||
|
||||
var errUntestAble = errors.New("untestable")
|
||||
|
||||
type fileWatcher struct {
|
||||
logger log.Logger
|
||||
|
||||
// full path to a watched file
|
||||
filename string
|
||||
interval time.Duration
|
||||
daemonHome string
|
||||
filename string // full path to a watched file
|
||||
interval time.Duration
|
||||
|
||||
currentBin string
|
||||
currentInfo upgradetypes.Plan
|
||||
lastModTime time.Time
|
||||
cancel chan bool
|
||||
ticker *time.Ticker
|
||||
needsUpdate bool
|
||||
|
||||
initialized bool
|
||||
needsUpdate bool
|
||||
initialized bool
|
||||
disableRecase bool
|
||||
}
|
||||
|
||||
func newUpgradeFileWatcher(logger log.Logger, filename string, interval time.Duration) (*fileWatcher, error) {
|
||||
func newUpgradeFileWatcher(cfg *Config) (*fileWatcher, error) {
|
||||
filename := cfg.UpgradeInfoFilePath()
|
||||
if filename == "" {
|
||||
return nil, errors.New("filename undefined")
|
||||
}
|
||||
|
||||
filenameAbs, err := filepath.Abs(filename)
|
||||
if err != nil {
|
||||
return nil,
|
||||
fmt.Errorf("invalid path; %s must be a valid file path: %w", filename, err)
|
||||
return nil, fmt.Errorf("invalid path: %s must be a valid file path: %w", filename, err)
|
||||
}
|
||||
|
||||
dirname := filepath.Dir(filename)
|
||||
info, err := os.Stat(dirname)
|
||||
if err != nil || !info.IsDir() {
|
||||
return nil, fmt.Errorf("invalid path; %s must be an existing directory: %w", dirname, err)
|
||||
if info, err := os.Stat(dirname); err != nil || !info.IsDir() {
|
||||
return nil, fmt.Errorf("invalid path: %s must be an existing directory: %w", dirname, err)
|
||||
}
|
||||
|
||||
bin, err := cfg.CurrentBin()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating symlink to genesis: %w", err)
|
||||
}
|
||||
|
||||
return &fileWatcher{
|
||||
logger: logger,
|
||||
filename: filenameAbs,
|
||||
interval: interval,
|
||||
currentInfo: upgradetypes.Plan{},
|
||||
lastModTime: time.Time{},
|
||||
cancel: make(chan bool),
|
||||
ticker: time.NewTicker(interval),
|
||||
needsUpdate: false,
|
||||
initialized: false,
|
||||
daemonHome: cfg.Home,
|
||||
currentBin: bin,
|
||||
filename: filenameAbs,
|
||||
interval: cfg.PollInterval,
|
||||
currentInfo: upgradetypes.Plan{},
|
||||
lastModTime: time.Time{},
|
||||
cancel: make(chan bool),
|
||||
ticker: time.NewTicker(cfg.PollInterval),
|
||||
needsUpdate: false,
|
||||
initialized: false,
|
||||
disableRecase: cfg.DisableRecase,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (fw *fileWatcher) Stop() {
|
||||
close(fw.cancel)
|
||||
fw.ticker.Stop()
|
||||
}
|
||||
|
||||
// pools the filesystem to check for new upgrade currentInfo. currentName is the name
|
||||
// of currently running upgrade. The check is rejected if it finds an upgrade with the same
|
||||
// name.
|
||||
// MonitorUpdate pools the filesystem to check for new upgrade currentInfo.
|
||||
// currentName is the name of currently running upgrade. The check is rejected if it finds
|
||||
// an upgrade with the same name.
|
||||
func (fw *fileWatcher) MonitorUpdate(currentUpgrade upgradetypes.Plan) <-chan struct{} {
|
||||
fw.ticker.Reset(fw.interval)
|
||||
done := make(chan struct{})
|
||||
@ -102,18 +111,48 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool {
|
||||
|
||||
stat, err := os.Stat(fw.filename)
|
||||
if err != nil {
|
||||
// file doesn't exists
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
} else {
|
||||
panic(fmt.Errorf("failed to stat upgrade info file: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// check https://github.com/cosmos/cosmos-sdk/issues/21086
|
||||
// If new file is still empty, wait a small amount of time for write to complete
|
||||
if stat.Size() == 0 {
|
||||
for range 10 {
|
||||
time.Sleep(2 * time.Millisecond)
|
||||
stat, err = os.Stat(fw.filename)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return false
|
||||
} else {
|
||||
panic(fmt.Errorf("failed to stat upgrade info file: %w", err))
|
||||
}
|
||||
}
|
||||
if stat.Size() == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if stat.Size() == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
// no update if the file already exists and has not been modified
|
||||
if !stat.ModTime().After(fw.lastModTime) {
|
||||
return false
|
||||
}
|
||||
|
||||
info, err := parseUpgradeInfoFile(fw.filename)
|
||||
info, err := parseUpgradeInfoFile(fw.filename, fw.disableRecase)
|
||||
if err != nil {
|
||||
zl := fw.logger.Impl().(*zerolog.Logger)
|
||||
zl.Fatal().Err(err).Msg("failed to parse upgrade info file")
|
||||
panic(fmt.Errorf("failed to parse upgrade info file: %w", err))
|
||||
}
|
||||
|
||||
// file exist but too early in height
|
||||
currentHeight, err := fw.checkHeight()
|
||||
if (err != nil || currentHeight < info.Height) && !errors.Is(err, errUntestAble) { // ignore this check for tests
|
||||
return false
|
||||
}
|
||||
|
||||
@ -123,7 +162,7 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool {
|
||||
fw.currentInfo = info
|
||||
fw.lastModTime = stat.ModTime()
|
||||
|
||||
// Heuristic: Deamon has restarted, so we don't know if we successfully
|
||||
// Heuristic: Daemon has restarted, so we don't know if we successfully
|
||||
// downloaded the upgrade or not. So we try to compare the running upgrade
|
||||
// name (read from the cosmovisor file) with the upgrade info.
|
||||
if !strings.EqualFold(currentUpgrade.Name, fw.currentInfo.Name) {
|
||||
@ -142,27 +181,64 @@ func (fw *fileWatcher) CheckUpdate(currentUpgrade upgradetypes.Plan) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func parseUpgradeInfoFile(filename string) (upgradetypes.Plan, error) {
|
||||
var ui upgradetypes.Plan
|
||||
// checkHeight checks if the current block height
|
||||
func (fw *fileWatcher) checkHeight() (int64, error) {
|
||||
if testing.Testing() { // we cannot test the command in the test environment
|
||||
return 0, errUntestAble
|
||||
}
|
||||
|
||||
f, err := os.Open(filename)
|
||||
result, err := exec.Command(fw.currentBin, "status", "--home", fw.daemonHome).CombinedOutput() //nolint:gosec // we want to execute the status command
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
type response struct {
|
||||
SyncInfo struct {
|
||||
LatestBlockHeight string `json:"latest_block_height"`
|
||||
} `json:"sync_info"`
|
||||
AnotherCasingSyncInfo struct {
|
||||
LatestBlockHeight string `json:"latest_block_height"`
|
||||
} `json:"SyncInfo"`
|
||||
}
|
||||
|
||||
var resp response
|
||||
if err := json.Unmarshal(result, &resp); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if resp.SyncInfo.LatestBlockHeight != "" {
|
||||
return strconv.ParseInt(resp.SyncInfo.LatestBlockHeight, 10, 64)
|
||||
} else if resp.AnotherCasingSyncInfo.LatestBlockHeight != "" {
|
||||
return strconv.ParseInt(resp.AnotherCasingSyncInfo.LatestBlockHeight, 10, 64)
|
||||
}
|
||||
|
||||
return 0, errors.New("latest block height is empty")
|
||||
}
|
||||
|
||||
func parseUpgradeInfoFile(filename string, disableRecase bool) (upgradetypes.Plan, error) {
|
||||
f, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return upgradetypes.Plan{}, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
d := json.NewDecoder(f)
|
||||
if err := d.Decode(&ui); err != nil {
|
||||
if len(f) == 0 {
|
||||
return upgradetypes.Plan{}, fmt.Errorf("empty upgrade-info.json in %q", filename)
|
||||
}
|
||||
|
||||
var upgradePlan upgradetypes.Plan
|
||||
if err := json.Unmarshal(f, &upgradePlan); err != nil {
|
||||
return upgradetypes.Plan{}, err
|
||||
}
|
||||
|
||||
// required values must be set
|
||||
if ui.Height <= 0 || ui.Name == "" {
|
||||
return upgradetypes.Plan{}, fmt.Errorf("invalid upgrade-info.json content; name and height must be not empty; got: %v", ui)
|
||||
if err := upgradePlan.ValidateBasic(); err != nil {
|
||||
return upgradetypes.Plan{}, fmt.Errorf("invalid upgrade-info.json content: %w, got: %v", err, upgradePlan)
|
||||
}
|
||||
|
||||
// normalize name to prevent operator error in upgrade name case sensitivity errors.
|
||||
ui.Name = strings.ToLower(ui.Name)
|
||||
if !disableRecase {
|
||||
upgradePlan.Name = strings.ToLower(upgradePlan.Name)
|
||||
}
|
||||
|
||||
return ui, err
|
||||
return upgradePlan, nil
|
||||
}
|
||||
|
||||
@ -13,52 +13,65 @@ func TestParseUpgradeInfoFile(t *testing.T) {
|
||||
cases := []struct {
|
||||
filename string
|
||||
expectUpgrade upgradetypes.Plan
|
||||
expectErr bool
|
||||
disableRecase bool
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
filename: "f1-good.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{Name: "upgrade1", Info: "some info", Height: 123},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
filename: "f2-normalized-name.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{Name: "upgrade2", Info: "some info", Height: 125},
|
||||
expectErr: false,
|
||||
},
|
||||
{
|
||||
filename: "f2-normalized-name.json",
|
||||
disableRecase: true,
|
||||
expectUpgrade: upgradetypes.Plan{Name: "Upgrade2", Info: "some info", Height: 125},
|
||||
},
|
||||
{
|
||||
filename: "f2-bad-type.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "cannot unmarshal number into Go struct",
|
||||
},
|
||||
{
|
||||
filename: "f2-bad-type-2.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "height must be greater than 0: invalid request",
|
||||
},
|
||||
{
|
||||
filename: "f3-empty.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "empty upgrade-info.json in",
|
||||
},
|
||||
{
|
||||
filename: "f4-empty-obj.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "invalid upgrade-info.json content: name cannot be empty",
|
||||
},
|
||||
{
|
||||
filename: "f5-partial-obj-1.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "height must be greater than 0",
|
||||
},
|
||||
{
|
||||
filename: "f5-partial-obj-2.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "name cannot be empty: invalid request",
|
||||
},
|
||||
{
|
||||
filename: "unknown.json",
|
||||
filename: "non-existent.json",
|
||||
disableRecase: false,
|
||||
expectUpgrade: upgradetypes.Plan{},
|
||||
expectErr: true,
|
||||
expectErr: "no such file or directory",
|
||||
},
|
||||
}
|
||||
|
||||
@ -66,9 +79,10 @@ func TestParseUpgradeInfoFile(t *testing.T) {
|
||||
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))
|
||||
if tc.expectErr {
|
||||
ui, err := parseUpgradeInfoFile(filepath.Join(".", "testdata", "upgrade-files", tc.filename), tc.disableRecase)
|
||||
if tc.expectErr != "" {
|
||||
require.Error(err)
|
||||
require.Contains(err.Error(), tc.expectErr)
|
||||
} else {
|
||||
require.NoError(err)
|
||||
require.Equal(tc.expectUpgrade, ui)
|
||||
|
||||
20
tools/cosmovisor/testdata/dontdie/cosmovisor/genesis/bin/dummyd
vendored
Executable file
20
tools/cosmovisor/testdata/dontdie/cosmovisor/genesis/bin/dummyd
vendored
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
|
||||
|
||||
warn() {
|
||||
echo "WARN Need Flush"
|
||||
}
|
||||
|
||||
trap warn INT
|
||||
echo Genesis $@
|
||||
sleep 1
|
||||
test -z $4 && exit 1001
|
||||
echo 'UPGRADE "Chain2" NEEDED at height: 49: {}'
|
||||
echo '{"name":"Chain2","height":49,"info":""}' > $4
|
||||
# Shutdown grace test waits 2 seconds for flush
|
||||
# Flush within 1 second
|
||||
sleep 1
|
||||
echo 'Flushed'
|
||||
# Now chain is halted for shutdown grace test.
|
||||
sleep 2
|
||||
echo Did not kill in time. Never should be printed!!!
|
||||
6
tools/cosmovisor/testdata/dontdie/cosmovisor/upgrades/chain2/bin/dummyd
vendored
Executable file
6
tools/cosmovisor/testdata/dontdie/cosmovisor/upgrades/chain2/bin/dummyd
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo Chain 2 is live!
|
||||
echo Args: $@
|
||||
sleep 1
|
||||
echo Finished successfully
|
||||
0
tools/cosmovisor/testdata/dontdie/data/.gitkeep
vendored
Normal file
0
tools/cosmovisor/testdata/dontdie/data/.gitkeep
vendored
Normal file
@ -6,7 +6,13 @@ 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/main/tools/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:13767eb0b57bf51a0f43d49f6277d5df97d4dec672dc39822d23a82fb8e70a7b\"}}"}' >$3
|
||||
cat > "$3" <<EOL
|
||||
{
|
||||
"name": "chain2",
|
||||
"height": 49,
|
||||
"info": "{\"binaries\":{\"linux/amd64\": \"https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:e84db844e123e3bb888c9ec52f6768ae29fbef7c17eaf8fc7431485a65dae6b9\",\"linux/arm64\": \"https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:e84db844e123e3bb888c9ec52f6768ae29fbef7c17eaf8fc7431485a65dae6b9\",\"darwin/amd64\": \"https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:e84db844e123e3bb888c9ec52f6768ae29fbef7c17eaf8fc7431485a65dae6b9\",\"darwin/arm64\": \"https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain2-zip_bin/autod.zip?checksum=sha256:e84db844e123e3bb888c9ec52f6768ae29fbef7c17eaf8fc7431485a65dae6b9\"}}"
|
||||
}
|
||||
EOL
|
||||
|
||||
sleep 0.1
|
||||
echo Never should be printed!!!
|
||||
|
||||
4
tools/cosmovisor/testdata/download/cosmovisor/preupgrade.sh
vendored
Executable file
4
tools/cosmovisor/testdata/download/cosmovisor/preupgrade.sh
vendored
Executable file
@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
echo "OK" > upgrade_name_$1_height_$2
|
||||
echo PWD=`pwd`
|
||||
echo upgrade_name_$1_height_$2
|
||||
9
tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd
vendored
Executable file
9
tools/cosmovisor/testdata/norecase/cosmovisor/genesis/bin/dummyd
vendored
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo Genesis $@
|
||||
sleep 1
|
||||
test -z $4 && exit 1001
|
||||
echo 'UPGRADE "Chain2" NEEDED at height: 49: {}'
|
||||
echo '{"name":"Chain2","height":49,"info":""}' > $4
|
||||
sleep 2
|
||||
echo Never should be printed!!!
|
||||
6
tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd
vendored
Executable file
6
tools/cosmovisor/testdata/norecase/cosmovisor/upgrades/Chain2/bin/dummyd
vendored
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo Chain 2 is live!
|
||||
echo Args: $@
|
||||
sleep 1
|
||||
echo Finished successfully
|
||||
0
tools/cosmovisor/testdata/norecase/data/.gitkeep
vendored
Normal file
0
tools/cosmovisor/testdata/norecase/data/.gitkeep
vendored
Normal file
@ -6,8 +6,13 @@ echo Args: $@
|
||||
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/main/tools/cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json?checksum=sha256:a95075f4dd83bc9f0f556ef73e64ce000f9bf3a6beeb9d4ae32f594b1417ef7a\"}}"}' > $3
|
||||
echo '{"name":"chain3","height":936,"info":"https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json?checksum=sha256:a95075f4dd83bc9f0f556ef73e64ce000f9bf3a6beeb9d4ae32f594b1417ef7a"}' >$3
|
||||
cat > "$3" <<EOL
|
||||
{
|
||||
"name": "chain3",
|
||||
"height": 936,
|
||||
"info": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/ref_to_chain3-zip_dir.json?checksum=sha256:6758973f7404f6d34381029931b85826fc7a6315584ede03bad4c19e9b787f6c"
|
||||
}
|
||||
EOL
|
||||
|
||||
sleep 1
|
||||
echo 'Do not print'
|
||||
|
||||
Binary file not shown.
@ -1,5 +1,8 @@
|
||||
{
|
||||
"binaries": {
|
||||
"linux/amd64": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4"
|
||||
"linux/amd64": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4",
|
||||
"linux/arm64": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4",
|
||||
"darwin/amd64": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4",
|
||||
"darwin/arm64": "https://github.com/cosmos/cosmos-sdk/raw/main/tools/cosmovisor/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -39,15 +39,11 @@ func UpgradeBinary(logger log.Logger, cfg *Config, p upgradetypes.Plan) error {
|
||||
return fmt.Errorf("unhandled error: %w", err)
|
||||
}
|
||||
|
||||
upgradeInfo, err := plan.ParseInfo(p.Info)
|
||||
upgradeInfo, err := plan.ParseInfo(p.Info, plan.ParseOptionEnforceChecksum(cfg.DownloadMustHaveChecksum))
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse upgrade info: %w", err)
|
||||
}
|
||||
|
||||
if err := upgradeInfo.ValidateFull(cfg.Name); err != nil {
|
||||
return fmt.Errorf("invalid binaries: %w", err)
|
||||
}
|
||||
|
||||
url, err := GetBinaryURL(upgradeInfo.Binaries)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
//go:build linux
|
||||
// +build linux
|
||||
//go:build darwin || linux
|
||||
|
||||
package cosmovisor_test
|
||||
|
||||
@ -7,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -28,13 +28,21 @@ func TestUpgradeTestSuite(t *testing.T) {
|
||||
}
|
||||
|
||||
func (s *upgradeTestSuite) TestCurrentBin() {
|
||||
home := copyTestData(s.T(), "validate")
|
||||
cfg := cosmovisor.Config{Home: home, Name: "dummyd"}
|
||||
cfg := prepareConfig(
|
||||
s.T(),
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/validate"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
},
|
||||
)
|
||||
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(cfg.GenesisBin(), currentBin)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin)
|
||||
|
||||
// ensure we cannot set this to an invalid value
|
||||
for _, name := range []string{"missing", "nobin"} {
|
||||
@ -43,7 +51,10 @@ func (s *upgradeTestSuite) TestCurrentBin() {
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(cfg.GenesisBin(), currentBin, name)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin, name)
|
||||
}
|
||||
|
||||
// try a few times to make sure this can be reproduced
|
||||
@ -55,29 +66,46 @@ func (s *upgradeTestSuite) TestCurrentBin() {
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(cfg.UpgradeBin(name), currentBin)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.UpgradeBin(name))
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *upgradeTestSuite) TestCurrentAlwaysSymlinkToDirectory() {
|
||||
home := copyTestData(s.T(), "validate")
|
||||
cfg := cosmovisor.Config{Home: home, Name: "dummyd"}
|
||||
cfg := prepareConfig(
|
||||
s.T(),
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/validate"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
},
|
||||
)
|
||||
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(cfg.GenesisBin(), currentBin)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin)
|
||||
s.assertCurrentLink(cfg, "genesis")
|
||||
|
||||
err = cfg.SetCurrentUpgrade(upgradetypes.Plan{Name: "chain2"})
|
||||
s.Require().NoError(err)
|
||||
currentBin, err = cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(cfg.UpgradeBin("chain2"), currentBin)
|
||||
|
||||
eval, err := filepath.EvalSymlinks(cfg.UpgradeBin("chain2"))
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(eval, currentBin)
|
||||
s.assertCurrentLink(cfg, filepath.Join("upgrades", "chain2"))
|
||||
}
|
||||
|
||||
func (s *upgradeTestSuite) assertCurrentLink(cfg cosmovisor.Config, target string) {
|
||||
func (s *upgradeTestSuite) assertCurrentLink(cfg *cosmovisor.Config, target string) {
|
||||
link := filepath.Join(cfg.Root(), "current")
|
||||
|
||||
// ensure this is a symlink
|
||||
info, err := os.Lstat(link)
|
||||
s.Require().NoError(err)
|
||||
@ -85,20 +113,29 @@ func (s *upgradeTestSuite) assertCurrentLink(cfg cosmovisor.Config, target strin
|
||||
|
||||
dest, err := os.Readlink(link)
|
||||
s.Require().NoError(err)
|
||||
expected := filepath.Join(cfg.Root(), target)
|
||||
s.Require().Equal(expected, dest)
|
||||
s.Require().Equal(target, dest)
|
||||
}
|
||||
|
||||
// TODO: test with download (and test all download functions)
|
||||
func (s *upgradeTestSuite) TestUpgradeBinaryNoDownloadUrl() {
|
||||
home := copyTestData(s.T(), "validate")
|
||||
cfg := &cosmovisor.Config{Home: home, Name: "dummyd", AllowDownloadBinaries: true}
|
||||
cfg := prepareConfig(
|
||||
s.T(),
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/validate"),
|
||||
cosmovisor.Config{
|
||||
Name: "dummyd",
|
||||
AllowDownloadBinaries: true,
|
||||
},
|
||||
)
|
||||
|
||||
logger := log.NewLogger(os.Stdout).With(log.ModuleKey, "cosmovisor")
|
||||
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(cfg.GenesisBin(), currentBin)
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin)
|
||||
|
||||
// do upgrade ignores bad files
|
||||
for _, name := range []string{"missing", "nobin"} {
|
||||
@ -107,7 +144,11 @@ func (s *upgradeTestSuite) TestUpgradeBinaryNoDownloadUrl() {
|
||||
s.Require().Error(err, name)
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
s.Require().Equal(cfg.GenesisBin(), currentBin, name)
|
||||
|
||||
rPath, err := filepath.EvalSymlinks(cfg.GenesisBin())
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin, name)
|
||||
}
|
||||
|
||||
// make sure it updates a few times
|
||||
@ -121,7 +162,10 @@ func (s *upgradeTestSuite) TestUpgradeBinaryNoDownloadUrl() {
|
||||
currentBin, err := cfg.CurrentBin()
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(upgradeBin, currentBin)
|
||||
rPath, err := filepath.EvalSymlinks(upgradeBin)
|
||||
s.Require().NoError(err)
|
||||
|
||||
s.Require().Equal(rPath, currentBin)
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,26 +179,25 @@ func (s *upgradeTestSuite) TestUpgradeBinary() {
|
||||
}{
|
||||
"get raw binary with checksum": {
|
||||
// sha256sum ./testdata/repo/raw_binary/autod
|
||||
url: "./testdata/repo/raw_binary/autod?checksum=sha256:e6bc7851600a2a9917f7bf88eb7bdee1ec162c671101485690b4deb089077b0d",
|
||||
url: workDir + "/testdata/repo/raw_binary/autod?checksum=sha256:e6bc7851600a2a9917f7bf88eb7bdee1ec162c671101485690b4deb089077b0d",
|
||||
canDownload: true,
|
||||
validBinary: true,
|
||||
},
|
||||
"get raw binary with invalid checksum": {
|
||||
url: "./testdata/repo/raw_binary/autod?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
url: workDir + "/testdata/repo/raw_binary/autod?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
canDownload: false,
|
||||
},
|
||||
"get zipped directory with valid checksum": {
|
||||
// sha256sum ./testdata/repo/chain3-zip_dir/autod.zip
|
||||
url: "./testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4",
|
||||
url: workDir + "/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:8951f52a0aea8617de0ae459a20daf704c29d259c425e60d520e363df0f166b4",
|
||||
canDownload: true,
|
||||
validBinary: true,
|
||||
},
|
||||
"get zipped directory with invalid checksum": {
|
||||
url: "./testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
url: workDir + "/testdata/repo/chain3-zip_dir/autod.zip?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
canDownload: false,
|
||||
},
|
||||
"invalid url": {
|
||||
url: "./testdata/repo/bad_dir/autod?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
url: workDir + "/testdata/repo/bad_dir/autod?checksum=sha256:73e2bd6cbb99261733caf137015d5cc58e3f96248d8b01da68be8564989dd906",
|
||||
canDownload: false,
|
||||
},
|
||||
"valid remote": {
|
||||
@ -167,14 +210,14 @@ func (s *upgradeTestSuite) TestUpgradeBinary() {
|
||||
for label, tc := range cases {
|
||||
s.Run(label, func() {
|
||||
var err error
|
||||
// make temp dir
|
||||
home := copyTestData(s.T(), "download")
|
||||
|
||||
cfg := &cosmovisor.Config{
|
||||
Home: home,
|
||||
Name: "autod",
|
||||
AllowDownloadBinaries: true,
|
||||
}
|
||||
cfg := prepareConfig(
|
||||
s.T(),
|
||||
fmt.Sprintf("%s/%s", workDir, "testdata/download"),
|
||||
cosmovisor.Config{
|
||||
Name: "autod",
|
||||
AllowDownloadBinaries: true,
|
||||
},
|
||||
)
|
||||
|
||||
url := tc.url
|
||||
if strings.HasPrefix(url, "./") {
|
||||
@ -198,17 +241,37 @@ func (s *upgradeTestSuite) TestUpgradeBinary() {
|
||||
}
|
||||
|
||||
func (s *upgradeTestSuite) TestOsArch() {
|
||||
// all download tests will fail if we are not on linux...
|
||||
s.Require().Equal("linux/amd64", cosmovisor.OSArch())
|
||||
// all download tests will fail if we are not on linux or darwin...
|
||||
hosts := []string{
|
||||
"linux/arm64",
|
||||
"linux/amd64",
|
||||
"darwin/amd64",
|
||||
"darwin/arm64",
|
||||
}
|
||||
|
||||
s.Require().True(slices.Contains(hosts, cosmovisor.OSArch()))
|
||||
}
|
||||
|
||||
// copyTestData will make a tempdir and then
|
||||
// "cp -r" a subdirectory under testdata there
|
||||
// returns the directory (which can now be used as Config.Home) and modified safely
|
||||
func copyTestData(t *testing.T, subdir string) string {
|
||||
func copyTestData(t *testing.T, testData string) string {
|
||||
t.Helper()
|
||||
tmpdir := t.TempDir()
|
||||
require.NoError(t, copy.Copy(filepath.Join("testdata", subdir), tmpdir))
|
||||
require.NoError(t, copy.Copy(testData, tmpdir))
|
||||
|
||||
return tmpdir
|
||||
}
|
||||
|
||||
func prepareConfig(t *testing.T, testData string, config cosmovisor.Config) *cosmovisor.Config {
|
||||
t.Helper()
|
||||
|
||||
tmpdir := copyTestData(t, testData)
|
||||
|
||||
config.Home = tmpdir
|
||||
|
||||
err := os.Chdir(config.Root())
|
||||
require.NoError(t, err)
|
||||
|
||||
return &config
|
||||
}
|
||||
|
||||
1
tools/hubl/.gitignore
vendored
1
tools/hubl/.gitignore
vendored
@ -1 +0,0 @@
|
||||
/hubl
|
||||
@ -1,32 +0,0 @@
|
||||
<!--
|
||||
Guiding Principles:
|
||||
|
||||
Changelogs are for humans, not machines.
|
||||
There should be an entry for every single version.
|
||||
The same types of changes should be grouped.
|
||||
Versions and sections should be linkable.
|
||||
The latest version comes first.
|
||||
The release date of each version is displayed.
|
||||
Mention whether you follow Semantic Versioning.
|
||||
|
||||
Usage:
|
||||
|
||||
Change log entries are to be added to the Unreleased section under the
|
||||
appropriate stanza (see below). Each entry should ideally include a tag and
|
||||
the Github issue reference in the following format:
|
||||
|
||||
* (<tag>) [#<issue-number>] Changelog message.
|
||||
|
||||
Types of changes (Stanzas):
|
||||
|
||||
"Features" for new features.
|
||||
"Improvements" for changes in existing functionality.
|
||||
"Deprecated" for soon-to-be removed features.
|
||||
"Bug Fixes" for any bug fixes.
|
||||
"API Breaking" for breaking exported APIs used by developers building on SDK.
|
||||
Ref: https://keepachangelog.com/en/1.0.0/
|
||||
-->
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Unreleased]
|
||||
@ -1,11 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
all: hubl test
|
||||
|
||||
hubl:
|
||||
go build -mod=readonly ./cmd/hubl
|
||||
|
||||
test:
|
||||
go test -mod=readonly -race ./...
|
||||
|
||||
.PHONY: all hubl test
|
||||
@ -1,73 +0,0 @@
|
||||
---
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
# Hubl
|
||||
|
||||
`Hubl` is a tool that allows you to query any Cosmos SDK based blockchain.
|
||||
It takes advantage of the new [AutoCLI](https://pkg.go.dev/github.com/cosmos/cosmos-sdk/client/v2@v2.0.0-20220916140313-c5245716b516/cli) feature <!-- TODO replace with AutoCLI docs --> of the Cosmos SDK.
|
||||
|
||||
## Installation
|
||||
|
||||
Hubl can be installed using `go install`:
|
||||
|
||||
```shell
|
||||
go install cosmossdk.io/tools/hubl/cmd/hubl@latest
|
||||
```
|
||||
|
||||
Or build from source:
|
||||
|
||||
```shell
|
||||
git clone --depth=1 https://github.com/cosmos/cosmos-sdk
|
||||
make hubl
|
||||
```
|
||||
|
||||
The binary will be located in `tools/hubl`.
|
||||
|
||||
## Usage
|
||||
|
||||
```shell
|
||||
hubl --help
|
||||
```
|
||||
|
||||
### Add chain
|
||||
|
||||
To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (<https://github.com/cosmos/chain-registry>).
|
||||
|
||||
If the chain is not listed in the chain registry, you can use any unique name.
|
||||
|
||||
```shell
|
||||
hubl init [chain-name]
|
||||
hubl init regen
|
||||
```
|
||||
|
||||
The chain configuration is stored in `~/.hubl/config.toml`.
|
||||
|
||||
:::tip
|
||||
|
||||
When using an unsecure gRPC endpoint, change the `insecure` field to `true` in the config file.
|
||||
|
||||
```toml
|
||||
[chains]
|
||||
[chains.regen]
|
||||
[[chains.regen.trusted-grpc-endpoints]]
|
||||
endpoint = 'localhost:9090'
|
||||
insecure = true
|
||||
```
|
||||
|
||||
Or use the `--insecure` flag:
|
||||
|
||||
```shell
|
||||
hubl init regen --insecure
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Query
|
||||
|
||||
To query a chain, you can use the `query` command.
|
||||
Then specify which module you want to query and the query itself.
|
||||
|
||||
```shell
|
||||
hubl regen query auth module-accounts
|
||||
```
|
||||
@ -1,16 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cosmossdk.io/tools/hubl/internal"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cmd, err := internal.RootCommand()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if err = cmd.Execute(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
@ -1,156 +0,0 @@
|
||||
module cosmossdk.io/tools/hubl
|
||||
|
||||
go 1.23
|
||||
|
||||
require (
|
||||
cosmossdk.io/api v0.7.5
|
||||
cosmossdk.io/client/v2 v2.0.0-20230719143845-dff6b0e26aa4
|
||||
cosmossdk.io/errors v1.0.1
|
||||
github.com/cockroachdb/errors v1.11.1
|
||||
github.com/cosmos/cosmos-sdk v0.50.0-rc.1
|
||||
github.com/manifoldco/promptui v0.9.0
|
||||
github.com/pelletier/go-toml/v2 v2.1.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
google.golang.org/grpc v1.63.2
|
||||
google.golang.org/protobuf v1.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
cosmossdk.io/collections v0.4.0 // indirect
|
||||
cosmossdk.io/core v0.11.0 // indirect
|
||||
cosmossdk.io/depinject v1.0.0-alpha.4 // indirect
|
||||
cosmossdk.io/log v1.4.1 // indirect
|
||||
cosmossdk.io/math v1.3.0 // indirect
|
||||
cosmossdk.io/store v1.0.0 // indirect
|
||||
cosmossdk.io/x/tx v0.13.4 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.1 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
|
||||
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.11 // indirect
|
||||
github.com/cometbft/cometbft-db v0.9.1 // 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/go-bip39 v1.0.0 // indirect
|
||||
github.com/cosmos/gogogateway v1.2.0 // indirect
|
||||
github.com/cosmos/gogoproto v1.7.0 // indirect
|
||||
github.com/cosmos/iavl v1.0.1 // indirect
|
||||
github.com/cosmos/ics23/go v0.11.0 // indirect
|
||||
github.com/cosmos/ledger-cosmos-go v0.13.3 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/emicklei/dot v1.6.1 // indirect
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/getsentry/sentry-go v0.27.0 // indirect
|
||||
github.com/go-kit/kit v0.12.0 // indirect
|
||||
github.com/go-kit/log v0.2.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
|
||||
github.com/gogo/googleapis v1.4.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.2.0 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.1.2 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/orderedcode v0.0.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
|
||||
github.com/hashicorp/go-metrics v0.5.1 // indirect
|
||||
github.com/hashicorp/go-plugin v1.5.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
|
||||
github.com/huandu/skiplist v1.2.0 // indirect
|
||||
github.com/iancoleman/strcase v0.3.0 // indirect
|
||||
github.com/improbable-eng/grpc-web v0.15.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jmhodges/levigo v1.0.0 // indirect
|
||||
github.com/klauspost/compress v1.17.7 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lib/pq v1.10.7 // indirect
|
||||
github.com/linxGnu/grocksdb v1.8.12 // indirect
|
||||
github.com/magiconair/properties v1.8.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/highwayhash v1.0.2 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mtibben/percent v0.2.1 // indirect
|
||||
github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.18.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.0 // indirect
|
||||
github.com/prometheus/common v0.47.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
|
||||
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||
github.com/rs/cors v1.8.3 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sasha-s/go-deadlock v0.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.18.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
|
||||
github.com/tendermint/go-amino v0.16.0 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v0.14.3 // indirect
|
||||
go.etcd.io/bbolt v1.3.8 // indirect
|
||||
go.uber.org/multierr v1.10.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/term v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
gotest.tools/v3 v3.5.1 // indirect
|
||||
nhooyr.io/websocket v1.8.6 // indirect
|
||||
pgregory.net/rapid v1.1.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
1026
tools/hubl/go.sum
1026
tools/hubl/go.sum
File diff suppressed because it is too large
Load Diff
@ -1,280 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/reflection/grpc_reflection_v1"
|
||||
"google.golang.org/protobuf/proto"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
reflectionv1beta1 "cosmossdk.io/api/cosmos/base/reflection/v1beta1"
|
||||
)
|
||||
|
||||
// loadFileDescriptorsGRPCReflection attempts to load the file descriptor set using gRPC reflection when cosmos.reflection.v1
|
||||
// is unavailable.
|
||||
func loadFileDescriptorsGRPCReflection(ctx context.Context, client *grpc.ClientConn) (*descriptorpb.FileDescriptorSet, error) {
|
||||
fmt.Printf("This chain does not support cosmos.reflection.v1 yet... attempting to use a fallback. Some features may be unsupported and it may not be possible to read all data.\n")
|
||||
|
||||
var interfaceImplNames []string
|
||||
cosmosReflectBetaClient := reflectionv1beta1.NewReflectionServiceClient(client)
|
||||
interfacesRes, err := cosmosReflectBetaClient.ListAllInterfaces(ctx, &reflectionv1beta1.ListAllInterfacesRequest{})
|
||||
if err == nil {
|
||||
for _, iface := range interfacesRes.InterfaceNames {
|
||||
implRes, err := cosmosReflectBetaClient.ListImplementations(ctx, &reflectionv1beta1.ListImplementationsRequest{
|
||||
InterfaceName: iface,
|
||||
})
|
||||
if err == nil {
|
||||
interfaceImplNames = append(interfaceImplNames, implRes.ImplementationMessageNames...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reflectClient, err := grpc_reflection_v1.NewServerReflectionClient(client).ServerReflectionInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fdMap := map[string]*descriptorpb.FileDescriptorProto{}
|
||||
waitListServiceRes := make(chan *grpc_reflection_v1.ListServiceResponse)
|
||||
waitc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
in, err := reflectClient.Recv()
|
||||
if err == io.EOF {
|
||||
// read done.
|
||||
close(waitc)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch res := in.MessageResponse.(type) {
|
||||
case *grpc_reflection_v1.ServerReflectionResponse_ListServicesResponse:
|
||||
waitListServiceRes <- res.ListServicesResponse
|
||||
case *grpc_reflection_v1.ServerReflectionResponse_FileDescriptorResponse:
|
||||
processFileDescriptorsResponse(res, fdMap)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
if err = reflectClient.Send(&grpc_reflection_v1.ServerReflectionRequest{
|
||||
MessageRequest: &grpc_reflection_v1.ServerReflectionRequest_ListServices{},
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
listServiceRes := <-waitListServiceRes
|
||||
|
||||
for _, response := range listServiceRes.Service {
|
||||
err = reflectClient.Send(&grpc_reflection_v1.ServerReflectionRequest{
|
||||
MessageRequest: &grpc_reflection_v1.ServerReflectionRequest_FileContainingSymbol{
|
||||
FileContainingSymbol: response.Name,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, msgName := range interfaceImplNames {
|
||||
err = reflectClient.Send(&grpc_reflection_v1.ServerReflectionRequest{
|
||||
MessageRequest: &grpc_reflection_v1.ServerReflectionRequest_FileContainingSymbol{
|
||||
FileContainingSymbol: msgName,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = reflectClient.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
<-waitc
|
||||
|
||||
// we loop through all the file descriptor dependencies to capture any file descriptors we haven't loaded yet
|
||||
cantFind := map[string]bool{}
|
||||
for {
|
||||
missing := missingFileDescriptors(fdMap, cantFind)
|
||||
if len(missing) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
err = addMissingFileDescriptors(ctx, client, fdMap, missing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// mark all deps that we aren't able to resolve as can't find, so we don't keep looping and get a 429 error
|
||||
for _, dep := range missing {
|
||||
if fdMap[dep] == nil {
|
||||
cantFind[dep] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for dep := range cantFind {
|
||||
fmt.Printf("Warning: can't find %s.\n", dep)
|
||||
}
|
||||
|
||||
fdSet := &descriptorpb.FileDescriptorSet{}
|
||||
for _, descriptorProto := range fdMap {
|
||||
fdSet.File = append(fdSet.File, descriptorProto)
|
||||
}
|
||||
|
||||
return fdSet, nil
|
||||
}
|
||||
|
||||
func processFileDescriptorsResponse(res *grpc_reflection_v1.ServerReflectionResponse_FileDescriptorResponse, fdMap map[string]*descriptorpb.FileDescriptorProto) {
|
||||
for _, bz := range res.FileDescriptorResponse.FileDescriptorProto {
|
||||
fd := &descriptorpb.FileDescriptorProto{}
|
||||
err := proto.Unmarshal(bz, fd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fdMap[fd.GetName()] = fd
|
||||
}
|
||||
}
|
||||
|
||||
func missingFileDescriptors(fdMap map[string]*descriptorpb.FileDescriptorProto, cantFind map[string]bool) []string {
|
||||
var missing []string
|
||||
for _, descriptorProto := range fdMap {
|
||||
for _, dep := range descriptorProto.Dependency {
|
||||
if fdMap[dep] == nil && !cantFind[dep] /* skip deps we've marked as can't find */ {
|
||||
missing = append(missing, dep)
|
||||
}
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
func addMissingFileDescriptors(ctx context.Context, client *grpc.ClientConn, fdMap map[string]*descriptorpb.FileDescriptorProto, missingFiles []string) error {
|
||||
reflectClient, err := grpc_reflection_v1.NewServerReflectionClient(client).ServerReflectionInfo(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
waitc := make(chan struct{})
|
||||
go func() {
|
||||
for {
|
||||
in, err := reflectClient.Recv()
|
||||
if err == io.EOF {
|
||||
// read done.
|
||||
close(waitc)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if res, ok := in.MessageResponse.(*grpc_reflection_v1.ServerReflectionResponse_FileDescriptorResponse); ok {
|
||||
processFileDescriptorsResponse(res, fdMap)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for _, file := range missingFiles {
|
||||
err = reflectClient.Send(&grpc_reflection_v1.ServerReflectionRequest{
|
||||
MessageRequest: &grpc_reflection_v1.ServerReflectionRequest_FileByFilename{
|
||||
FileByFilename: file,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = reflectClient.CloseSend()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-waitc
|
||||
return nil
|
||||
}
|
||||
|
||||
func guessAutocli(files *protoregistry.Files) *autocliv1.AppOptionsResponse {
|
||||
fmt.Printf("This chain does not support autocli directly yet. Using some default mappings in the meantime to support a subset of the available services.\n")
|
||||
res := map[string]*autocliv1.ModuleOptions{}
|
||||
files.RangeFiles(func(descriptor protoreflect.FileDescriptor) bool {
|
||||
services := descriptor.Services()
|
||||
n := services.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
service := services.Get(i)
|
||||
serviceName := service.FullName()
|
||||
mapping, ok := defaultAutocliMappings[serviceName]
|
||||
if ok {
|
||||
parts := strings.Split(mapping, " ")
|
||||
numParts := len(parts)
|
||||
if numParts < 2 || numParts > 3 {
|
||||
fmt.Printf("Warning: bad mapping %q found for %q\n", mapping, serviceName)
|
||||
continue
|
||||
}
|
||||
|
||||
modOpts := res[parts[0]]
|
||||
if modOpts == nil {
|
||||
modOpts = &autocliv1.ModuleOptions{}
|
||||
res[parts[0]] = modOpts
|
||||
}
|
||||
|
||||
switch parts[1] {
|
||||
case "query":
|
||||
if modOpts.Query == nil {
|
||||
modOpts.Query = &autocliv1.ServiceCommandDescriptor{
|
||||
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{},
|
||||
}
|
||||
}
|
||||
if numParts == 3 {
|
||||
modOpts.Query.SubCommands[parts[2]] = &autocliv1.ServiceCommandDescriptor{Service: string(serviceName)}
|
||||
} else {
|
||||
modOpts.Query.Service = string(serviceName)
|
||||
}
|
||||
case "tx":
|
||||
if modOpts.Tx == nil {
|
||||
modOpts.Tx = &autocliv1.ServiceCommandDescriptor{
|
||||
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{},
|
||||
}
|
||||
}
|
||||
if numParts == 3 {
|
||||
modOpts.Tx.SubCommands[parts[2]] = &autocliv1.ServiceCommandDescriptor{Service: string(serviceName)}
|
||||
} else {
|
||||
modOpts.Tx.Service = string(serviceName)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("Warning: bad mapping %q found for %q\n", mapping, serviceName)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return &autocliv1.AppOptionsResponse{ModuleOptions: res}
|
||||
}
|
||||
|
||||
var defaultAutocliMappings = map[protoreflect.FullName]string{
|
||||
"cosmos.auth.v1beta1.Query": "auth query",
|
||||
"cosmos.authz.v1beta1.Query": "authz query",
|
||||
"cosmos.bank.v1beta1.Query": "bank query",
|
||||
"cosmos.distribution.v1beta1.Query": "distribution query",
|
||||
"cosmos.evidence.v1.Query": "evidence query",
|
||||
"cosmos.feegrant.v1beta1.Query": "feegrant query",
|
||||
"cosmos.gov.v1.Query": "gov query",
|
||||
"cosmos.gov.v1beta1.Query": "gov query v1beta1",
|
||||
"cosmos.group.v1.Query": "group query",
|
||||
"cosmos.mint.v1beta1.Query": "mint query",
|
||||
"cosmos.params.v1beta1.Query": "params query",
|
||||
"cosmos.slashing.v1beta1.Query": "slashing query",
|
||||
"cosmos.staking.v1beta1.Query": "staking query",
|
||||
"cosmos.upgrade.v1.Query": "upgrade query",
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
|
||||
"cosmossdk.io/errors"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Chains map[string]*ChainConfig `toml:"chains"`
|
||||
}
|
||||
|
||||
type ChainConfig struct {
|
||||
GRPCEndpoints []GRPCEndpoint `toml:"trusted-grpc-endpoints"`
|
||||
Bech32Prefix string `toml:"bech32-prefix"`
|
||||
}
|
||||
|
||||
type GRPCEndpoint struct {
|
||||
Endpoint string `toml:"endpoint"`
|
||||
Insecure bool `toml:"insecure"`
|
||||
}
|
||||
|
||||
func LoadConfig(configDir string) (*Config, error) {
|
||||
configPath := configFilename(configDir)
|
||||
|
||||
if _, err := os.Stat(configPath); os.IsNotExist(err) {
|
||||
return &Config{Chains: map[string]*ChainConfig{}}, nil
|
||||
}
|
||||
|
||||
bz, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "can't read config file: %s", configPath)
|
||||
}
|
||||
|
||||
config := &Config{}
|
||||
if err = toml.Unmarshal(bz, config); err != nil {
|
||||
return nil, errors.Wrapf(err, "can't load config file: %s", configPath)
|
||||
}
|
||||
|
||||
return config, err
|
||||
}
|
||||
|
||||
func SaveConfig(configDir string, config *Config) error {
|
||||
buf := &bytes.Buffer{}
|
||||
enc := toml.NewEncoder(buf)
|
||||
if err := enc.Encode(config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(configDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configPath := configFilename(configDir)
|
||||
if err := os.WriteFile(configPath, buf.Bytes(), 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func configFilename(configDir string) string {
|
||||
return path.Join(configDir, "config.toml")
|
||||
}
|
||||
@ -1,203 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
cockroachdberrors "github.com/cockroachdb/errors"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protodesc"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
|
||||
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
|
||||
reflectionv2alpha1 "cosmossdk.io/api/cosmos/base/reflection/v2alpha1"
|
||||
reflectionv1 "cosmossdk.io/api/cosmos/reflection/v1"
|
||||
)
|
||||
|
||||
const DefaultConfigDirName = ".hubl"
|
||||
|
||||
type ChainInfo struct {
|
||||
client *grpc.ClientConn
|
||||
|
||||
Context context.Context
|
||||
ConfigDir string
|
||||
Chain string
|
||||
Config *ChainConfig
|
||||
|
||||
ProtoFiles *protoregistry.Files
|
||||
ModuleOptions map[string]*autocliv1.ModuleOptions
|
||||
}
|
||||
|
||||
func NewChainInfo(configDir, chain string, config *ChainConfig) *ChainInfo {
|
||||
return &ChainInfo{
|
||||
Context: context.Background(),
|
||||
Config: config,
|
||||
Chain: chain,
|
||||
ConfigDir: configDir,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChainInfo) getCacheDir() (string, error) {
|
||||
cacheDir := path.Join(c.ConfigDir, "cache")
|
||||
return cacheDir, os.MkdirAll(cacheDir, 0o755)
|
||||
}
|
||||
|
||||
func (c *ChainInfo) fdsCacheFilename() (string, error) {
|
||||
cacheDir, err := c.getCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(cacheDir, fmt.Sprintf("%s.fds", c.Chain)), nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) appOptsCacheFilename() (string, error) {
|
||||
cacheDir, err := c.getCacheDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(cacheDir, fmt.Sprintf("%s.autocli", c.Chain)), nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) Load(reload bool) error {
|
||||
fdSet := &descriptorpb.FileDescriptorSet{}
|
||||
fdsFilename, err := c.fdsCacheFilename()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(fdsFilename); os.IsNotExist(err) || reload {
|
||||
client, err := c.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
reflectionClient := reflectionv1.NewReflectionServiceClient(client)
|
||||
fdRes, err := reflectionClient.FileDescriptors(c.Context, &reflectionv1.FileDescriptorsRequest{})
|
||||
if err != nil {
|
||||
fdSet, err = loadFileDescriptorsGRPCReflection(c.Context, client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fdSet = &descriptorpb.FileDescriptorSet{File: fdRes.Files}
|
||||
}
|
||||
|
||||
bz, err := proto.Marshal(fdSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = os.WriteFile(fdsFilename, bz, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
bz, err := os.ReadFile(fdsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = proto.Unmarshal(bz, fdSet); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.ProtoFiles, err = protodesc.FileOptions{AllowUnresolvable: true}.NewFiles(fdSet)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error building protoregistry.Files: %w", err)
|
||||
}
|
||||
|
||||
appOptsFilename, err := c.appOptsCacheFilename()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := os.Stat(appOptsFilename); os.IsNotExist(err) || reload {
|
||||
client, err := c.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
autocliQueryClient := autocliv1.NewQueryClient(client)
|
||||
appOptsRes, err := autocliQueryClient.AppOptions(c.Context, &autocliv1.AppOptionsRequest{})
|
||||
if err != nil {
|
||||
appOptsRes = guessAutocli(c.ProtoFiles)
|
||||
}
|
||||
|
||||
bz, err := proto.Marshal(appOptsRes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.WriteFile(appOptsFilename, bz, 0o600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ModuleOptions = appOptsRes.ModuleOptions
|
||||
} else {
|
||||
bz, err := os.ReadFile(appOptsFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var appOptsRes autocliv1.AppOptionsResponse
|
||||
if err := proto.Unmarshal(bz, &appOptsRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.ModuleOptions = appOptsRes.ModuleOptions
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *ChainInfo) OpenClient() (*grpc.ClientConn, error) {
|
||||
if c.client != nil {
|
||||
return c.client, nil
|
||||
}
|
||||
|
||||
var res error
|
||||
for _, endpoint := range c.Config.GRPCEndpoints {
|
||||
var creds credentials.TransportCredentials
|
||||
if endpoint.Insecure {
|
||||
creds = insecure.NewCredentials()
|
||||
} else {
|
||||
creds = credentials.NewTLS(&tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
})
|
||||
}
|
||||
|
||||
var err error
|
||||
c.client, err = grpc.Dial(endpoint.Endpoint, grpc.WithTransportCredentials(creds))
|
||||
if err != nil {
|
||||
res = errors.Join(res, err)
|
||||
continue
|
||||
}
|
||||
|
||||
return c.client, nil
|
||||
}
|
||||
|
||||
return nil, cockroachdberrors.Wrapf(res, "error loading gRPC client")
|
||||
}
|
||||
|
||||
// getAddressPrefix returns the address prefix of the chain.
|
||||
func getAddressPrefix(ctx context.Context, conn grpc.ClientConnInterface) (string, error) {
|
||||
reflectionClient := reflectionv2alpha1.NewReflectionServiceClient(conn)
|
||||
resp, err := reflectionClient.GetConfigurationDescriptor(ctx, &reflectionv2alpha1.GetConfigurationDescriptorRequest{})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if resp == nil || resp.Config == nil || resp.Config.Bech32AccountAddressPrefix == "" {
|
||||
return "", cockroachdberrors.New("bech32 account address prefix is not set")
|
||||
}
|
||||
|
||||
return resp.Config.Bech32AccountAddressPrefix, nil
|
||||
}
|
||||
@ -1,101 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/manifoldco/promptui"
|
||||
)
|
||||
|
||||
type ChainRegistryEntry struct {
|
||||
APIs ChainRegistryAPIs `json:"apis"`
|
||||
}
|
||||
|
||||
type ChainRegistryAPIs struct {
|
||||
GRPC []*APIEntry `json:"grpc"`
|
||||
}
|
||||
|
||||
type APIEntry struct {
|
||||
Address string
|
||||
Provider string
|
||||
}
|
||||
|
||||
func GetChainRegistryEntry(chain string) (*ChainRegistryEntry, error) {
|
||||
res, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/cosmos/chain-registry/master/%v/chain.json", chain))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bz, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &ChainRegistryEntry{}
|
||||
if err = json.Unmarshal(bz, data); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// clean-up the URL
|
||||
cleanEntries := make([]*APIEntry, 0)
|
||||
for i, apiEntry := range data.APIs.GRPC {
|
||||
// clean-up the http(s):// prefix
|
||||
if strings.Contains(apiEntry.Address, "https://") {
|
||||
data.APIs.GRPC[i].Address = strings.Replace(apiEntry.Address, "https://", "", 1)
|
||||
} else if strings.Contains(apiEntry.Address, "http://") {
|
||||
data.APIs.GRPC[i].Address = strings.Replace(apiEntry.Address, "http://", "", 1)
|
||||
}
|
||||
|
||||
// remove trailing slashes
|
||||
data.APIs.GRPC[i].Address = strings.TrimSuffix(data.APIs.GRPC[i].Address, "/")
|
||||
|
||||
// remove addresses without a port
|
||||
if !strings.Contains(data.APIs.GRPC[i].Address, ":") {
|
||||
continue
|
||||
}
|
||||
|
||||
cleanEntries = append(cleanEntries, data.APIs.GRPC[i])
|
||||
}
|
||||
|
||||
data.APIs.GRPC = cleanEntries
|
||||
fmt.Printf("Found data for %s in the chain registry\n", chain)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func SelectGRPCEndpoints(chain string) (string, error) {
|
||||
entry, err := GetChainRegistryEntry(chain)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load data for %s in the chain registry. Specify a custom gRPC endpoint manually.\n", chain)
|
||||
prompt := &promptui.Prompt{
|
||||
Label: "Enter a gRPC endpoint that you trust",
|
||||
}
|
||||
return prompt.Run()
|
||||
}
|
||||
|
||||
var items []string
|
||||
if entry != nil {
|
||||
for _, apiEntry := range entry.APIs.GRPC {
|
||||
items = append(items, fmt.Sprintf("%s: %s", apiEntry.Provider, apiEntry.Address))
|
||||
}
|
||||
}
|
||||
prompt := promptui.SelectWithAdd{
|
||||
Label: fmt.Sprintf("Select a gRPC endpoint that you trust for the %s network", chain),
|
||||
Items: items,
|
||||
AddLabel: "Custom endpoint:",
|
||||
}
|
||||
|
||||
i, ep, err := prompt.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// user selected a custom endpoint
|
||||
if i == -1 {
|
||||
return ep, nil
|
||||
}
|
||||
|
||||
return entry.APIs.GRPC[i].Address, nil
|
||||
}
|
||||
@ -1,258 +0,0 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/reflect/protoregistry"
|
||||
"google.golang.org/protobuf/types/dynamicpb"
|
||||
|
||||
"cosmossdk.io/client/v2/autocli"
|
||||
"cosmossdk.io/client/v2/autocli/flag"
|
||||
|
||||
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
|
||||
)
|
||||
|
||||
var (
|
||||
flagInsecure = "insecure"
|
||||
flagUpdate = "update"
|
||||
flagConfig = "config"
|
||||
)
|
||||
|
||||
func RootCommand() (*cobra.Command, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
configDir := path.Join(homeDir, DefaultConfigDirName)
|
||||
config, err := LoadConfig(configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "hubl",
|
||||
Short: "Hubl is a CLI for interacting with Cosmos SDK chains",
|
||||
Long: "Hubl is a CLI for interacting with Cosmos SDK chains",
|
||||
}
|
||||
|
||||
// add commands
|
||||
commands, err := RemoteCommand(config, configDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commands = append(commands, InitCommand(config, configDir))
|
||||
|
||||
cmd.AddCommand(commands...)
|
||||
return cmd, nil
|
||||
}
|
||||
|
||||
func InitCommand(config *Config, configDir string) *cobra.Command {
|
||||
var insecure bool
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "init [foochain]",
|
||||
Short: "Initialize a new chain",
|
||||
Long: `To configure a new chain just run this command using the --init flag and the name of the chain as it's listed in the chain registry (https://github.com/cosmos/chain-registry).
|
||||
If the chain is not listed in the chain registry, you can use any unique name.`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
chainName := strings.ToLower(args[0])
|
||||
|
||||
return reconfigure(cmd, config, configDir, chainName)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func RemoteCommand(config *Config, configDir string) ([]*cobra.Command, error) {
|
||||
commands := []*cobra.Command{}
|
||||
|
||||
for chain, chainConfig := range config.Chains {
|
||||
chain, chainConfig := chain, chainConfig
|
||||
|
||||
// load chain info
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
if err := chainInfo.Load(false); err != nil {
|
||||
commands = append(commands, RemoteErrorCommand(config, configDir, chain, chainConfig, err))
|
||||
continue
|
||||
}
|
||||
|
||||
appOpts := autocli.AppOptions{
|
||||
ModuleOptions: chainInfo.ModuleOptions,
|
||||
}
|
||||
|
||||
builder := &autocli.Builder{
|
||||
Builder: flag.Builder{
|
||||
AddressCodec: addresscodec.NewBech32Codec(chainConfig.Bech32Prefix),
|
||||
ValidatorAddressCodec: addresscodec.NewBech32Codec(fmt.Sprintf("%svaloper", chainConfig.Bech32Prefix)),
|
||||
ConsensusAddressCodec: addresscodec.NewBech32Codec(fmt.Sprintf("%svalcons", chainConfig.Bech32Prefix)),
|
||||
TypeResolver: &dynamicTypeResolver{chainInfo},
|
||||
FileResolver: chainInfo.ProtoFiles,
|
||||
},
|
||||
GetClientConn: func(command *cobra.Command) (grpc.ClientConnInterface, error) {
|
||||
return chainInfo.OpenClient()
|
||||
},
|
||||
AddQueryConnFlags: func(command *cobra.Command) {},
|
||||
}
|
||||
|
||||
var (
|
||||
update bool
|
||||
reconfig bool
|
||||
insecure bool
|
||||
)
|
||||
|
||||
chainCmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: fmt.Sprintf("Commands for the %s chain", chain),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
switch {
|
||||
case reconfig:
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
case update:
|
||||
cmd.Printf("Updating autocli data for %s\n", chain)
|
||||
return chainInfo.Load(true)
|
||||
default:
|
||||
return cmd.Help()
|
||||
}
|
||||
},
|
||||
}
|
||||
chainCmd.Flags().BoolVar(&update, flagUpdate, false, "update the CLI commands for the selected chain (should be used after every chain upgrade)")
|
||||
chainCmd.Flags().BoolVar(&reconfig, flagConfig, false, "re-configure the selected chain (allows choosing a new gRPC endpoint and refreshes data")
|
||||
chainCmd.Flags().BoolVar(&insecure, flagInsecure, false, "allow re-configuring the selected chain using an insecure gRPC connection")
|
||||
|
||||
if err := appOpts.EnhanceRootCommandWithBuilder(chainCmd, builder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
commands = append(commands, chainCmd)
|
||||
}
|
||||
|
||||
return commands, nil
|
||||
}
|
||||
|
||||
func RemoteErrorCommand(config *Config, configDir, chain string, chainConfig *ChainConfig, err error) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: chain,
|
||||
Short: "Unable to load data",
|
||||
Long: "Unable to load data, reconfiguration needed.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cmd.Printf("Error loading chain data for %s: %+v\n", chain, err)
|
||||
|
||||
return reconfigure(cmd, config, configDir, chain)
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Bool(flagInsecure, chainConfig.GRPCEndpoints[0].Insecure, "allow setting up insecure gRPC connection")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func reconfigure(cmd *cobra.Command, config *Config, configDir, chain string) error {
|
||||
insecure, _ := cmd.Flags().GetBool(flagInsecure)
|
||||
|
||||
cmd.Printf("Configuring %s\n", chain)
|
||||
endpoint, err := SelectGRPCEndpoints(chain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Printf("%s endpoint selected\n", endpoint)
|
||||
chainConfig := &ChainConfig{
|
||||
GRPCEndpoints: []GRPCEndpoint{
|
||||
{
|
||||
Endpoint: endpoint,
|
||||
Insecure: insecure,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
chainInfo := NewChainInfo(configDir, chain, chainConfig)
|
||||
if err = chainInfo.Load(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := chainInfo.OpenClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
addressPrefix, err := getAddressPrefix(context.Background(), client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chainConfig.Bech32Prefix = addressPrefix
|
||||
config.Chains[chain] = chainConfig
|
||||
|
||||
if err := SaveConfig(configDir, config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Printf("Configuration saved to %s\n", configDir)
|
||||
return nil
|
||||
}
|
||||
|
||||
type dynamicTypeResolver struct {
|
||||
*ChainInfo
|
||||
}
|
||||
|
||||
var (
|
||||
_ protoregistry.MessageTypeResolver = dynamicTypeResolver{}
|
||||
_ protoregistry.ExtensionTypeResolver = dynamicTypeResolver{}
|
||||
)
|
||||
|
||||
func (d dynamicTypeResolver) FindMessageByName(message protoreflect.FullName) (protoreflect.MessageType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dynamicpb.NewMessageType(desc.(protoreflect.MessageDescriptor)), nil
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindMessageByURL(url string) (protoreflect.MessageType, error) {
|
||||
if i := strings.LastIndexByte(url, '/'); i >= 0 {
|
||||
url = url[i+len("/"):]
|
||||
}
|
||||
|
||||
return d.FindMessageByName(protoreflect.FullName(url))
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindExtensionByName(field protoreflect.FullName) (protoreflect.ExtensionType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dynamicpb.NewExtensionType(desc.(protoreflect.ExtensionTypeDescriptor)), nil
|
||||
}
|
||||
|
||||
func (d dynamicTypeResolver) FindExtensionByNumber(message protoreflect.FullName, field protoreflect.FieldNumber) (protoreflect.ExtensionType, error) {
|
||||
desc, err := d.ProtoFiles.FindDescriptorByName(message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
messageDesc := desc.(protoreflect.MessageDescriptor)
|
||||
exts := messageDesc.Extensions()
|
||||
n := exts.Len()
|
||||
for i := 0; i < n; i++ {
|
||||
ext := exts.Get(i)
|
||||
if ext.Number() == field {
|
||||
return dynamicpb.NewExtensionType(ext), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, protoregistry.NotFound
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user