Skip to content

Commit 610ab12

Browse files
committed
Add polling wait functions to replace sleep workarounds
1 parent 0c27865 commit 610ab12

File tree

1 file changed

+111
-57
lines changed

1 file changed

+111
-57
lines changed

tests/suite/graceful_recovery_test.go

Lines changed: 111 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,33 @@ package suite
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67
"os/exec"
8+
"strings"
79
"time"
810

911
. "github.com/onsi/ginkgo/v2"
1012
. "github.com/onsi/gomega"
1113
core "k8s.io/api/core/v1"
1214
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/apimachinery/pkg/util/wait"
1417
"sigs.k8s.io/controller-runtime/pkg/client"
1518

1619
"github.com/nginxinc/nginx-gateway-fabric/tests/framework"
1720
)
1821

22+
const (
23+
// FIXME(bjee19): Find an automated way to keep the version updated here similar to dependabot.
24+
// https://github.com/nginxinc/nginx-gateway-fabric/issues/1665
25+
debugImage = "busybox:1.28"
26+
teaURL = "https://cafe.example.com/tea"
27+
coffeeURL = "http://cafe.example.com/coffee"
28+
nginxContainerName = "nginx"
29+
ngfContainerName = "nginx-gateway"
30+
)
31+
1932
var _ = Describe("Graceful Recovery test", Ordered, Label("nfr", "graceful-recovery"), func() {
2033
files := []string{
2134
"graceful-recovery/cafe.yaml",
@@ -29,11 +42,6 @@ var _ = Describe("Graceful Recovery test", Ordered, Label("nfr", "graceful-recov
2942
},
3043
}
3144

32-
nginxContainerName := "nginx"
33-
ngfContainerName := "nginx-gateway"
34-
teaURL := "https://cafe.example.com/tea"
35-
coffeeURL := "http://cafe.example.com/coffee"
36-
3745
BeforeAll(func() {
3846
cfg := getDefaultSetupCfg()
3947
cfg.nfr = true
@@ -44,10 +52,9 @@ var _ = Describe("Graceful Recovery test", Ordered, Label("nfr", "graceful-recov
4452
Expect(resourceManager.Apply([]client.Object{ns})).To(Succeed())
4553
Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
4654
Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())
47-
// Sometimes the traffic would error with code 502, after implementing this sleep it stopped.
48-
time.Sleep(2 * time.Second)
4955

50-
expectWorkingTraffic(teaURL, coffeeURL)
56+
err := waitForWorkingTraffic()
57+
Expect(err).ToNot(HaveOccurred())
5158
})
5259

5360
AfterAll(func() {
@@ -63,21 +70,22 @@ var _ = Describe("Graceful Recovery test", Ordered, Label("nfr", "graceful-recov
6370
output, err := restartNGFProcess(ngfContainerName)
6471
Expect(err).ToNot(HaveOccurred(), string(output))
6572

66-
expectWorkingTraffic(teaURL, coffeeURL)
67-
6873
checkContainerLogsForErrors(podNames[0])
6974

75+
err = waitForWorkingTraffic()
76+
Expect(err).ToNot(HaveOccurred())
77+
7078
// I tried just deleting the routes and ran into a bunch of issues, deleting all the files was better
7179
Expect(resourceManager.DeleteFromFiles(files, ns.Name)).To(Succeed())
72-
// Wait for files to be deleted.
73-
time.Sleep(2 * time.Second)
7480

75-
expectFailingTraffic(teaURL, coffeeURL)
81+
err = waitForFailingTraffic()
82+
Expect(err).ToNot(HaveOccurred())
7683

7784
Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
7885
Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())
7986

80-
expectWorkingTraffic(teaURL, coffeeURL)
87+
err = waitForWorkingTraffic()
88+
Expect(err).ToNot(HaveOccurred())
8189
})
8290

8391
It("recovers when nginx container is restarted", func() {
@@ -90,16 +98,19 @@ var _ = Describe("Graceful Recovery test", Ordered, Label("nfr", "graceful-recov
9098

9199
checkContainerLogsForErrors(podNames[0])
92100

101+
err = waitForWorkingTraffic()
102+
Expect(err).ToNot(HaveOccurred())
103+
93104
Expect(resourceManager.DeleteFromFiles(files, ns.Name)).To(Succeed())
94-
// Wait for files to be deleted.
95-
time.Sleep(2 * time.Second)
96105

97-
expectFailingTraffic(teaURL, coffeeURL)
106+
err = waitForFailingTraffic()
107+
Expect(err).ToNot(HaveOccurred())
98108

99109
Expect(resourceManager.ApplyFromFiles(files, ns.Name)).To(Succeed())
100110
Expect(resourceManager.WaitForAppsToBeReady(ns.Name)).To(Succeed())
101111

102-
expectWorkingTraffic(teaURL, coffeeURL)
112+
err = waitForWorkingTraffic()
113+
Expect(err).ToNot(HaveOccurred())
103114
})
104115
})
105116

@@ -138,17 +149,9 @@ func restartNginxContainer(nginxContainerName string) ([]byte, error) {
138149
return output, err
139150
}
140151

141-
// Wait for NGF to restart.
142-
time.Sleep(2 * time.Second)
143-
144-
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ngfNamespace, Name: podNames[0]}, &ngfPod)
152+
err = waitForContainerRestart(podNames[0], nginxContainerName, restartCount)
145153
Expect(err).ToNot(HaveOccurred())
146154

147-
for _, containerStatus := range ngfPod.Status.ContainerStatuses {
148-
if containerStatus.Name == nginxContainerName {
149-
Expect(int(containerStatus.RestartCount)).To(Equal(restartCount + 1))
150-
}
151-
}
152155
return nil, nil
153156
}
154157

@@ -177,7 +180,7 @@ func restartNGFProcess(ngfContainerName string) ([]byte, error) {
177180
"-n",
178181
ngfNamespace,
179182
podNames[0],
180-
"--image=busybox:1.28",
183+
"--image="+debugImage,
181184
"--target=nginx-gateway",
182185
"--",
183186
"sh",
@@ -187,58 +190,109 @@ func restartNGFProcess(ngfContainerName string) ([]byte, error) {
187190
return output, err
188191
}
189192

190-
// Wait for NGF to restart.
191-
time.Sleep(6 * time.Second)
192-
193-
err = k8sClient.Get(ctx, types.NamespacedName{Namespace: ngfNamespace, Name: podNames[0]}, &ngfPod)
193+
err = waitForContainerRestart(podNames[0], ngfContainerName, restartCount)
194194
Expect(err).ToNot(HaveOccurred())
195195

196-
for _, containerStatus := range ngfPod.Status.ContainerStatuses {
197-
if containerStatus.Name == ngfContainerName {
198-
Expect(int(containerStatus.RestartCount)).To(Equal(restartCount + 1))
199-
}
200-
}
201196
return nil, nil
202197
}
203198

204-
func expectWorkingTraffic(teaURL, coffeeURL string) {
205-
status, body, err := framework.Get(teaURL, address, timeoutConfig.RequestTimeout)
206-
Expect(err).ToNot(HaveOccurred())
207-
Expect(status).To(Equal(http.StatusOK))
208-
Expect(body).To(ContainSubstring("URI: /tea"))
199+
func waitForContainerRestart(ngfPodName string, containerName string, currentRestartCount int) error {
200+
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.RequestTimeout)
201+
defer cancel()
209202

210-
status, body, err = framework.Get(coffeeURL, address, timeoutConfig.RequestTimeout)
211-
Expect(err).ToNot(HaveOccurred())
212-
Expect(status).To(Equal(http.StatusOK), coffeeURL+" "+address)
213-
Expect(body).To(ContainSubstring("URI: /coffee"))
203+
//nolint:nilerr
204+
return wait.PollUntilContextCancel(
205+
ctx,
206+
500*time.Millisecond,
207+
true, /* poll immediately */
208+
func(ctx context.Context) (bool, error) {
209+
var ngfPod core.Pod
210+
if err := k8sClient.Get(ctx, types.NamespacedName{Namespace: ngfNamespace, Name: ngfPodName}, &ngfPod); err != nil {
211+
return false, nil
212+
}
213+
214+
for _, containerStatus := range ngfPod.Status.ContainerStatuses {
215+
if containerStatus.Name == containerName {
216+
return int(containerStatus.RestartCount) == currentRestartCount+1, nil
217+
}
218+
}
219+
return false, nil
220+
},
221+
)
222+
}
223+
224+
func waitForWorkingTraffic() error {
225+
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.RequestTimeout)
226+
defer cancel()
227+
228+
//nolint:nilerr
229+
return wait.PollUntilContextCancel(
230+
ctx,
231+
500*time.Millisecond,
232+
true, /* poll immediately */
233+
func(_ context.Context) (bool, error) {
234+
if err := expectRequest(teaURL, address, http.StatusOK, "URI: /tea"); err != nil {
235+
return false, nil
236+
}
237+
if err := expectRequest(coffeeURL, address, http.StatusOK, "URI: /coffee"); err != nil {
238+
return false, nil
239+
}
240+
return true, nil
241+
},
242+
)
214243
}
215244

216-
func expectFailingTraffic(teaURL, coffeeURL string) {
217-
status, body, err := framework.Get(teaURL, address, timeoutConfig.RequestTimeout)
218-
Expect(err).To(HaveOccurred())
219-
Expect(status).ToNot(Equal(http.StatusOK))
220-
Expect(body).ToNot(ContainSubstring("URI: /tea"))
245+
func waitForFailingTraffic() error {
246+
ctx, cancel := context.WithTimeout(context.Background(), timeoutConfig.RequestTimeout)
247+
defer cancel()
221248

222-
status, body, err = framework.Get(coffeeURL, address, timeoutConfig.RequestTimeout)
223-
Expect(err).To(HaveOccurred())
224-
Expect(status).ToNot(Equal(http.StatusOK))
225-
Expect(body).ToNot(ContainSubstring("URI: /coffee"))
249+
return wait.PollUntilContextCancel(
250+
ctx,
251+
500*time.Millisecond,
252+
true, /* poll immediately */
253+
func(_ context.Context) (bool, error) {
254+
if err := expectRequest(teaURL, address, 0, "URI: /tea"); err == nil {
255+
return false, nil
256+
}
257+
if err := expectRequest(coffeeURL, address, 0, "URI: /coffee"); err == nil {
258+
return false, nil
259+
}
260+
return true, nil
261+
},
262+
)
263+
}
264+
265+
func expectRequest(appURL string, address string, httpStatus int, responseBodyMessage string) error {
266+
status, body, err := framework.Get(appURL, address, timeoutConfig.RequestTimeout)
267+
if status != httpStatus {
268+
return errors.New("http statuses were not equal")
269+
}
270+
if httpStatus == http.StatusOK {
271+
if !strings.Contains(body, responseBodyMessage) {
272+
return errors.New("expected response body to contain body message")
273+
}
274+
} else {
275+
if strings.Contains(body, responseBodyMessage) {
276+
return errors.New("expected response body to not contain body message")
277+
}
278+
}
279+
return err
226280
}
227281

228282
func checkContainerLogsForErrors(ngfPodName string) {
229283
sinceSeconds := int64(15)
230284
logs, err := resourceManager.GetPodLogs(
231285
ngfNamespace,
232286
ngfPodName,
233-
&core.PodLogOptions{Container: "nginx", SinceSeconds: &sinceSeconds},
287+
&core.PodLogOptions{Container: nginxContainerName, SinceSeconds: &sinceSeconds},
234288
)
235289
Expect(err).ToNot(HaveOccurred())
236290
Expect(logs).ToNot(ContainSubstring("emerg"), logs)
237291

238292
logs, err = resourceManager.GetPodLogs(
239293
ngfNamespace,
240294
ngfPodName,
241-
&core.PodLogOptions{Container: "nginx-gateway", SinceSeconds: &sinceSeconds},
295+
&core.PodLogOptions{Container: ngfContainerName, SinceSeconds: &sinceSeconds},
242296
)
243297
Expect(err).ToNot(HaveOccurred())
244298
Expect(logs).ToNot(ContainSubstring("error"), logs)

0 commit comments

Comments
 (0)