Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: make Cosmovisor use cobra #11823

Merged
merged 18 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cosmovisor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ 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.
### Features

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

## v1.1.0 2022-10-02

Expand Down
31 changes: 3 additions & 28 deletions cosmovisor/cmd/cosmovisor/cmd/help.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,6 @@ import (
"github.com/cosmos/cosmos-sdk/cosmovisor"
)

// HelpArgs are the strings that indicate a cosmovisor help command.
var HelpArgs = []string{"help", "--help", "-h"}

// ShouldGiveHelp checks the env and provided args to see if help is needed or being requested.
// 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)
}

// DoHelp outputs help text
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.
func GetHelpText() string {
return fmt.Sprintf(`Cosmosvisor - A process manager for Cosmos SDK application binaries.
Expand All @@ -36,14 +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://github.com/cosmos/cosmos-sdk/tree/main/cosmovisor/README.md

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)
documented in: https://github.com/cosmos/cosmos-sdk/tree/main/cosmovisor/README.md`,
cosmovisor.EnvName, cosmovisor.EnvHome,
)
}
67 changes: 0 additions & 67 deletions cosmovisor/cmd/cosmovisor/cmd/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"os"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/stretchr/testify/suite"

Expand Down Expand Up @@ -88,72 +87,6 @@ func (s *HelpTestSuite) setEnv(t *testing.T, env *cosmovisorHelpEnv) {
}
}

func (s HelpTestSuite) TestShouldGiveHelpArg() {
initialEnv := s.clearEnv()
defer s.setEnv(nil, initialEnv)

s.setEnv(s.T(), &cosmovisorHelpEnv{"/testhome", "testname"})

tests := []struct {
name string
arg string
expected bool
}{
{
name: "empty string",
arg: "",
expected: false,
},
{
name: "random",
arg: "random",
expected: false,
},
{
name: "help",
arg: "help",
expected: true,
},
{
name: "-h",
arg: "-h",
expected: true,
},
{
name: "--help",
arg: "--help",
expected: true,
},
{
name: "help weird casing",
arg: "hELP",
expected: true,
},
{
name: "version",
arg: "version",
expected: false,
},
{
name: "--version",
arg: "--version",
expected: false,
},
{
name: "run",
arg: "run",
expected: false,
},
}

for _, tc := range tests {
s.T().Run(fmt.Sprintf("%s - %t", tc.name, tc.expected), func(t *testing.T) {
actual := ShouldGiveHelp(tc.arg)
assert.Equal(t, tc.expected, actual)
})
}
}

func (s *HelpTestSuite) TestGetHelpText() {
expectedPieces := []string{
"Cosmosvisor",
Expand Down
50 changes: 17 additions & 33 deletions cosmovisor/cmd/cosmovisor/cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,28 @@
package cmd

import (
"strings"
"context"
"os"

"github.com/rs/zerolog"
)

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

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

case ShouldGiveHelp(arg0):
return DoHelp()
"github.com/spf13/cobra"

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

warnRun := func() {
logger.Warn().Msg("use of cosmovisor without the 'run' command is deprecated. Use: cosmovisor run [args]")
}
warnRun()
defer warnRun()
"github.com/cosmos/cosmos-sdk/cosmovisor"
cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
)

return Run(logger, args)
var rootCmd = &cobra.Command{
Use: "cosmovisor",
Short: "A process manager for Cosmos SDK application binaries.",
Long: GetHelpText(),
}

// isOneOf returns true if the given arg equals one of the provided options (ignoring case).
func isOneOf(arg string, options []string) bool {
for _, opt := range options {
if strings.EqualFold(arg, opt) {
return true
}
// Execute the CLI application.
func Execute(logger *zerolog.Logger) {
ctx := context.WithValue(context.Background(), cosmovisor.LoggerKey, logger)

if err := rootCmd.ExecuteContext(ctx); err != nil {
cverrors.LogErrors(logger, "", err)
os.Exit(1)
}
return false
}
19 changes: 14 additions & 5 deletions cosmovisor/cmd/cosmovisor/cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,23 @@ package cmd
import (
"github.com/cosmos/cosmos-sdk/cosmovisor"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
)

// RunArgs are the strings that indicate a cosmovisor run command.
var RunArgs = []string{"run"}
func init() {
rootCmd.AddCommand(runCmd)
}

var runCmd = &cobra.Command{
Use: "run",
Short: "Run an APP command.",
SilenceUsage: true,
DisableFlagParsing: true,
RunE: func(cmd *cobra.Command, args []string) error {
logger := cmd.Context().Value(cosmovisor.LoggerKey).(*zerolog.Logger)

// IsRunCommand checks if the given args indicate that a run is desired.
func IsRunCommand(arg string) bool {
return isOneOf(arg, RunArgs)
return Run(logger, args)
},
}

// Run runs the configured program with the given args and monitors it for upgrades.
Expand Down
73 changes: 0 additions & 73 deletions cosmovisor/cmd/cosmovisor/cmd/run_test.go
Original file line number Diff line number Diff line change
@@ -1,76 +1,3 @@
package cmd

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"
)

func TestIsRunCommand(t *testing.T) {
cases := []struct {
name string
arg string
expected bool
}{
{
name: "empty string",
arg: "",
expected: false,
},
{
name: "random",
arg: "random",
expected: false,
},
{
name: "run",
arg: "run",
expected: true,
},
{
name: "run weird casing",
arg: "RUn",
expected: true,
},
{
name: "--run",
arg: "--run",
expected: false,
},
{
name: "help",
arg: "help",
expected: false,
},
{
name: "-h",
arg: "-h",
expected: false,
},
{
name: "--help",
arg: "--help",
expected: false,
},
{
name: "version",
arg: "version",
expected: false,
},
{
name: "--version",
arg: "--version",
expected: false,
},
}

for _, tc := range cases {
t.Run(fmt.Sprintf("%s - %t", tc.name, tc.expected), func(t *testing.T) {
actual := IsRunCommand(tc.arg)
require.Equal(t, tc.expected, actual)
})
}
}

// TODO: Write tests for func Run(args []string) error
53 changes: 23 additions & 30 deletions cosmovisor/cmd/cosmovisor/cmd/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,46 @@ package cmd
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"

cverrors "github.com/cosmos/cosmos-sdk/cosmovisor/errors"
"github.com/cosmos/cosmos-sdk/cosmovisor"
"github.com/rs/zerolog"
"github.com/spf13/cobra"
tmcli "github.com/tendermint/tendermint/libs/cli"
)

func init() {
versionCmd.Flags().StringP(OutputFlag, "o", "text", "Output format (text|json)")
rootCmd.AddCommand(versionCmd)
}

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"}
// OutputFlag defines the output format flag
OutputFlag = tmcli.OutputFlag
)

// IsVersionCommand checks if the given args indicate that the version is being requested.
func IsVersionCommand(arg string) bool {
return isOneOf(arg, VersionArgs)
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Prints the version of Cosmovisor.",
SilenceUsage: true,
RunE: func(cmd *cobra.Command, args []string) error {
logger := cmd.Context().Value(cosmovisor.LoggerKey).(*zerolog.Logger)

// PrintVersion prints the cosmovisor version.
func PrintVersion(logger *zerolog.Logger, args []string) error {
for _, arg := range args {
if strings.Contains(arg, FlagJSON) {
if val, err := cmd.Flags().GetString(OutputFlag); val == "json" && err == nil {
return printVersionJSON(logger, args)
}
}

return printVersion(logger, args)
return printVersion(logger, args)
},
}

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

if err := Run(logger, append([]string{"version"}, args...)); err != nil {
handleRunVersionFailure(err)
return fmt.Errorf("failed to run version command: %w", err)
}

return nil
Expand All @@ -58,7 +60,7 @@ func printVersionJSON(logger *zerolog.Logger, args []string) error {
[]string{"version", "--long", "--output", "json"},
StdOutRunOption(buf),
); err != nil {
handleRunVersionFailure(err)
return fmt.Errorf("failed to run version command: %w", err)
}

out, err := json.Marshal(struct {
Expand All @@ -71,18 +73,9 @@ func printVersionJSON(logger *zerolog.Logger, args []string) error {
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)
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)
}
Loading