@@ -160,6 +160,12 @@ type NetworkingTestConfig struct {
160
160
Namespace string
161
161
}
162
162
163
+ // NetexecDialResponse represents the response returned by the `netexec` subcommand of `agnhost`
164
+ type NetexecDialResponse struct {
165
+ Responses []string `json:"responses"`
166
+ Errors []string `json:"errors"`
167
+ }
168
+
163
169
// DialFromEndpointContainer executes a curl via kubectl exec in an endpoint container.
164
170
func (config * NetworkingTestConfig ) DialFromEndpointContainer (protocol , targetIP string , targetPort , maxTries , minTries int , expectedEps sets.String ) {
165
171
config .DialFromContainer (protocol , echoHostname , config .EndpointPods [0 ].Status .PodIP , targetIP , EndpointHTTPPort , targetPort , maxTries , minTries , expectedEps )
@@ -212,6 +218,18 @@ func (config *NetworkingTestConfig) EndpointHostnames() sets.String {
212
218
return expectedEps
213
219
}
214
220
221
+ func makeCURLDialCommand (ipPort , dialCmd , protocol , targetIP string , targetPort int ) string {
222
+ // The current versions of curl included in CentOS and RHEL distros
223
+ // misinterpret square brackets around IPv6 as globbing, so use the -g
224
+ // argument to disable globbing to handle the IPv6 case.
225
+ return fmt .Sprintf ("curl -g -q -s 'http://%s/dial?request=%s&protocol=%s&host=%s&port=%d&tries=1'" ,
226
+ ipPort ,
227
+ dialCmd ,
228
+ protocol ,
229
+ targetIP ,
230
+ targetPort )
231
+ }
232
+
215
233
// DialFromContainer executes a curl via kubectl exec in a test container,
216
234
// which might then translate to a tcp or udp request based on the protocol
217
235
// argument in the url.
@@ -232,38 +250,23 @@ func (config *NetworkingTestConfig) EndpointHostnames() sets.String {
232
250
// pod and confirm it doesn't show up as an endpoint.
233
251
func (config * NetworkingTestConfig ) DialFromContainer (protocol , dialCommand , containerIP , targetIP string , containerHTTPPort , targetPort , maxTries , minTries int , expectedResponses sets.String ) {
234
252
ipPort := net .JoinHostPort (containerIP , strconv .Itoa (containerHTTPPort ))
235
- // The current versions of curl included in CentOS and RHEL distros
236
- // misinterpret square brackets around IPv6 as globbing, so use the -g
237
- // argument to disable globbing to handle the IPv6 case.
238
- cmd := fmt .Sprintf ("curl -g -q -s 'http://%s/dial?request=%s&protocol=%s&host=%s&port=%d&tries=1'" ,
239
- ipPort ,
240
- dialCommand ,
241
- protocol ,
242
- targetIP ,
243
- targetPort )
253
+ cmd := makeCURLDialCommand (ipPort , dialCommand , protocol , targetIP , targetPort )
244
254
245
255
responses := sets .NewString ()
246
256
247
257
for i := 0 ; i < maxTries ; i ++ {
248
- stdout , stderr , err := config .f . ExecShellInPodWithFullOutput ( config . TestContainerPod . Name , cmd )
258
+ resp , err := config .GetResponseFromContainer ( protocol , dialCommand , containerIP , targetIP , containerHTTPPort , targetPort )
249
259
if err != nil {
250
260
// A failure to kubectl exec counts as a try, not a hard fail.
251
261
// Also note that we will keep failing for maxTries in tests where
252
262
// we confirm unreachability.
253
- framework .Logf ("Failed to execute %q: %v, stdout: %q, stderr %q" , cmd , err , stdout , stderr )
254
- } else {
255
- var output map [string ][]string
256
- if err := json .Unmarshal ([]byte (stdout ), & output ); err != nil {
257
- framework .Logf ("WARNING: Failed to unmarshal curl response. Cmd %v run in %v, output: %s, err: %v" ,
258
- cmd , config .TestContainerPod .Name , stdout , err )
259
- continue
260
- }
261
-
262
- for _ , response := range output ["responses" ] {
263
- trimmed := strings .TrimSpace (response )
264
- if trimmed != "" {
265
- responses .Insert (trimmed )
266
- }
263
+ framework .Logf ("GetResponseFromContainer: %s" , err )
264
+ continue
265
+ }
266
+ for _ , response := range resp .Responses {
267
+ trimmed := strings .TrimSpace (response )
268
+ if trimmed != "" {
269
+ responses .Insert (trimmed )
267
270
}
268
271
}
269
272
framework .Logf ("Waiting for responses: %v" , expectedResponses .Difference (responses ))
@@ -294,14 +297,7 @@ func (config *NetworkingTestConfig) GetEndpointsFromTestContainer(protocol, targ
294
297
// we don't see any endpoints, the test fails.
295
298
func (config * NetworkingTestConfig ) GetEndpointsFromContainer (protocol , containerIP , targetIP string , containerHTTPPort , targetPort , tries int ) (sets.String , error ) {
296
299
ipPort := net .JoinHostPort (containerIP , strconv .Itoa (containerHTTPPort ))
297
- // The current versions of curl included in CentOS and RHEL distros
298
- // misinterpret square brackets around IPv6 as globbing, so use the -g
299
- // argument to disable globbing to handle the IPv6 case.
300
- cmd := fmt .Sprintf ("curl -g -q -s 'http://%s/dial?request=hostName&protocol=%s&host=%s&port=%d&tries=1'" ,
301
- ipPort ,
302
- protocol ,
303
- targetIP ,
304
- targetPort )
300
+ cmd := makeCURLDialCommand (ipPort , "hostName" , protocol , targetIP , targetPort )
305
301
306
302
eps := sets .NewString ()
307
303
@@ -314,14 +310,14 @@ func (config *NetworkingTestConfig) GetEndpointsFromContainer(protocol, containe
314
310
framework .Logf ("Failed to execute %q: %v, stdout: %q, stderr: %q" , cmd , err , stdout , stderr )
315
311
} else {
316
312
framework .Logf ("Tries: %d, in try: %d, stdout: %v, stderr: %v, command run in: %#v" , tries , i , stdout , stderr , config .TestContainerPod )
317
- var output map [ string ][] string
313
+ var output NetexecDialResponse
318
314
if err := json .Unmarshal ([]byte (stdout ), & output ); err != nil {
319
315
framework .Logf ("WARNING: Failed to unmarshal curl response. Cmd %v run in %v, output: %s, err: %v" ,
320
316
cmd , config .TestContainerPod .Name , stdout , err )
321
317
continue
322
318
}
323
319
324
- for _ , hostName := range output [ "responses" ] {
320
+ for _ , hostName := range output . Responses {
325
321
trimmed := strings .TrimSpace (hostName )
326
322
if trimmed != "" {
327
323
eps .Insert (trimmed )
@@ -334,6 +330,50 @@ func (config *NetworkingTestConfig) GetEndpointsFromContainer(protocol, containe
334
330
return eps , nil
335
331
}
336
332
333
+ // GetResponseFromContainer executes a curl via kubectl exec in a container.
334
+ func (config * NetworkingTestConfig ) GetResponseFromContainer (protocol , dialCommand , containerIP , targetIP string , containerHTTPPort , targetPort int ) (NetexecDialResponse , error ) {
335
+ ipPort := net .JoinHostPort (containerIP , strconv .Itoa (containerHTTPPort ))
336
+ cmd := makeCURLDialCommand (ipPort , dialCommand , protocol , targetIP , targetPort )
337
+
338
+ stdout , stderr , err := config .f .ExecShellInPodWithFullOutput (config .TestContainerPod .Name , cmd )
339
+ if err != nil {
340
+ return NetexecDialResponse {}, fmt .Errorf ("failed to execute %q: %v, stdout: %q, stderr: %q" , cmd , err , stdout , stderr )
341
+ }
342
+
343
+ var output NetexecDialResponse
344
+ if err := json .Unmarshal ([]byte (stdout ), & output ); err != nil {
345
+ return NetexecDialResponse {}, fmt .Errorf ("failed to unmarshal curl response. Cmd %v run in %v, output: %s, err: %v" ,
346
+ cmd , config .TestContainerPod .Name , stdout , err )
347
+ }
348
+ return output , nil
349
+ }
350
+
351
+ // GetResponseFromTestContainer executes a curl via kubectl exec in a test container.
352
+ func (config * NetworkingTestConfig ) GetResponseFromTestContainer (protocol , dialCommand , targetIP string , targetPort int ) (NetexecDialResponse , error ) {
353
+ return config .GetResponseFromContainer (protocol , dialCommand , config .TestContainerPod .Status .PodIP , targetIP , testContainerHTTPPort , targetPort )
354
+ }
355
+
356
+ // GetHTTPCodeFromTestContainer executes a curl via kubectl exec in a test container and returns the status code.
357
+ func (config * NetworkingTestConfig ) GetHTTPCodeFromTestContainer (path , targetIP string , targetPort int ) (int , error ) {
358
+ cmd := fmt .Sprintf ("curl -g -q -s -o /dev/null -w %%{http_code} http://%s:%d%s" ,
359
+ targetIP ,
360
+ targetPort ,
361
+ path )
362
+ stdout , stderr , err := config .f .ExecShellInPodWithFullOutput (config .TestContainerPod .Name , cmd )
363
+ // We only care about the status code reported by curl,
364
+ // and want to return any other errors, such as cannot execute command in the Pod.
365
+ // If curl failed to connect to host, it would exit with code 7, which makes `ExecShellInPodWithFullOutput`
366
+ // return a non-nil error and output "000" to stdout.
367
+ if err != nil && len (stdout ) == 0 {
368
+ return 0 , fmt .Errorf ("failed to execute %q: %v, stderr: %q" , cmd , err , stderr )
369
+ }
370
+ code , err := strconv .Atoi (stdout )
371
+ if err != nil {
372
+ return 0 , fmt .Errorf ("failed to parse status code returned by healthz endpoint: %w, code: %s" , err , stdout )
373
+ }
374
+ return code , nil
375
+ }
376
+
337
377
// DialFromNode executes a tcp or udp request based on protocol via kubectl exec
338
378
// in a test container running with host networking.
339
379
// - minTries is the minimum number of curl attempts required before declaring
0 commit comments