Skip to content

Commit

Permalink
cli: add support command to list error code description
Browse files Browse the repository at this point in the history
Adds a new command `osm support error-info` that lists
the mapping of one or all error codes to their description.

Part of openservicemesh#2866

Signed-off-by: Shashank Ram <shashr2204@gmail.com>
  • Loading branch information
shashankram committed Jul 2, 2021
1 parent 230b321 commit 72ac7e5
Show file tree
Hide file tree
Showing 5 changed files with 185 additions and 7 deletions.
1 change: 1 addition & 0 deletions cmd/cli/osm.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func newRootCmd(config *action.Configuration, in io.Reader, out io.Writer, args
newProxyCmd(config, out),
newTrafficPolicyCmd(out),
newUninstallCmd(config, in, out),
newSupportCmd(out),
)

_ = flags.Parse(args)
Expand Down
24 changes: 24 additions & 0 deletions cmd/cli/support.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package main

import (
"io"

"github.com/spf13/cobra"
)

const supportCmdDescription = `
This command consists of multiple subcommands related supportability and
associated tooling, such as examining error codes.
`

func newSupportCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "support",
Short: "supportability tooling",
Long: supportCmdDescription,
Args: cobra.NoArgs,
}
cmd.AddCommand(newSupportErrInfoCmd(out))

return cmd
}
88 changes: 88 additions & 0 deletions cmd/cli/support_err.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package main

import (
"io"
"sort"

"github.com/olekukonko/tablewriter"
"github.com/pkg/errors"
"github.com/spf13/cobra"

"github.com/openservicemesh/osm/pkg/errcode"
)

const errInfoDescription = `
This command lists the mapping of one or all error codes to their description.`

const errInfoExample = `
Get the description for the error code E1000
# osm support error-info E1000
Get the description for all error codes
# osm support error-info
`

type errInfoCmd struct {
out io.Writer
}

func newSupportErrInfoCmd(out io.Writer) *cobra.Command {
errInfoCmd := &errInfoCmd{
out: out,
}

cmd := &cobra.Command{
Use: "error-info",
Short: "lists mapping of error code to its description",
Long: errInfoDescription,
Args: cobra.MaximumNArgs(1),
RunE: func(_ *cobra.Command, args []string) error {
var errCode string
if len(args) != 0 {
errCode = args[0]
}
return errInfoCmd.run(errCode)
},
Example: errInfoExample,
}

return cmd
}

func (cmd *errInfoCmd) run(errCode string) error {
table := tablewriter.NewWriter(cmd.out)
table.SetHeader([]string{"Error code", "Description"})
table.SetRowLine(true)
table.SetColWidth(70)

if errCode != "" {
// Print the error code description mapping only for the given error code
e, err := errcode.FromStr(errCode)
if err != nil {
return errors.Errorf("Error code '%s' is not a valid error code format. Should be of the form Exxxx, ex. E1000.", errCode)
}
description, ok := errcode.ErrCodeMap[e]
if !ok {
return errors.Errorf("Error code '%s' is not a valid error code recognized by OSM", errCode)
}
table.Append([]string{errCode, description})
} else {
// Print the error code description mapping for all error codes
var sortedErrKeys []errcode.ErrCode
for err := range errcode.ErrCodeMap {
sortedErrKeys = append(sortedErrKeys, err)
}
sort.Slice(sortedErrKeys, func(i, j int) bool {
return sortedErrKeys[i] < sortedErrKeys[j]
})

for _, key := range sortedErrKeys {
desc := errcode.ErrCodeMap[key]
table.Append([]string{key.String(), desc})
}
}

table.Render()

return nil
}
51 changes: 51 additions & 0 deletions cmd/cli/support_err_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package main

import (
"bytes"
"testing"

tassert "github.com/stretchr/testify/assert"
)

// TestErrInfoRun tests errInfoCmd.run()

func TestErrInfoRun(t *testing.T) {
assert := tassert.New(t)

testCases := []struct {
name string
errCode string
expectErr bool
}{
{
name: "valid error code as input",
errCode: "E1000",
expectErr: false,
},
{
name: "invalid error code format as input",
errCode: "Foo",
expectErr: true,
},
{
name: "valid error code format but unrecognized code as input",
errCode: "E10000",
expectErr: true,
},
{
name: "list all error codes",
errCode: "",
expectErr: false,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var out bytes.Buffer
cmd := &errInfoCmd{out: &out}
err := cmd.run(tc.errCode)

assert.Equal(tc.expectErr, err != nil)
})
}
}
28 changes: 21 additions & 7 deletions pkg/errcode/errcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ package errcode

import (
"fmt"
"strconv"
"strings"
)

type errCode int
// ErrCode defines the type to represent error codes
type ErrCode int

const (
// Kind defines the kind for the error code constants
Expand All @@ -16,7 +19,7 @@ const (
// Range 1000-1050 is reserved for errors related to application startup or bootstrapping
const (
// ErrInvalidCLIArgument indicates an invalid CLI argument
ErrInvalidCLIArgument errCode = iota + 1000
ErrInvalidCLIArgument ErrCode = iota + 1000

// ErrSettingLogLevel indicates the specified log level could not be set
ErrSettingLogLevel
Expand All @@ -37,7 +40,7 @@ const (
// Range 2000-2500 is reserved for errors related to traffic policies
const (
// ErrDedupEgressTrafficMatches indicates an error related to deduplicating egress traffic matches
ErrDedupEgressTrafficMatches errCode = iota + 2000
ErrDedupEgressTrafficMatches ErrCode = iota + 2000

// ErrDedupEgressClusterConfigs indicates an error related to deduplicating egress cluster configs
ErrDedupEgressClusterConfigs
Expand Down Expand Up @@ -71,22 +74,33 @@ const (
// Range 3000-3500 is reserved for errors related to k8s constructs (service accounts, namespaces, etc.)
const (
// ErrServiceHostnames indicates the hostnames associated with a service could not be computed
ErrServiceHostnames errCode = iota + 3000
ErrServiceHostnames ErrCode = iota + 3000

// ErrNoMatchingServiceForServiceAccount indicates there are no services associated with the service account
ErrNoMatchingServiceForServiceAccount
)

// String returns the error code as a string, ex. E1000
func (e errCode) String() string {
func (e ErrCode) String() string {
return fmt.Sprintf("E%d", e)
}

// FromStr returns the ErrCode representation for the given error code string
// Ex. E1000 is converted to ErrInvalidCLIArgument
func FromStr(e string) (ErrCode, error) {
errStr := strings.TrimLeft(e, "E")
errInt, err := strconv.Atoi(errStr)
if err != nil {
return ErrCode(-1), err
}
return ErrCode(errInt), nil
}

// ErrCodeMap defines the mapping of error codes to their description.
// Note: error code description mappings must be defined in the same order
// as they appear in the error code definitions - from lowest to highest
// ranges in the order they appear within the range.
//nolint: deadcode,varcheck,unused
var errCodeMap = map[errCode]string{
var ErrCodeMap = map[ErrCode]string{
//
// Range 1000-1050
//
Expand Down

0 comments on commit 72ac7e5

Please sign in to comment.