@@ -3,114 +3,177 @@ package registry
33import (
44 "bytes"
55 "fmt"
6- "io "
6+ "math/rand "
77 "net/http"
8- "strings"
98 "testing"
9+ "time"
1010)
1111
1212type mockHTTPGetter struct {
13- httpGet func (string ) (* http.Response , error )
13+ callNumber int
14+ httpGet func (mockHTTPGetter , string ) (* http.Response , error )
1415}
1516
16- func newMockHTTPGetter (f func (string ) (* http.Response , error )) * mockHTTPGetter {
17+ func newMockHTTPGetter (f func (mockHTTPGetter , string ) (* http.Response , error )) * mockHTTPGetter {
1718 return & mockHTTPGetter {
18- httpGet : f ,
19+ callNumber : 0 ,
20+ httpGet : f ,
1921 }
2022}
21- func (m mockHTTPGetter ) Get (url string ) (resp * http.Response , err error ) {
22- return m .httpGet (url )
23+ func (m * mockHTTPGetter ) Get (url string ) (resp * http.Response , err error ) {
24+ m .callNumber = m .callNumber + 1
25+ return m .httpGet (* m , url )
2326}
2427
2528func TestDownloadSchema (t * testing.T ) {
29+ callCounts := map [string ]int {}
30+
31+ // http server to simulate different responses
32+ http .HandleFunc ("/" , func (w http.ResponseWriter , r * http.Request ) {
33+ var s int
34+ callCounts [r .URL .Path ]++
35+ callCount := callCounts [r .URL .Path ]
36+
37+ switch r .URL .Path {
38+ case "/404" :
39+ s = http .StatusNotFound
40+ case "/500" :
41+ s = http .StatusInternalServerError
42+ case "/503" :
43+ if callCount < 2 {
44+ s = http .StatusServiceUnavailable
45+ } else {
46+ s = http .StatusOK // Should succeed on 3rd try
47+ }
48+
49+ case "/simulate-reset" :
50+ if callCount < 2 {
51+ if hj , ok := w .(http.Hijacker ); ok {
52+ conn , _ , err := hj .Hijack ()
53+ if err != nil {
54+ fmt .Printf ("Hijacking failed: %v\n " , err )
55+ return
56+ }
57+ conn .Close () // Close the connection to simulate a reset
58+ }
59+ return
60+ }
61+ s = http .StatusOK // Should succeed on third try
62+
63+ default :
64+ s = http .StatusOK
65+ }
66+
67+ w .WriteHeader (s )
68+ w .Write ([]byte (http .StatusText (s )))
69+ })
70+
71+ port := fmt .Sprint (rand .Intn (1000 ) + 9000 ) // random port
72+ server := & http.Server {Addr : "127.0.0.1:" + port }
73+ url := fmt .Sprintf ("http://localhost:%s" , port )
74+
75+ go func () {
76+ if err := server .ListenAndServe (); err != nil {
77+ fmt .Printf ("Failed to start server: %v\n " , err )
78+ }
79+ }()
80+ defer server .Shutdown (nil )
81+
82+ // Wait for the server to start
83+ for i := 0 ; i < 20 ; i ++ {
84+ if _ , err := http .Get (url ); err == nil {
85+ break
86+ }
87+
88+ if i == 19 {
89+ t .Error ("http server did not start" )
90+ return
91+ }
92+
93+ time .Sleep (50 * time .Millisecond )
94+ }
95+
2696 for _ , testCase := range []struct {
2797 name string
28- c httpGetter
2998 schemaPathTemplate string
3099 strict bool
31100 resourceKind , resourceAPIVersion , k8sversion string
32101 expect []byte
33102 expectErr error
34103 }{
35104 {
36- "error when downloading" ,
37- newMockHTTPGetter (func (url string ) (resp * http.Response , err error ) {
38- return nil , fmt .Errorf ("failed downloading from registry" )
39- }),
40- "http://kubernetesjson.dev" ,
105+ "retry connection reset by peer" ,
106+ fmt .Sprintf ("%s/simulate-reset" , url ),
41107 true ,
42108 "Deployment" ,
43109 "v1" ,
44110 "1.18.0" ,
111+ []byte (http .StatusText (http .StatusOK )),
45112 nil ,
46- fmt .Errorf ("failed downloading schema at http://kubernetesjson.dev: failed downloading from registry" ),
47113 },
48114 {
49115 "getting 404" ,
50- newMockHTTPGetter (func (url string ) (resp * http.Response , err error ) {
51- return & http.Response {
52- StatusCode : http .StatusNotFound ,
53- Body : io .NopCloser (strings .NewReader ("http response mock body" )),
54- }, nil
55- }),
56- "http://kubernetesjson.dev" ,
116+ fmt .Sprintf ("%s/404" , url ),
57117 true ,
58118 "Deployment" ,
59119 "v1" ,
60120 "1.18.0" ,
61121 nil ,
62- fmt .Errorf ("could not find schema at http://kubernetesjson.dev" ),
122+ fmt .Errorf ("could not find schema at %s/404" , url ),
63123 },
64124 {
65- "getting 503" ,
66- newMockHTTPGetter (func (url string ) (resp * http.Response , err error ) {
67- return & http.Response {
68- StatusCode : http .StatusServiceUnavailable ,
69- Body : io .NopCloser (strings .NewReader ("http response mock body" )),
70- }, nil
71- }),
72- "http://kubernetesjson.dev" ,
125+ "getting 500" ,
126+ fmt .Sprintf ("%s/500" , url ),
73127 true ,
74128 "Deployment" ,
75129 "v1" ,
76130 "1.18.0" ,
77131 nil ,
78- fmt .Errorf ("error while downloading schema at http://kubernetesjson.dev - received HTTP status 503" ),
132+ fmt .Errorf ("failed downloading schema at %s/500: Get \" %s/500\" : GET %s/500 giving up after 3 attempt(s)" , url , url , url ),
133+ },
134+ {
135+ "retry 503" ,
136+ fmt .Sprintf ("%s/503" , url ),
137+ true ,
138+ "Deployment" ,
139+ "v1" ,
140+ "1.18.0" ,
141+ []byte (http .StatusText (http .StatusOK )),
142+ nil ,
79143 },
80144 {
81145 "200" ,
82- newMockHTTPGetter (func (url string ) (resp * http.Response , err error ) {
83- return & http.Response {
84- StatusCode : http .StatusOK ,
85- Body : io .NopCloser (strings .NewReader ("http response mock body" )),
86- }, nil
87- }),
88- "http://kubernetesjson.dev" ,
146+ url ,
89147 true ,
90148 "Deployment" ,
91149 "v1" ,
92150 "1.18.0" ,
93- []byte (" http response mock body" ),
151+ []byte (http . StatusText ( http . StatusOK ) ),
94152 nil ,
95153 },
96154 } {
97- reg := SchemaRegistry {
98- c : testCase .c ,
99- schemaPathTemplate : testCase .schemaPathTemplate ,
100- strict : testCase .strict ,
155+ callCounts = map [string ]int {} // Reinitialise counters
156+
157+ reg , err := newHTTPRegistry (testCase .schemaPathTemplate , "" , testCase .strict , true , true )
158+ if err != nil {
159+ t .Errorf ("during test '%s': failed to create registry: %s" , testCase .name , err )
160+ continue
101161 }
102162
103163 _ , res , err := reg .DownloadSchema (testCase .resourceKind , testCase .resourceAPIVersion , testCase .k8sversion )
104164 if err == nil || testCase .expectErr == nil {
105- if err != testCase .expectErr {
106- t .Errorf ("during test '%s': expected error, got:\n %s\n %s\n " , testCase .name , testCase .expectErr , err )
165+ if err == nil && testCase .expectErr != nil {
166+ t .Errorf ("during test '%s': expected error\n %s, got nil" , testCase .name , testCase .expectErr )
167+ }
168+ if err != nil && testCase .expectErr == nil {
169+ t .Errorf ("during test '%s': expected no error, got\n %s\n " , testCase .name , err )
107170 }
108171 } else if err .Error () != testCase .expectErr .Error () {
109- t .Errorf ("during test '%s': expected error, got: \n %s\n %s\n " , testCase .name , testCase .expectErr , err )
172+ t .Errorf ("during test '%s': expected error\n %s, got: \n %s\n " , testCase .name , testCase .expectErr , err )
110173 }
111174
112175 if ! bytes .Equal (res , testCase .expect ) {
113- t .Errorf ("during test '%s': expected %s , got %s " , testCase .name , testCase .expect , res )
176+ t .Errorf ("during test '%s': expected '%s' , got '%s' " , testCase .name , testCase .expect , res )
114177 }
115178 }
116179
0 commit comments