Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit df10559

Browse files
authored
Handle wac template parse errors (#271)
- Template parse errors -> clog in `handleAPIError` - Handles precondition errors better
1 parent e509596 commit df10559

File tree

3 files changed

+106
-14
lines changed

3 files changed

+106
-14
lines changed

coder-sdk/error.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,52 @@ var ErrPermissions = xerrors.New("insufficient permissions")
1717
// ErrAuthentication describes the error case in which the requester has invalid authentication.
1818
var ErrAuthentication = xerrors.New("invalid authentication")
1919

20-
// apiError is the expected payload format for our errors.
21-
type apiError struct {
22-
Err apiErrorMsg `json:"error"`
20+
// APIError is the expected payload format for API errors.
21+
type APIError struct {
22+
Err APIErrorMsg `json:"error"`
2323
}
2424

25-
// apiErrorMsg contains the rich error information returned by API errors.
26-
type apiErrorMsg struct {
27-
Msg string `json:"msg"`
25+
// APIErrorMsg contains the rich error information returned by API errors.
26+
type APIErrorMsg struct {
27+
Msg string `json:"msg"`
28+
Code string `json:"code"`
29+
Details json.RawMessage `json:"details"`
2830
}
2931

3032
// HTTPError represents an error from the Coder API.
3133
type HTTPError struct {
3234
*http.Response
35+
cached *APIError
36+
cachedErr error
3337
}
3438

35-
func (e *HTTPError) Error() string {
36-
var msg apiError
39+
// Payload decode the response body into the standard error structure. The `details`
40+
// section is stored as a raw json, and type depends on the `code` field.
41+
func (e *HTTPError) Payload() (*APIError, error) {
42+
var msg APIError
43+
if e.cached != nil || e.cachedErr != nil {
44+
return e.cached, e.cachedErr
45+
}
46+
3747
// Try to decode the payload as an error, if it fails or if there is no error message,
3848
// return the response URL with the status.
39-
if err := json.NewDecoder(e.Response.Body).Decode(&msg); err != nil || msg.Err.Msg == "" {
49+
if err := json.NewDecoder(e.Response.Body).Decode(&msg); err != nil {
50+
e.cachedErr = err
51+
return nil, err
52+
}
53+
54+
e.cached = &msg
55+
return &msg, nil
56+
}
57+
58+
func (e *HTTPError) Error() string {
59+
apiErr, err := e.Payload()
60+
if err != nil || apiErr.Err.Msg == "" {
4061
return fmt.Sprintf("%s: %d %s", e.Request.URL, e.StatusCode, e.Status)
4162
}
4263

4364
// If the payload was a in the expected error format with a message, include it.
44-
return msg.Err.Msg
65+
return apiErr.Err.Msg
4566
}
4667

4768
func bodyError(resp *http.Response) error {

internal/cmd/envs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,8 +290,8 @@ func createEnvFromRepoCmd() *cobra.Command {
290290
Long: "Create a new Coder environment from a config file.",
291291
Hidden: true,
292292
Example: `# create a new environment from git repository template
293-
coder envs create-from-repo --repo-url github.com/cdr/m --branch my-branch
294-
coder envs create-from-repo -f coder.yaml`,
293+
coder envs create-from-config --repo-url github.com/cdr/m --branch my-branch
294+
coder envs create-from-config -f coder.yaml`,
295295
RunE: func(cmd *cobra.Command, args []string) error {
296296
ctx := cmd.Context()
297297

@@ -355,7 +355,7 @@ coder envs create-from-repo -f coder.yaml`,
355355

356356
tpl, err := client.ParseTemplate(ctx, req)
357357
if err != nil {
358-
return xerrors.Errorf("parse environment template config: %w", err)
358+
return handleAPIError(err)
359359
}
360360

361361
provider, err := coderutil.DefaultWorkspaceProvider(ctx, client)
@@ -370,7 +370,7 @@ coder envs create-from-repo -f coder.yaml`,
370370
Namespace: provider.DefaultNamespace,
371371
})
372372
if err != nil {
373-
return xerrors.Errorf("create environment: %w", err)
373+
return handleAPIError(err)
374374
}
375375

376376
if follow {

internal/cmd/errors.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"golang.org/x/xerrors"
8+
9+
"cdr.dev/coder-cli/coder-sdk"
10+
"cdr.dev/coder-cli/pkg/clog"
11+
)
12+
13+
// handleAPIError attempts to convert an api error into a more detailed clog error.
14+
// If it cannot, it will return the original error.
15+
func handleAPIError(origError error) error {
16+
var httpError *coder.HTTPError
17+
if !xerrors.As(origError, &httpError) {
18+
return origError // Return the original
19+
}
20+
21+
ae, err := httpError.Payload()
22+
if err != nil {
23+
return origError // Return the original
24+
}
25+
26+
switch ae.Err.Code {
27+
case "wac_template": // template parse errors
28+
type templatePayload struct {
29+
ErrorType string `json:"error_type"`
30+
Msgs []string `json:"messages"`
31+
}
32+
33+
var p templatePayload
34+
err := json.Unmarshal(ae.Err.Details, &p)
35+
if err != nil {
36+
return origError
37+
}
38+
39+
return clog.Error(p.ErrorType, p.Msgs...)
40+
case "verbose":
41+
type verbosePayload struct {
42+
Verbose string `json:"verbose"`
43+
}
44+
var p verbosePayload
45+
err := json.Unmarshal(ae.Err.Details, &p)
46+
if err != nil {
47+
return origError
48+
}
49+
50+
return clog.Error(origError.Error(), p.Verbose)
51+
case "precondition":
52+
type preconditionPayload struct {
53+
Error string `json:"error"`
54+
Message string `json:"message"`
55+
Solution string `json:"solution"`
56+
}
57+
58+
var p preconditionPayload
59+
err := json.Unmarshal(ae.Err.Details, &p)
60+
if err != nil {
61+
return origError
62+
}
63+
64+
return clog.Error(fmt.Sprintf("Precondition Error : Status Code=%d", httpError.StatusCode),
65+
p.Message,
66+
clog.BlankLine,
67+
clog.Tipf(p.Solution))
68+
}
69+
70+
return origError // Return the original
71+
}

0 commit comments

Comments
 (0)