fix(cosmovisor): udpate the help and version command output (#10458)

## Description

Closes: #10450

* `cosmovisor help` should not print any error. 
* `cosmovisor version` should print more clear message if it can't run `app version`.

## Before 

```
~/tmp ❯ cosmovisor version
Cosmosvisor - A process manager for Cosmos SDK application binaries.

Cosmovisor is a wrapper for a Cosmos SDK based App (set using the required DAEMON_NAME env variable).
It starts the App by passing all provided arguments and monitors the DAEMON_HOME/data/upgrade-info.json
file to perform an update. The upgrade-info.json file is created by the App x/upgrade module
when the blockchain height reaches an approved upgrade proposal. The file includes data from
the proposal. Cosmovisor interprets that data to perform an update: switch a current binary
and restart the App.

Configuration of Cosmovisor is done through environment variables, which are
documented in: https://github.com/cosmos/cosmos-sdk/tree/master/cosmovisor/README.md

8:03PM ERR multiple configuration errors found: module=cosmovisor
8:03PM ERR   1: error="DAEMON_NAME is not set" module=cosmovisor
8:03PM ERR   2: error="DAEMON_HOME is not set" module=cosmovisor
8:03PM ERR  error="2 errors: 1: DAEMON_NAME is not set, 2: DAEMON_HOME is not set" module=cosmovisor


❯ cosmovisor help
Cosmosvisor - A process manager for Cosmos SDK application binaries.

Cosmovisor is a wrapper for a Cosmos SDK based App (set using the required DAEMON_NAME env variable).
It starts the App by passing all provided arguments and monitors the DAEMON_HOME/data/upgrade-info.json
file to perform an update. The upgrade-info.json file is created by the App x/upgrade module
when the blockchain height reaches an approved upgrade proposal. The file includes data from
the proposal. Cosmovisor interprets that data to perform an update: switch a current binary
and restart the App.

Configuration of Cosmovisor is done through environment variables, which are
documented in: https://github.com/cosmos/cosmos-sdk/tree/master/cosmovisor/README.md

8:03PM ERR multiple configuration errors found: module=cosmovisor
8:03PM ERR   1: error="DAEMON_NAME is not set" module=cosmovisor
8:03PM ERR   2: error="DAEMON_HOME is not set" module=cosmovisor
8:03PM ERR  error="2 errors: 1: DAEMON_NAME is not set, 2: DAEMON_HOME is not set" module=cosmovisor
```

## After

```
❯ ./cosmovisor version
Cosmovisor Version:  v1.0.0-76-g66bddfb04
8:05PM ERR Can't run APP version
8:05PM ERR   1: error="DAEMON_NAME is not set"
8:05PM ERR   2: error="DAEMON_HOME is not set"


❯ ./cosmovisor help
Cosmosvisor - A process manager for Cosmos SDK application binaries.

Cosmovisor is a wrapper for a Cosmos SDK based App (set using the required DAEMON_NAME env variable).
It starts the App by passing all provided arguments and monitors the DAEMON_HOME/data/upgrade-info.json
file to perform an update. The upgrade-info.json file is created by the App x/upgrade module
when the blockchain height reaches an approved upgrade proposal. The file includes data from
the proposal. Cosmovisor interprets that data to perform an update: switch a current binary
and restart the App.

Configuration of Cosmovisor is done through environment variables, which are
documented in: https://github.com/cosmos/cosmos-sdk/tree/master/cosmovisor/README.md

To get help for the configured binary:
  cosmovisor run help
```




---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
Robert Zaremba 2021-11-19 18:35:56 +01:00 committed by GitHub
parent 8647474888
commit f6458f0795
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 60 additions and 123 deletions

View File

@ -44,6 +44,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
+ [\#10285](https://github.com/cosmos/cosmos-sdk/pull/10316) Running `cosmovisor` without the `run` argument.
### Bug Fixes
+ [\#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
### Features

View File

@ -177,20 +177,16 @@ func GetConfigFromEnv() (*Config, error) {
}
// LogConfigOrError logs either the config details or the error.
func LogConfigOrError(logger zerolog.Logger, cfg *Config, cerr error) {
func LogConfigOrError(logger zerolog.Logger, cfg *Config, err error) {
if cfg == nil && err == nil {
return
}
logger.Info().Msg("Configuration:")
switch {
case cerr != nil:
switch err := cerr.(type) {
case *cverrors.MultiError:
logger.Error().Msg("multiple configuration errors found:")
for i, e := range err.GetErrors() {
logger.Error().Err(e).Msg(fmt.Sprintf(" %d:", i+1))
}
default:
logger.Error().Err(cerr).Msg("configuration error:")
}
case err != nil:
cverrors.LogErrors(logger, "configuration errors found", err)
case cfg != nil:
logger.Info().Msg("Configuration is valid:\n" + cfg.DetailString())
logger.Info().Msg(cfg.DetailString())
}
}

View File

@ -554,14 +554,14 @@ func (s *argsTestSuite) TestLogConfigOrError() {
name: "multi error",
cfg: nil,
err: errMulti,
contains: []string{"multiple configuration errors found", errs[0].Error(), errs[1].Error(), errs[2].Error()},
contains: []string{"configuration errors found", errs[0].Error(), errs[1].Error(), errs[2].Error()},
notcontains: nil,
},
{
name: "config",
cfg: cfg,
err: nil,
contains: []string{"Configuration is valid", cfg.DetailString()},
contains: []string{"Configurable Values", cfg.DetailString()},
notcontains: nil,
},
{

View File

@ -2,10 +2,6 @@ package cmd
import (
"fmt"
"os"
"time"
"github.com/rs/zerolog"
"github.com/cosmos/cosmos-sdk/cosmovisor"
)
@ -17,20 +13,13 @@ var HelpArgs = []string{"help", "--help", "-h"}
// Help is needed if either cosmovisor.EnvName and/or cosmovisor.EnvHome env vars aren't set.
// Help is requested if the first arg is "help", "--help", or "-h".
func ShouldGiveHelp(arg string) bool {
return isOneOf(arg, HelpArgs) || len(os.Getenv(cosmovisor.EnvName)) == 0 || len(os.Getenv(cosmovisor.EnvHome)) == 0
return isOneOf(arg, HelpArgs)
}
// DoHelp outputs help text
func DoHelp() {
// Not using the logger for this output because the header and footer look weird for help text.
fmt.Println(GetHelpText())
// 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()
cfg, err := cosmovisor.GetConfigFromEnv()
cosmovisor.LogConfigOrError(logger, cfg, err)
}
// GetHelpText creates the help text multi-line string.
@ -49,5 +38,10 @@ documented in: https://github.com/cosmos/cosmos-sdk/tree/master/cosmovisor/READM
To get help for the configured binary:
cosmovisor run help
Available Commands:
help This help message
run Runs app passing all subsequent parameters
version Prints version of cosmovisor and the associated app.
`, cosmovisor.EnvName, cosmovisor.EnvHome)
}

View File

@ -88,94 +88,6 @@ func (s *HelpTestSuite) setEnv(t *testing.T, env *cosmovisorHelpEnv) {
}
}
func (s *HelpTestSuite) TestShouldGiveHelpEnvVars() {
initialEnv := s.clearEnv()
defer s.setEnv(nil, initialEnv)
emptyVal := ""
homeVal := "/somehome"
nameVal := "somename"
tests := []struct {
name string
envHome *string
envName *string
expected bool
}{
{
name: "home set name set",
envHome: &homeVal,
envName: &nameVal,
expected: false,
},
{
name: "home not set name not set",
envHome: nil,
envName: nil,
expected: true,
},
{
name: "home empty name not set",
envHome: &emptyVal,
envName: nil,
expected: true,
},
{
name: "home set name not set",
envHome: &homeVal,
envName: nil,
expected: true,
},
{
name: "home not set name empty",
envHome: nil,
envName: &emptyVal,
expected: true,
},
{
name: "home empty name empty",
envHome: &emptyVal,
envName: &emptyVal,
expected: true,
},
{
name: "home set name empty",
envHome: &homeVal,
envName: &emptyVal,
expected: true,
},
{
name: "home not set name set",
envHome: nil,
envName: &nameVal,
expected: true,
},
{
name: "home empty name set",
envHome: &emptyVal,
envName: &nameVal,
expected: true,
},
}
prepEnv := func(t *testing.T, envVar string, envVal *string) {
if envVal == nil {
require.NoError(t, os.Unsetenv(cosmovisor.EnvHome))
} else {
require.NoError(t, os.Setenv(envVar, *envVal))
}
}
for _, tc := range tests {
s.T().Run(tc.name, func(t *testing.T) {
prepEnv(t, cosmovisor.EnvHome, tc.envHome)
prepEnv(t, cosmovisor.EnvName, tc.envName)
actual := ShouldGiveHelp("not-a-help-arg")
assert.Equal(t, tc.expected, actual)
})
}
}
func (s HelpTestSuite) TestShouldGiveHelpArg() {
initialEnv := s.clearEnv()
defer s.setEnv(nil, initialEnv)

View File

@ -13,12 +13,11 @@ func RunCosmovisorCommand(args []string) error {
arg0 = strings.TrimSpace(args[0])
}
switch {
case IsVersionCommand(arg0):
return PrintVersion()
case ShouldGiveHelp(arg0):
DoHelp()
return nil
case IsVersionCommand(arg0):
PrintVersion()
return Run([]string{"version"})
case IsRunCommand(arg0):
return Run(args[1:])
}

View File

@ -16,10 +16,9 @@ func IsRunCommand(arg string) bool {
// Run runs the configured program with the given args and monitors it for upgrades.
func Run(args []string) error {
cfg, cerr := cosmovisor.GetConfigFromEnv()
cosmovisor.LogConfigOrError(cosmovisor.Logger, cfg, cerr)
if cerr != nil {
return cerr
cfg, err := cosmovisor.GetConfigFromEnv()
if err != nil {
return err
}
launcher, err := cosmovisor.NewLauncher(cfg)
if err != nil {

View File

@ -2,6 +2,11 @@ package cmd
import (
"fmt"
"os"
"time"
cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
"github.com/rs/zerolog"
)
// Version represents Cosmovisor version value. Set during build
@ -16,6 +21,16 @@ func IsVersionCommand(arg string) bool {
}
// PrintVersion prints the cosmovisor version.
func PrintVersion() {
func PrintVersion() 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)
}
return nil
}

View File

@ -5,12 +5,13 @@ import (
"github.com/cosmos/cosmos-sdk/cosmovisor"
"github.com/cosmos/cosmos-sdk/cosmovisor/cmd/cosmovisor/cmd"
cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
)
func main() {
cosmovisor.SetupLogging()
if err := cmd.RunCosmovisorCommand(os.Args[1:]); err != nil {
cosmovisor.Logger.Error().Err(err).Msg("")
cverrors.LogErrors(cosmovisor.Logger, "", err)
os.Exit(1)
}
}

View File

@ -3,6 +3,8 @@ package errors
import (
"fmt"
"strings"
"github.com/rs/zerolog"
)
// MultiError is an error combining multiple other errors.
@ -67,3 +69,18 @@ func (e *MultiError) Error() string {
func (e MultiError) String() string {
return e.Error()
}
func LogErrors(logger zerolog.Logger, msg string, err error) {
switch err := err.(type) {
case *MultiError:
if msg != "" {
logger.Error().Msg(msg)
}
for i, e := range err.GetErrors() {
logger.Error().Err(e).Msg(fmt.Sprintf(" %d:", i+1))
}
default:
logger.Error().Err(err).Msg(msg)
}
}