Skip to content

Commit 6b54a20

Browse files
[k8s] Properly parse the kubernetes' errors.StatusError type (#349)
Have the k8s package properly parse the kubernetes' errors.StatusError type if returned and return a `*k8s.ServerResponseError` wrapping it. Add tests for error parsing. Resolves #301 --------- Co-authored-by: Igor Suleymanov <radiohead@users.noreply.github.com>
1 parent feb5e57 commit 6b54a20

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

k8s/gvclient.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"net/http"
910
"strconv"
@@ -14,6 +15,7 @@ import (
1415
"github.com/prometheus/client_golang/prometheus"
1516
"go.opentelemetry.io/otel/attribute"
1617
"go.opentelemetry.io/otel/codes"
18+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
1719
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1820
"k8s.io/apimachinery/pkg/types"
1921
"k8s.io/apimachinery/pkg/watch"
@@ -555,11 +557,23 @@ type k8sErrBody struct {
555557
// Ths method will parse the response body for a better error message if available, and return a *ServerResponseError
556558
// if the status code is a non-success (>= 300).
557559
func parseKubernetesError(responseBytes []byte, statusCode int, err error) error {
560+
if err != nil {
561+
statusErr := &k8serrors.StatusError{}
562+
if errors.As(err, &statusErr) {
563+
if statusCode == 0 || (statusErr.ErrStatus.Code > 0 && statusCode != int(statusErr.ErrStatus.Code)) {
564+
return NewServerResponseError(statusErr, int(statusErr.ErrStatus.Code))
565+
}
566+
return NewServerResponseError(statusErr, statusCode)
567+
}
568+
}
558569
if len(responseBytes) > 0 {
559570
parsed := k8sErrBody{}
560571
// If we can parse the response body, use the error contained there instead, because it's clearer
561572
if e := json.Unmarshal(responseBytes, &parsed); e == nil {
562573
err = fmt.Errorf(parsed.Message)
574+
if statusCode == 0 && parsed.Code > 0 {
575+
statusCode = parsed.Code
576+
}
563577
}
564578
}
565579
// HTTP error?

k8s/gvclient_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package k8s
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/stretchr/testify/assert"
9+
k8serrors "k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
)
12+
13+
func TestParseKubernetesError(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
bytes []byte
17+
statusCode int
18+
err error
19+
expectedErr error
20+
}{{
21+
name: "status error",
22+
bytes: nil,
23+
statusCode: 0,
24+
err: &k8serrors.StatusError{
25+
ErrStatus: metav1.Status{
26+
Message: "I AM ERROR",
27+
Code: http.StatusInternalServerError,
28+
},
29+
},
30+
expectedErr: NewServerResponseError(&k8serrors.StatusError{
31+
ErrStatus: metav1.Status{
32+
Message: "I AM ERROR",
33+
Code: http.StatusInternalServerError,
34+
},
35+
}, http.StatusInternalServerError),
36+
}, {
37+
name: "status error, code conflicts with returned response code",
38+
bytes: nil,
39+
statusCode: http.StatusConflict,
40+
err: &k8serrors.StatusError{
41+
ErrStatus: metav1.Status{
42+
Message: "I AM ERROR",
43+
Code: http.StatusInternalServerError,
44+
},
45+
},
46+
expectedErr: NewServerResponseError(&k8serrors.StatusError{
47+
ErrStatus: metav1.Status{
48+
Message: "I AM ERROR",
49+
Code: http.StatusInternalServerError,
50+
},
51+
}, http.StatusInternalServerError),
52+
}, {
53+
name: "status error, code conflicts with returned response code (status.Code 0)",
54+
bytes: nil,
55+
statusCode: http.StatusConflict,
56+
err: &k8serrors.StatusError{
57+
ErrStatus: metav1.Status{
58+
Message: "I AM ERROR",
59+
},
60+
},
61+
expectedErr: NewServerResponseError(&k8serrors.StatusError{
62+
ErrStatus: metav1.Status{
63+
Message: "I AM ERROR",
64+
},
65+
}, http.StatusConflict),
66+
}, {
67+
name: "other error type, body JSON",
68+
bytes: []byte(`{"code":404,"message":"no way"}`),
69+
err: fmt.Errorf("no way"),
70+
expectedErr: NewServerResponseError(fmt.Errorf("no way"), http.StatusNotFound),
71+
}, {
72+
name: "other error type, body JSON, no status code",
73+
bytes: []byte(`{"message":"no way"}`),
74+
err: fmt.Errorf("no way 2"),
75+
expectedErr: fmt.Errorf("no way"),
76+
}, {
77+
name: "other error type, no body JSON",
78+
err: fmt.Errorf("nope"),
79+
expectedErr: fmt.Errorf("nope"),
80+
}}
81+
82+
for _, test := range tests {
83+
t.Run(test.name, func(t *testing.T) {
84+
err := parseKubernetesError(test.bytes, test.statusCode, test.err)
85+
assert.Equal(t, test.expectedErr, err)
86+
})
87+
}
88+
}

0 commit comments

Comments
 (0)