chore(cosmovisor): remove cosmovisor error package (#16574)

This commit is contained in:
Julien Robert 2023-06-15 16:45:29 +02:00 committed by GitHub
parent 99913cdce0
commit 7b26ff5bec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 15 additions and 389 deletions

View File

@ -2,6 +2,7 @@ package cosmovisor
import (
"encoding/json"
"errors"
"fmt"
"net/url"
"os"
@ -10,8 +11,6 @@ import (
"strings"
"time"
"cosmossdk.io/log"
cverrors "cosmossdk.io/tools/cosmovisor/errors"
"cosmossdk.io/x/upgrade/plan"
upgradetypes "cosmossdk.io/x/upgrade/types"
)
@ -197,10 +196,10 @@ func GetConfigFromEnv() (*Config, error) {
}
errs = append(errs, cfg.validate()...)
if len(errs) > 0 {
return nil, cverrors.FlattenErrors(errs...)
return nil, errors.Join(errs...)
}
return cfg, nil
}
@ -217,20 +216,6 @@ func parseEnvDuration(input string) (time.Duration, error) {
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

View File

@ -1,21 +1,16 @@
package cosmovisor
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"testing"
"time"
"github.com/rs/zerolog"
"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"
)
@ -632,8 +627,8 @@ func (s *argsTestSuite) TestGetConfigFromEnv() {
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")
}
@ -642,91 +637,6 @@ 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...)
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)
}
tests := []struct {
name string
cfg *Config
err error
contains []string
notcontains []string
}{
{
name: "normal error",
cfg: nil,
err: errNormal,
contains: []string{"configuration error", errNormal.Error()}, // TODO: Fix this.
notcontains: nil,
},
{
name: "multi error",
cfg: nil,
err: errMulti,
contains: []string{"configuration errors found", errs[0].Error(), errs[1].Error(), errs[2].Error()},
notcontains: nil,
},
{
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{" "},
},
}
for _, tc := range tests {
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)
}
})
}
}
var sink interface{}
func BenchmarkDetailString(b *testing.B) {

View File

@ -11,7 +11,6 @@ import (
"cosmossdk.io/log"
"cosmossdk.io/tools/cosmovisor"
cverrors "cosmossdk.io/tools/cosmovisor/errors"
"cosmossdk.io/x/upgrade/plan"
)
@ -106,7 +105,7 @@ func getConfigForInitCmd() (*cosmovisor.Config, error) {
errs = append(errs, fmt.Errorf("%s must be an absolute path", cosmovisor.EnvHome))
}
if len(errs) > 0 {
return nil, cverrors.FlattenErrors(errs...)
return nil, errors.Join(errs...)
}
return cfg, nil
}

View File

@ -5,7 +5,6 @@ import (
"os"
"cosmossdk.io/log"
cverrors "cosmossdk.io/tools/cosmovisor/errors"
)
func main() {
@ -13,7 +12,15 @@ func main() {
ctx := context.WithValue(context.Background(), log.ContextKey, logger)
if err := NewRootCmd().ExecuteContext(ctx); err != nil {
cverrors.LogErrors(logger, "", err)
if errMulti, ok := err.(interface{ Unwrap() []error }); ok {
err := errMulti.Unwrap()
for _, e := range err {
logger.Error("", "error", e)
}
} else {
logger.Error("", "error", err)
}
os.Exit(1)
}
}

View File

@ -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)
}
}

View File

@ -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)
})
}
}