1
16
17 package rest
18
19 import (
20 "context"
21 "fmt"
22 "net/http"
23 "net/http/httptest"
24 "net/http/httputil"
25 "net/url"
26 "reflect"
27 "testing"
28 "time"
29
30 v1 "k8s.io/api/core/v1"
31 v1beta1 "k8s.io/api/extensions/v1beta1"
32 "k8s.io/apimachinery/pkg/api/errors"
33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34 "k8s.io/apimachinery/pkg/runtime"
35 "k8s.io/apimachinery/pkg/types"
36 "k8s.io/client-go/kubernetes/scheme"
37 utiltesting "k8s.io/client-go/util/testing"
38
39 "github.com/google/go-cmp/cmp"
40 )
41
42 type TestParam struct {
43 actualError error
44 expectingError bool
45 actualCreated bool
46 expCreated bool
47 expStatus *metav1.Status
48 testBody bool
49 testBodyErrorIsNotNil bool
50 }
51
52
53 func TestSerializer(t *testing.T) {
54 gv := v1beta1.SchemeGroupVersion
55 contentConfig := ContentConfig{
56 ContentType: "application/json",
57 GroupVersion: &gv,
58 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
59 }
60
61 n := runtime.NewClientNegotiator(contentConfig.NegotiatedSerializer, gv)
62 d, err := n.Decoder("application/json", nil)
63 if err != nil {
64 t.Fatal(err)
65 }
66
67
68 obj, err := runtime.Decode(d, []byte(`{"kind":"Status","apiVersion":"v1","metadata":{},"status":"Success"}`))
69 t.Log(obj)
70 if err != nil {
71 t.Fatal(err)
72 }
73 }
74
75 func TestDoRequestSuccess(t *testing.T) {
76 testServer, fakeHandler, status := testServerEnv(t, 200)
77 defer testServer.Close()
78
79 c, err := restClient(testServer)
80 if err != nil {
81 t.Fatalf("unexpected error: %v", err)
82 }
83 body, err := c.Get().Prefix("test").Do(context.Background()).Raw()
84
85 testParam := TestParam{actualError: err, expectingError: false, expCreated: true,
86 expStatus: status, testBody: true, testBodyErrorIsNotNil: false}
87 validate(testParam, t, body, fakeHandler)
88 }
89
90 func TestDoRequestFailed(t *testing.T) {
91 status := &metav1.Status{
92 Code: http.StatusNotFound,
93 Status: metav1.StatusFailure,
94 Reason: metav1.StatusReasonNotFound,
95 Message: " \"\" not found",
96 Details: &metav1.StatusDetails{},
97 }
98 expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
99 fakeHandler := utiltesting.FakeHandler{
100 StatusCode: 404,
101 ResponseBody: string(expectedBody),
102 T: t,
103 }
104 testServer := httptest.NewServer(&fakeHandler)
105 defer testServer.Close()
106
107 c, err := restClient(testServer)
108 if err != nil {
109 t.Fatalf("unexpected error: %v", err)
110 }
111 err = c.Get().Do(context.Background()).Error()
112 if err == nil {
113 t.Errorf("unexpected non-error")
114 }
115 ss, ok := err.(errors.APIStatus)
116 if !ok {
117 t.Errorf("unexpected error type %v", err)
118 }
119 actual := ss.Status()
120 if !reflect.DeepEqual(status, &actual) {
121 t.Errorf("Unexpected mis-match: %s", cmp.Diff(status, &actual))
122 }
123 }
124
125 func TestDoRawRequestFailed(t *testing.T) {
126 status := &metav1.Status{
127 Code: http.StatusNotFound,
128 Status: metav1.StatusFailure,
129 Reason: metav1.StatusReasonNotFound,
130 Message: "the server could not find the requested resource",
131 Details: &metav1.StatusDetails{
132 Causes: []metav1.StatusCause{
133 {Type: metav1.CauseTypeUnexpectedServerResponse, Message: "unknown"},
134 },
135 },
136 }
137 expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
138 fakeHandler := utiltesting.FakeHandler{
139 StatusCode: 404,
140 ResponseBody: string(expectedBody),
141 T: t,
142 }
143 testServer := httptest.NewServer(&fakeHandler)
144 defer testServer.Close()
145
146 c, err := restClient(testServer)
147 if err != nil {
148 t.Fatalf("unexpected error: %v", err)
149 }
150 body, err := c.Get().Do(context.Background()).Raw()
151
152 if err == nil || body == nil {
153 t.Errorf("unexpected non-error: %#v", body)
154 }
155 ss, ok := err.(errors.APIStatus)
156 if !ok {
157 t.Errorf("unexpected error type %v", err)
158 }
159 actual := ss.Status()
160 if !reflect.DeepEqual(status, &actual) {
161 t.Errorf("Unexpected mis-match: %s", cmp.Diff(status, &actual))
162 }
163 }
164
165 func TestDoRequestCreated(t *testing.T) {
166 testServer, fakeHandler, status := testServerEnv(t, 201)
167 defer testServer.Close()
168
169 c, err := restClient(testServer)
170 if err != nil {
171 t.Fatalf("unexpected error: %v", err)
172 }
173 created := false
174 body, err := c.Get().Prefix("test").Do(context.Background()).WasCreated(&created).Raw()
175
176 testParam := TestParam{actualError: err, expectingError: false, expCreated: true,
177 expStatus: status, testBody: false}
178 validate(testParam, t, body, fakeHandler)
179 }
180
181 func TestDoRequestNotCreated(t *testing.T) {
182 testServer, fakeHandler, expectedStatus := testServerEnv(t, 202)
183 defer testServer.Close()
184 c, err := restClient(testServer)
185 if err != nil {
186 t.Fatalf("unexpected error: %v", err)
187 }
188 created := false
189 body, err := c.Get().Prefix("test").Do(context.Background()).WasCreated(&created).Raw()
190 testParam := TestParam{actualError: err, expectingError: false, expCreated: false,
191 expStatus: expectedStatus, testBody: false}
192 validate(testParam, t, body, fakeHandler)
193 }
194
195 func TestDoRequestAcceptedNoContentReturned(t *testing.T) {
196 testServer, fakeHandler, _ := testServerEnv(t, 204)
197 defer testServer.Close()
198
199 c, err := restClient(testServer)
200 if err != nil {
201 t.Fatalf("unexpected error: %v", err)
202 }
203 created := false
204 body, err := c.Get().Prefix("test").Do(context.Background()).WasCreated(&created).Raw()
205 testParam := TestParam{actualError: err, expectingError: false, expCreated: false,
206 testBody: false}
207 validate(testParam, t, body, fakeHandler)
208 }
209
210 func TestBadRequest(t *testing.T) {
211 testServer, fakeHandler, _ := testServerEnv(t, 400)
212 defer testServer.Close()
213 c, err := restClient(testServer)
214 if err != nil {
215 t.Fatalf("unexpected error: %v", err)
216 }
217 created := false
218 body, err := c.Get().Prefix("test").Do(context.Background()).WasCreated(&created).Raw()
219 testParam := TestParam{actualError: err, expectingError: true, expCreated: false,
220 testBody: true}
221 validate(testParam, t, body, fakeHandler)
222 }
223
224 func validate(testParam TestParam, t *testing.T, body []byte, fakeHandler *utiltesting.FakeHandler) {
225 switch {
226 case testParam.expectingError && testParam.actualError == nil:
227 t.Errorf("Expected error")
228 case !testParam.expectingError && testParam.actualError != nil:
229 t.Error(testParam.actualError)
230 }
231 if !testParam.expCreated {
232 if testParam.actualCreated {
233 t.Errorf("Expected object not to be created")
234 }
235 }
236 statusOut, err := runtime.Decode(scheme.Codecs.UniversalDeserializer(), body)
237 if testParam.testBody {
238 if testParam.testBodyErrorIsNotNil && err == nil {
239 t.Errorf("Expected Error")
240 }
241 if !testParam.testBodyErrorIsNotNil && err != nil {
242 t.Errorf("Unexpected Error: %v", err)
243 }
244 }
245
246 if testParam.expStatus != nil {
247 if !reflect.DeepEqual(testParam.expStatus, statusOut) {
248 t.Errorf("Unexpected mis-match. Expected %#v. Saw %#v", testParam.expStatus, statusOut)
249 }
250 }
251 fakeHandler.ValidateRequest(t, "/"+v1.SchemeGroupVersion.String()+"/test", "GET", nil)
252
253 }
254
255 func TestHTTPMethods(t *testing.T) {
256 testServer, _, _ := testServerEnv(t, 200)
257 defer testServer.Close()
258 c, _ := restClient(testServer)
259
260 request := c.Post()
261 if request == nil {
262 t.Errorf("Post : Object returned should not be nil")
263 }
264
265 request = c.Get()
266 if request == nil {
267 t.Errorf("Get: Object returned should not be nil")
268 }
269
270 request = c.Put()
271 if request == nil {
272 t.Errorf("Put : Object returned should not be nil")
273 }
274
275 request = c.Delete()
276 if request == nil {
277 t.Errorf("Delete : Object returned should not be nil")
278 }
279
280 request = c.Patch(types.JSONPatchType)
281 if request == nil {
282 t.Errorf("Patch : Object returned should not be nil")
283 }
284 }
285
286 func TestHTTPProxy(t *testing.T) {
287 ctx := context.Background()
288 testServer, fh, _ := testServerEnv(t, 200)
289 fh.ResponseBody = "backend data"
290 defer testServer.Close()
291
292 testProxyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
293 to, err := url.Parse(req.RequestURI)
294 if err != nil {
295 t.Fatalf("err: %v", err)
296 }
297 w.Write([]byte("proxied: "))
298 httputil.NewSingleHostReverseProxy(to).ServeHTTP(w, req)
299 }))
300 defer testProxyServer.Close()
301
302 t.Logf(testProxyServer.URL)
303
304 u, err := url.Parse(testProxyServer.URL)
305 if err != nil {
306 t.Fatalf("Failed to parse test proxy server url: %v", err)
307 }
308
309 c, err := RESTClientFor(&Config{
310 Host: testServer.URL,
311 ContentConfig: ContentConfig{
312 GroupVersion: &v1.SchemeGroupVersion,
313 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
314 },
315 Proxy: http.ProxyURL(u),
316 Username: "user",
317 Password: "pass",
318 })
319 if err != nil {
320 t.Fatalf("Failed to create client: %v", err)
321 }
322
323 request := c.Get()
324 if request == nil {
325 t.Fatalf("Get: Object returned should not be nil")
326 }
327
328 b, err := request.DoRaw(ctx)
329 if err != nil {
330 t.Fatalf("unexpected err: %v", err)
331 }
332 if got, want := string(b), "proxied: backend data"; !cmp.Equal(got, want) {
333 t.Errorf("unexpected body: %v", cmp.Diff(want, got))
334 }
335 }
336
337 func TestCreateBackoffManager(t *testing.T) {
338
339 theUrl, _ := url.Parse("http://localhost")
340
341
342 t.Setenv(envBackoffBase, "1")
343 t.Setenv(envBackoffDuration, "2")
344 backoff := readExpBackoffConfig()
345 backoff.UpdateBackoff(theUrl, nil, 500)
346 backoff.UpdateBackoff(theUrl, nil, 500)
347 if backoff.CalculateBackoff(theUrl)/time.Second != 2 {
348 t.Errorf("Backoff env not working.")
349 }
350
351
352 t.Setenv(envBackoffBase, "1")
353 t.Setenv(envBackoffDuration, "0")
354 backoff.UpdateBackoff(theUrl, nil, 500)
355 backoff.UpdateBackoff(theUrl, nil, 500)
356 backoff = readExpBackoffConfig()
357 if backoff.CalculateBackoff(theUrl)/time.Second != 0 {
358 t.Errorf("Zero backoff duration, but backoff still occurring.")
359 }
360
361
362 t.Setenv(envBackoffBase, "")
363 t.Setenv(envBackoffDuration, "")
364 backoff = readExpBackoffConfig()
365 backoff.UpdateBackoff(theUrl, nil, 500)
366 backoff.UpdateBackoff(theUrl, nil, 500)
367 if backoff.CalculateBackoff(theUrl)/time.Second != 0 {
368 t.Errorf("Backoff should have been 0.")
369 }
370
371 }
372
373 func testServerEnv(t *testing.T, statusCode int) (*httptest.Server, *utiltesting.FakeHandler, *metav1.Status) {
374 status := &metav1.Status{TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Status"}, Status: fmt.Sprintf("%s", metav1.StatusSuccess)}
375 expectedBody, _ := runtime.Encode(scheme.Codecs.LegacyCodec(v1.SchemeGroupVersion), status)
376 fakeHandler := utiltesting.FakeHandler{
377 StatusCode: statusCode,
378 ResponseBody: string(expectedBody),
379 T: t,
380 }
381 testServer := httptest.NewServer(&fakeHandler)
382 return testServer, &fakeHandler, status
383 }
384
385 func restClient(testServer *httptest.Server) (*RESTClient, error) {
386 c, err := RESTClientFor(&Config{
387 Host: testServer.URL,
388 ContentConfig: ContentConfig{
389 GroupVersion: &v1.SchemeGroupVersion,
390 NegotiatedSerializer: scheme.Codecs.WithoutConversion(),
391 },
392 Username: "user",
393 Password: "pass",
394 })
395 return c, err
396 }
397
View as plain text