Skip to content

Commit 4aa3e05

Browse files
authored
fix: print cleaner error message if authentication failed (#249)
1 parent d1cabbe commit 4aa3e05

7 files changed

Lines changed: 62 additions & 5 deletions

File tree

internal/clierrors/client_error.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package clierrors
33
const (
44
UnspecifiedErrorCode int = 100
55
InterruptSignalCode int = 101
6+
MissingCredentials int = 102
7+
InvalidCredentials int = 103
68
)
79

810
// ClientError declares interface for errors known to the client that set specific error code.

internal/clierrors/command_failed.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ type CommandFailedError struct {
66
FailedCount int
77
}
88

9+
var _ ClientError = CommandFailedError{}
10+
911
func min(a, b int) int {
1012
if a < b {
1113
return a
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package clierrors
2+
3+
import (
4+
"errors"
5+
6+
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud"
7+
)
8+
9+
var _ ClientError = InvalidCredentialsError{}
10+
11+
type InvalidCredentialsError struct{}
12+
13+
func (err InvalidCredentialsError) ErrorCode() int {
14+
return InvalidCredentials
15+
}
16+
17+
func (err InvalidCredentialsError) Error() string {
18+
return "invalid user credentials, authentication failed using the given username and password"
19+
}
20+
21+
func CheckAuthenticationFailed(err error) bool {
22+
prob := &upcloud.Problem{}
23+
24+
if errors.As(err, &prob) {
25+
errCode := prob.ErrorCode()
26+
if errCode == upcloud.ErrCodeAuthenticationFailed || errCode == "INVALID_CREDENTIALS" {
27+
return true
28+
}
29+
}
30+
31+
return false
32+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package clierrors
2+
3+
import "fmt"
4+
5+
var _ ClientError = MissingCredentialsError{}
6+
7+
type MissingCredentialsError struct {
8+
ConfigFile string
9+
}
10+
11+
func (err MissingCredentialsError) ErrorCode() int {
12+
return MissingCredentials
13+
}
14+
15+
func (err MissingCredentialsError) Error() string {
16+
return fmt.Sprintf("user credentials not found, these must be set in config file (%s) or via environment variables", err.ConfigFile)
17+
}

internal/commands/command.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,7 @@ func BuildCommand(child Command, parent *cobra.Command, config *config.Config) C
115115
if err != nil {
116116
// Error was caused by missing credentials, not incorrect command
117117
child.Cobra().SilenceUsage = true
118-
119-
return fmt.Errorf("cannot create service: %w", err)
118+
return err
120119
}
121120
return commandRunE(child, service, config, args)
122121
}

internal/commands/runcommand.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package commands
33
import (
44
"fmt"
55

6+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/clierrors"
67
"github.com/UpCloudLtd/upcloud-cli/v2/internal/config"
78
"github.com/UpCloudLtd/upcloud-cli/v2/internal/output"
89
"github.com/UpCloudLtd/upcloud-cli/v2/internal/resolver"
@@ -92,6 +93,10 @@ func resolveArguments(nc Command, exec Executor, args []string) (out []resolvedA
9293
func execute(command Command, executor Executor, args []string, parallelRuns int, executeCommand func(exec Executor, arg string) (output.Output, error)) ([]output.Output, error) {
9394
resolvedArgs, err := resolveArguments(command, executor, args)
9495
if err != nil {
96+
// If authentication failed, return helpful message instead of the raw error.
97+
if clierrors.CheckAuthenticationFailed(err) {
98+
return nil, clierrors.InvalidCredentialsError{}
99+
}
95100
return nil, fmt.Errorf("cannot resolve command line arguments: %w", err)
96101
}
97102

internal/config/config.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/UpCloudLtd/upcloud-cli/v2/internal/clierrors"
1112
internal "github.com/UpCloudLtd/upcloud-cli/v2/internal/service"
1213

1314
"github.com/UpCloudLtd/upcloud-go-api/v6/upcloud/client"
@@ -188,14 +189,13 @@ func (s *Config) CreateService() (internal.AllServices, error) {
188189
password := s.GetString("password")
189190

190191
if username == "" || password == "" {
191-
// nb. this might give silghtly unexpected results on OS X, as xdg.ConfigHome points to ~/Library/Application Support
192+
// This might give silghtly unexpected results on OS X, as xdg.ConfigHome points to ~/Library/Application Support
192193
// while we really use/prefer/document ~/.config - which does work on osx as well but won't be displayed here.
193-
// TODO: fix this?
194194
configDetails := fmt.Sprintf("default location %s", filepath.Join(xdg.ConfigHome, "upctl.yaml"))
195195
if s.GetString("config") != "" {
196196
configDetails = fmt.Sprintf("used %s", s.GetString("config"))
197197
}
198-
return nil, fmt.Errorf("user credentials not found, these must be set in config file (%s) or via environment variables", configDetails)
198+
return nil, clierrors.MissingCredentialsError{ConfigFile: configDetails}
199199
}
200200

201201
client := client.New(username, password, client.WithTimeout(s.ClientTimeout()))

0 commit comments

Comments
 (0)