Skip to content

Commit 812244d

Browse files
committed
TUN-3443: Decode as v4api response on non-200 status
1 parent be7b7c7 commit 812244d

File tree

2 files changed

+38
-23
lines changed

2 files changed

+38
-23
lines changed

tunnelstore/client.go

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -376,16 +376,12 @@ func (r *RESTClient) sendRequest(method string, url url.URL, body interface{}) (
376376
func parseResponse(reader io.Reader, data interface{}) error {
377377
// Schema for Tunnelstore responses in the v1 API.
378378
// Roughly, it's a wrapper around a particular result that adds failures/errors/etc
379-
var result struct {
380-
Result json.RawMessage `json:"result"`
381-
Success bool `json:"success"`
382-
Errors []string `json:"errors"`
383-
}
379+
var result response
384380
// First, parse the wrapper and check the API call succeeded
385381
if err := json.NewDecoder(reader).Decode(&result); err != nil {
386382
return errors.Wrap(err, "failed to decode response")
387383
}
388-
if err := checkErrors(result.Errors); err != nil {
384+
if err := result.checkErrors(); err != nil {
389385
return err
390386
}
391387
if !result.Success {
@@ -402,24 +398,43 @@ func unmarshalTunnel(reader io.Reader) (*Tunnel, error) {
402398
return &tunnel, err
403399
}
404400

405-
func checkErrors(errs []string) error {
406-
if len(errs) == 0 {
401+
type response struct {
402+
Success bool `json:"success,omitempty"`
403+
Errors []apiErr `json:"errors,omitempty"`
404+
Messages []string `json:"messages,omitempty"`
405+
Result json.RawMessage `json:"result,omitempty"`
406+
}
407+
408+
func (r *response) checkErrors() error {
409+
if len(r.Errors) == 0 {
407410
return nil
408411
}
409-
if len(errs) == 1 {
410-
return fmt.Errorf("API error: %s", errs[0])
412+
if len(r.Errors) == 1 {
413+
return r.Errors[0]
411414
}
412-
allErrs := strings.Join(errs, "; ")
413-
return fmt.Errorf("API errors: %s", allErrs)
415+
var messages string
416+
for _, e := range r.Errors {
417+
messages += fmt.Sprintf("%s; ", e)
418+
}
419+
return fmt.Errorf("API errors: %s", messages)
420+
}
421+
422+
type apiErr struct {
423+
Code json.Number `json:"code,omitempty"`
424+
Message string `json:"message,omitempty"`
425+
}
426+
427+
func (e apiErr) Error() string {
428+
return fmt.Sprintf("code: %v, reason: %s", e.Code, e.Message)
414429
}
415430

416431
func (r *RESTClient) statusCodeToError(op string, resp *http.Response) error {
417432
if resp.Header.Get("Content-Type") == "application/json" {
418-
var errorsResp struct {
419-
Error string `json:"error"`
420-
}
421-
if json.NewDecoder(resp.Body).Decode(&errorsResp) == nil && errorsResp.Error != "" {
422-
return errors.Errorf("Failed to %s: %s", op, errorsResp.Error)
433+
var errorsResp response
434+
if json.NewDecoder(resp.Body).Decode(&errorsResp) == nil {
435+
if err := errorsResp.checkErrors(); err != nil {
436+
return errors.Errorf("Failed to %s: %s", op, err)
437+
}
423438
}
424439
}
425440

tunnelstore/client_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ func TestDNSRouteUnmarshalResult(t *testing.T) {
3030
badJSON := []string{
3131
`abc`,
3232
`{"success": false, "result": {"cname": "new"}}`,
33-
`{"errors": ["foo"], "result": {"cname": "new"}}`,
34-
`{"errors": ["foo", "bar"], "result": {"cname": "new"}}`,
33+
`{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}], "result": {"cname": "new"}}`,
34+
`{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}, {"code": 1004, "message":"Cannot use tunnel as origin for non-proxied load balancer"}], "result": {"cname": "new"}}`,
3535
`{"result": {"cname": "new"}}`,
3636
`{"result": {"cname": "new"}}`,
3737
}
@@ -60,8 +60,8 @@ func TestLBRouteUnmarshalResult(t *testing.T) {
6060
badJSON := []string{
6161
`abc`,
6262
`{"success": false, "result": {"pool": "unchanged", "load_balancer": "updated"}}`,
63-
`{"errors": ["foo"], "result": {"pool": "unchanged", "load_balancer": "updated"}}`,
64-
`{"errors": ["foo", "bar"], "result": {"pool": "unchanged", "load_balancer": "updated"}}`,
63+
`{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}], "result": {"pool": "unchanged", "load_balancer": "updated"}}`,
64+
`{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}, {"code": 1004, "message":"Cannot use tunnel as origin for non-proxied load balancer"}], "result": {"pool": "unchanged", "load_balancer": "updated"}}`,
6565
`{"result": {"pool": "unchanged", "load_balancer": "updated"}}`,
6666
}
6767

@@ -127,7 +127,7 @@ func Test_parseListTunnels(t *testing.T) {
127127
},
128128
{
129129
name: "errors are present",
130-
args: args{body: `{"errors": ["foo"], "result": []}`},
130+
args: args{body: `{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}], "result": []}`},
131131
wantErr: true,
132132
},
133133
{
@@ -197,7 +197,7 @@ func TestUnmarshalTunnelErr(t *testing.T) {
197197
`abc`,
198198
`{"success": true, "result": abc}`,
199199
`{"success": false, "result": {"id": "00000000-0000-0000-0000-000000000000","name":"test","created_at":"0001-01-01T00:00:00Z","connections":[]}}}`,
200-
`{"errors": ["foo"], "result": {"id": "00000000-0000-0000-0000-000000000000","name":"test","created_at":"0001-01-01T00:00:00Z","connections":[]}}}`,
200+
`{"errors": [{"code": 1003, "message":"An A, AAAA or CNAME record already exists with that host"}], "result": {"id": "00000000-0000-0000-0000-000000000000","name":"test","created_at":"0001-01-01T00:00:00Z","connections":[]}}}`,
201201
}
202202

203203
for i, test := range tests {

0 commit comments

Comments
 (0)