diff --git a/test/gracefulshutdown/gracefulshutdown_test.go b/test/gracefulshutdown/gracefulshutdown_test.go index 9d4ba556b..1205ba2ac 100644 --- a/test/gracefulshutdown/gracefulshutdown_test.go +++ b/test/gracefulshutdown/gracefulshutdown_test.go @@ -27,6 +27,7 @@ import ( "testing" "time" + "golang.org/x/sync/errgroup" "gotest.tools/v3/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -97,45 +98,69 @@ func TestGracefulShutdown(t *testing.T) { }}, }) - // Prepare the request to the ingress: this request will wait for slightly less than the drain time configured - requestTimeout := drainTime - (3 * time.Second) - reqURL := fmt.Sprintf("http://%s.example.com?initialTimeout=%d", name, requestTimeout.Milliseconds()) - req, err := http.NewRequest("GET", reqURL, nil) - if err != nil { - t.Fatal("Error making GET request:", err) - } - - errs := make(chan error, 1) - var statusCode int - - // Do the request asynchronously: goroutine will return only once the service has returned a response (after initialTimeout), - // or early if there is an error - go func() { - resp, err := client.Do(req) - if err != nil { - errs <- err - return + tests := []struct { + name string + requestDuration time.Duration + }{ + { + name: fmt.Sprintf("do a request taking slightly less than the drain time: %s", drainTime), + requestDuration: drainTime - (3 * time.Second), + }, + { + name: fmt.Sprintf("do a request taking slightly more than the drain time: %s", drainTime), + requestDuration: drainTime + (3 * time.Second), } + } - statusCode = resp.StatusCode - resp.Body.Close() + g := new(errgroup.Group) + statusCodes := make(map[time.Duration]int, len(tests)) - errs <- nil - }() + // Run all requests asynchronously at the same time, and collect the results in statusCodes map + for _, test := range tests { + g.Go(func() error { + statusCode, err := sendRequest(name, test.requestDuration) + statusCodes[test.requestDuration] = statusCode + return err + }) + } - // In parallel, delete the gateway pod: - // the 1 second sleep before the deletion is here to ensure the request has been sent by the goroutine above + // Once requests are in-flight, delete the gateway pod: + // the 1 second sleep before the deletion is here to ensure the requests have been sent by the goroutines above time.Sleep(1 * time.Second) if err := clients.KubeClient.CoreV1().Pods(gatewayNs).Delete(ctx, gatewayPodName, metav1.DeleteOptions{}); err != nil { t.Fatalf("Failed to delete pod %s: %v", gatewayPodName, err) } - // Wait until we get a response from the asynchronous request - err = <-errs - if err != nil { + // Wait until we get responses from the asynchronous requests + if err := g.Wait(); err != nil { t.Fatal(err) } - // The gateway has been gracefully shutdown, so the in-flight request finishes with a OK status code - assert.Equal(t, statusCode, http.StatusOK) + // The gateway has been gracefully shutdown, so the in-flight requests taking less than the drain time will finish with a OK status code + // But, requests taking more than the drain time will be terminated, thus returning a non-OK status code + for timeout, statusCode := range statusCodes { + if timeout < drainTime { + assert.Equal(t, statusCode, http.StatusOK) + } else { + assert.Equal(t, statusCode, http.StatusOK) // TODO: get the right status code + } + } +} + +func sendRequest(name string, requestTimeout time.Duration) (statusCode int, err error) { + reqURL := fmt.Sprintf("http://%s.example.com?initialTimeout=%d", name, requestTimeout.Milliseconds()) + req, err := http.NewRequest("GET", reqURL, nil) + if err != nil { + t.Fatal("Error making GET request:", err) + } + + resp, err := client.Do(req) + if err != nil { + return 0, err + } + + statusCode = resp.StatusCode + resp.Body.Close() + + return statusCode, nil }