Skip to content

Commit

Permalink
fix(cosmovisor): let cosmovisor version return a valid json (#11731)
Browse files Browse the repository at this point in the history
  • Loading branch information
julienrbrt authored Apr 26, 2022
1 parent 1d8a878 commit 019444a
Show file tree
Hide file tree
Showing 17 changed files with 208 additions and 89 deletions.
2 changes: 2 additions & 0 deletions cosmovisor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Ref: https://keepachangelog.com/en/1.0.0/
## [Unreleased]
<!-- NOTE: when creating a new release, update cosmovisor/cmd/cosmovisor/cmd/version.go:Version -->

* [\#11731](https://github.com/cosmos/cosmos-sdk/pull/11731) `cosmovisor version --json` returns the cosmovisor version and the result of `simd --output json --long` in one JSON object.

## v1.1.0 2022-10-02

### Features
Expand Down
20 changes: 9 additions & 11 deletions cosmovisor/args.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,10 @@ import (
"strings"
"time"

"github.com/rs/zerolog"

cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/rs/zerolog"
)

// environment variable names
Expand Down Expand Up @@ -184,11 +183,11 @@ func GetConfigFromEnv() (*Config, error) {
}

// LogConfigOrError logs either the config details or the error.
func LogConfigOrError(logger zerolog.Logger, cfg *Config, err error) {
func LogConfigOrError(logger *zerolog.Logger, cfg *Config, err error) {
if cfg == nil && err == nil {
return
}
logger.Info().Msg("Configuration:")
logger.Info().Msg("configuration:")
switch {
case err != nil:
cverrors.LogErrors(logger, "configuration errors found", err)
Expand Down Expand Up @@ -227,9 +226,9 @@ func (cfg *Config) validate() []error {
// if UnsafeSkipBackup is false, check if the DataBackupPath valid
switch {
case cfg.DataBackupPath == "":
errs = append(errs, errors.New(EnvDataBackupPath + " must not be empty"))
errs = append(errs, fmt.Errorf("%s must not be empty", EnvDataBackupPath))
case !filepath.IsAbs(cfg.DataBackupPath):
errs = append(errs, errors.New(cfg.DataBackupPath + " must be an absolute path"))
errs = append(errs, fmt.Errorf("%s must be an absolute path", cfg.DataBackupPath))
default:
switch info, err := os.Stat(cfg.DataBackupPath); {
case err != nil:
Expand Down Expand Up @@ -281,9 +280,9 @@ func (cfg *Config) SetCurrentUpgrade(u upgradetypes.Plan) error {
return f.Close()
}

func (cfg *Config) UpgradeInfo() upgradetypes.Plan {
func (cfg *Config) UpgradeInfo() (upgradetypes.Plan, error) {
if cfg.currentUpgrade.Name != "" {
return cfg.currentUpgrade
return cfg.currentUpgrade, nil
}

filename := filepath.Join(cfg.Root(), currentLink, upgradekeeper.UpgradeInfoFileName)
Expand All @@ -300,12 +299,11 @@ func (cfg *Config) UpgradeInfo() upgradetypes.Plan {
goto returnError
}
cfg.currentUpgrade = u
return cfg.currentUpgrade
return cfg.currentUpgrade, nil

returnError:
Logger.Error().Err(err).Str("filename", filename).Msg("failed to read")
cfg.currentUpgrade.Name = "_"
return cfg.currentUpgrade
return cfg.currentUpgrade, fmt.Errorf("failed to read %q: %w", filename, err)
}

// checks and validates env option
Expand Down
5 changes: 3 additions & 2 deletions cosmovisor/args_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -570,9 +570,10 @@ func (s *argsTestSuite) TestLogConfigOrError() {
}
errMulti := errors.FlattenErrors(errs...)

makeTestLogger := func(testName string, out io.Writer) zerolog.Logger {
makeTestLogger := func(testName string, out io.Writer) *zerolog.Logger {
output := zerolog.ConsoleWriter{Out: out, TimeFormat: time.Kitchen, NoColor: true}
return zerolog.New(output).With().Str("test", testName).Timestamp().Logger()
logger := zerolog.New(output).With().Str("test", testName).Timestamp().Logger()
return &logger
}

tests := []struct {
Expand Down
4 changes: 3 additions & 1 deletion cosmovisor/cmd/cosmovisor/cmd/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ func ShouldGiveHelp(arg string) bool {
}

// DoHelp outputs help text
func DoHelp() {
func DoHelp() error {
// Not using the logger for this output because the header and footer look weird for help text.
fmt.Println(GetHelpText())

return nil
}

// GetHelpText creates the help text multi-line string.
Expand Down
20 changes: 12 additions & 8 deletions cosmovisor/cmd/cosmovisor/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,34 @@ package cmd
import (
"strings"

"github.com/cosmos/cosmos-sdk/cosmovisor"
"github.com/rs/zerolog"
)

// RunCosmovisorCommand executes the desired cosmovisor command.
func RunCosmovisorCommand(args []string) error {
func RunCosmovisorCommand(logger *zerolog.Logger, args []string) error {
arg0 := ""
if len(args) > 0 {
arg0 = strings.TrimSpace(args[0])
}

switch {
case IsVersionCommand(arg0):
return PrintVersion()
return PrintVersion(logger, args[1:])

case ShouldGiveHelp(arg0):
DoHelp()
return nil
return DoHelp()

case IsRunCommand(arg0):
return Run(args[1:])
return Run(logger, args[1:])
}

warnRun := func() {
cosmovisor.Logger.Warn().Msg("Use of cosmovisor without the 'run' command is deprecated. Use: cosmovisor run [args]")
logger.Warn().Msg("use of cosmovisor without the 'run' command is deprecated. Use: cosmovisor run [args]")
}
warnRun()
defer warnRun()
return Run(args)

return Run(logger, args)
}

// isOneOf returns true if the given arg equals one of the provided options (ignoring case).
Expand Down
21 changes: 13 additions & 8 deletions cosmovisor/cmd/cosmovisor/cmd/run.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package cmd

import (
"os"

"github.com/cosmos/cosmos-sdk/cosmovisor"
"github.com/rs/zerolog"
)

// RunArgs are the strings that indicate a cosmovisor run command.
Expand All @@ -15,24 +14,30 @@ func IsRunCommand(arg string) bool {
}

// Run runs the configured program with the given args and monitors it for upgrades.
func Run(args []string) error {
func Run(logger *zerolog.Logger, args []string, options ...RunOption) error {
cfg, err := cosmovisor.GetConfigFromEnv()
if err != nil {
return err
}
launcher, err := cosmovisor.NewLauncher(cfg)

runCfg := DefaultRunConfig
for _, opt := range options {
opt(&runCfg)
}

launcher, err := cosmovisor.NewLauncher(logger, cfg)
if err != nil {
return err
}

doUpgrade, err := launcher.Run(args, os.Stdout, os.Stderr)
doUpgrade, err := launcher.Run(args, runCfg.StdOut, runCfg.StdErr)
// if RestartAfterUpgrade, we launch after a successful upgrade (only condition LaunchProcess returns nil)
for cfg.RestartAfterUpgrade && err == nil && doUpgrade {
cosmovisor.Logger.Info().Str("app", cfg.Name).Msg("upgrade detected, relaunching")
doUpgrade, err = launcher.Run(args, os.Stdout, os.Stderr)
logger.Info().Str("app", cfg.Name).Msg("upgrade detected, relaunching")
doUpgrade, err = launcher.Run(args, runCfg.StdOut, runCfg.StdErr)
}
if doUpgrade && err == nil {
cosmovisor.Logger.Info().Msg("upgrade detected, DAEMON_RESTART_AFTER_UPGRADE is off. Verify new upgrade and start cosmovisor again.")
logger.Info().Msg("upgrade detected, DAEMON_RESTART_AFTER_UPGRADE is off. Verify new upgrade and start cosmovisor again.")
}

return err
Expand Down
34 changes: 34 additions & 0 deletions cosmovisor/cmd/cosmovisor/cmd/run_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cmd

import (
"io"
"os"
)

// DefaultRunConfig defintes a default RunConfig that writes to os.Stdout and os.Stderr
var DefaultRunConfig = RunConfig{
StdOut: os.Stdout,
StdErr: os.Stderr,
}

// RunConfig defines the configuration for running a command
type RunConfig struct {
StdOut io.Writer
StdErr io.Writer
}

type RunOption func(*RunConfig)

// StdOutRunOption sets the StdOut writer for the Run command
func StdOutRunOption(w io.Writer) RunOption {
return func(cfg *RunConfig) {
cfg.StdOut = w
}
}

// SdErrRunOption sets the StdErr writer for the Run command
func StdErrRunOption(w io.Writer) RunOption {
return func(cfg *RunConfig) {
cfg.StdErr = w
}
}
78 changes: 65 additions & 13 deletions cosmovisor/cmd/cosmovisor/cmd/version.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,88 @@
package cmd

import (
"encoding/json"
"fmt"
"os"
"strings"
"time"

cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
"github.com/rs/zerolog"
)

// Version represents Cosmovisor version value. Overwritten during build
var Version = "1.1.0"

// VersionArgs is the strings that indicate a cosmovisor version command.
var VersionArgs = []string{"version", "--version"}
var (
// FlagJSON formats the output in json
FlagJSON = "--json"
// Version represents Cosmovisor version value. Overwritten during build
Version = "1.1.0"
// VersionArgs is the strings that indicate a cosmovisor version command.
VersionArgs = []string{"version", "--version"}
)

// IsVersionCommand checks if the given args indicate that the version is being requested.
func IsVersionCommand(arg string) bool {
return isOneOf(arg, VersionArgs)
}

// PrintVersion prints the cosmovisor version.
func PrintVersion() error {
func PrintVersion(logger *zerolog.Logger, args []string) error {
for _, arg := range args {
if strings.Contains(arg, FlagJSON) {
return printVersionJSON(logger, args)
}
}

return printVersion(logger, args)
}

func printVersion(logger *zerolog.Logger, args []string) error {
fmt.Println("Cosmovisor Version: ", Version)

if err := Run([]string{"version"}); err != nil {
// Check the config and output details or any errors.
// Not using the cosmovisor.Logger in order to ignore any level it might have set,
// and also to not have any of the extra parameters in the output.
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Kitchen}
logger := zerolog.New(output).With().Timestamp().Logger()
cverrors.LogErrors(logger, "Can't run APP version", err)
if err := Run(logger, append([]string{"version"}, args...)); err != nil {
handleRunVersionFailure(err)
}

return nil
}

func printVersionJSON(logger *zerolog.Logger, args []string) error {
buf := new(strings.Builder)

// disable logger
l := logger.Level(zerolog.Disabled)
logger = &l

if err := Run(
logger,
[]string{"version", "--long", "--output", "json"},
StdOutRunOption(buf),
); err != nil {
handleRunVersionFailure(err)
}

out, err := json.Marshal(struct {
Version string `json:"cosmovisor_version"`
AppVersion json.RawMessage `json:"app_version"`
}{
Version: Version,
AppVersion: json.RawMessage(buf.String()),
})
if err != nil {
l := logger.Level(zerolog.TraceLevel)
logger = &l
return fmt.Errorf("Can't print version output, expected valid json from APP, got: %s - %w", buf.String(), err)
}

fmt.Println(string(out))
return nil
}

func handleRunVersionFailure(err error) {
// Check the config and output details or any errors.
// Not using the cosmovisor.Logger in order to ignore any level it might have set,
// and also to not have any of the extra parameters in the output.
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Kitchen}
logger := zerolog.New(output).With().Timestamp().Logger()
cverrors.LogErrors(&logger, "Can't run APP version", err)
}
6 changes: 3 additions & 3 deletions cosmovisor/cmd/cosmovisor/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import (
)

func main() {
cosmovisor.SetupLogging()
if err := cmd.RunCosmovisorCommand(os.Args[1:]); err != nil {
cverrors.LogErrors(cosmovisor.Logger, "", err)
logger := cosmovisor.NewLogger()
if err := cmd.RunCosmovisorCommand(logger, os.Args[1:]); err != nil {
cverrors.LogErrors(logger, "", err)
os.Exit(1)
}
}
2 changes: 1 addition & 1 deletion cosmovisor/errors/multi.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (e MultiError) String() string {
return e.Error()
}

func LogErrors(logger zerolog.Logger, msg string, err error) {
func LogErrors(logger *zerolog.Logger, msg string, err error) {
switch err := err.(type) {
case *MultiError:
if msg != "" {
Expand Down
7 changes: 3 additions & 4 deletions cosmovisor/logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@ import (
"github.com/rs/zerolog"
)

var Logger zerolog.Logger

func SetupLogging() {
func NewLogger() *zerolog.Logger {
output := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.Kitchen}
Logger = zerolog.New(output).With().Str("module", "cosmovisor").Timestamp().Logger()
logger := zerolog.New(output).With().Str("module", "cosmovisor").Timestamp().Logger()
return &logger
}
Loading

0 comments on commit 019444a

Please sign in to comment.