diff --git a/changelog/v1.19.0-beta3/fix-flakey-TestConfigureNotAttachedHttpListenerOptions.yaml b/changelog/v1.19.0-beta3/fix-flakey-TestConfigureNotAttachedHttpListenerOptions.yaml new file mode 100644 index 00000000000..6b358150fb1 --- /dev/null +++ b/changelog/v1.19.0-beta3/fix-flakey-TestConfigureNotAttachedHttpListenerOptions.yaml @@ -0,0 +1,4 @@ +changelog: +- type: NON_USER_FACING + issueLink: https://github.com/solo-io/solo-projects/issues/6617 + description: Fix flakey TestConfigureNotAttachedHttpListenerOptions caused by the gateway-proxy pod being deployed after the test begins leading to a timeout. diff --git a/test/gomega/matchers/pod.go b/test/gomega/matchers/pod.go index 12f95dfeb29..bf15cac6d9f 100644 --- a/test/gomega/matchers/pod.go +++ b/test/gomega/matchers/pod.go @@ -16,6 +16,9 @@ type ExpectedPod struct { // Status is the pod phase status (e.g. Running, Pending, Succeeded, Failed). Optional. Status corev1.PodPhase + // Ready indicates that the Pod is able to serve requests. Optional. + Ready bool + // TODO(npolshak): Add more fields to match on as needed } @@ -41,28 +44,47 @@ func (pm *podMatcher) Match(actual interface{}) (bool, error) { } } if !foundContainer { - log.Printf("expected pod to have container '%s', but it was not found", pm.expectedPod.ContainerName) + log.Printf("expected pod %s to have container '%s', but it was not found", pod.Name, pm.expectedPod.ContainerName) return false, nil } } if pm.expectedPod.Status != "" { if pod.Status.Phase != pm.expectedPod.Status { - log.Printf("expected pod to have status %s, but it was %s", pm.expectedPod.Status, pod.Status.Phase) + log.Printf("expected pod %s to have status %s, but it was %s", pod.Name, pm.expectedPod.Status, pod.Status.Phase) return false, nil } } + if pm.expectedPod.Ready { + for _, condition := range pod.Status.Conditions { + if condition.Type == corev1.PodReady { + ready := condition.Status == corev1.ConditionTrue + if !ready { + log.Printf("expected pod %s to have condition ready, but it was not ready", pod.Name) + } + return ready, nil + } + } + log.Printf("expected pod %s to have condition ready, but it was not found", pod.Name) + return false, nil + } + return true, nil } func (pm *podMatcher) FailureMessage(actual interface{}) string { var errorMsg string + pod, ok := actual.(corev1.Pod) + if !ok { + return fmt.Sprintf("expected a pod, got %T", actual) + } + if pm.expectedPod.ContainerName != "" { - errorMsg += fmt.Sprintf("Expected pod to have container '%s', but it was not found", pm.expectedPod.ContainerName) + errorMsg += fmt.Sprintf("Expected pod %s to have container '%s', but it was not found", pod.Name, pm.expectedPod.ContainerName) } if pm.expectedPod.Status != "" { - errorMsg += fmt.Sprintf("Expected pod to have status '%s', but it was not found", pm.expectedPod.Status) + errorMsg += fmt.Sprintf("Expected pod %s to have status '%s', but it was not found", pod.Name, pm.expectedPod.Status) } return errorMsg } @@ -76,10 +98,10 @@ func (pm *podMatcher) NegatedFailureMessage(actual interface{}) string { for _, container := range pod.Spec.Containers { containers += container.Name + ", " } - errorMsg += fmt.Sprintf("Expected pod to have container '%s', but it found %s", pm.expectedPod.ContainerName, containers) + errorMsg += fmt.Sprintf("Expected pod %s to have container '%s', but it found %s", pod.Name, pm.expectedPod.ContainerName, containers) } if pm.expectedPod.Status != "" { - errorMsg += fmt.Sprintf("Expected pod to have status '%s', but it found %s", pm.expectedPod.Status, pod.Status.Phase) + errorMsg += fmt.Sprintf("Expected pod %s to have status '%s', but it found %s", pod.Name, pm.expectedPod.Status, pod.Status.Phase) } return errorMsg } diff --git a/test/kubernetes/e2e/features/http_listener_options/http_lis_opt_suite.go b/test/kubernetes/e2e/features/http_listener_options/http_lis_opt_suite.go index e0f5177ae90..2e08b17927d 100644 --- a/test/kubernetes/e2e/features/http_listener_options/http_lis_opt_suite.go +++ b/test/kubernetes/e2e/features/http_listener_options/http_lis_opt_suite.go @@ -45,6 +45,9 @@ func (s *testingSuite) SetupSuite() { s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, nginxPod.ObjectMeta.GetNamespace(), metav1.ListOptions{ LabelSelector: "app.kubernetes.io/name=nginx", }) + s.testInstallation.Assertions.EventuallyPodsRunning(s.ctx, testdefaults.CurlPod.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app=curl", + }) // include gateway manifests for the tests, so we recreate it for each test run s.manifests = map[string][]string{ @@ -88,6 +91,10 @@ func (s *testingSuite) AfterTest(suiteName, testName string) { output, err := s.testInstallation.Actions.Kubectl().DeleteFileWithOutput(s.ctx, manifest) s.testInstallation.Assertions.ExpectObjectDeleted(manifest, err, output) } + s.testInstallation.Assertions.EventuallyObjectsNotExist(s.ctx, proxyService, proxyDeployment) + s.testInstallation.Assertions.EventuallyPodsNotExist(s.ctx, proxyDeployment.ObjectMeta.GetNamespace(), metav1.ListOptions{ + LabelSelector: "app.kubernetes.io/name=gloo-proxy-gw", + }) } func (s *testingSuite) TestConfigureHttpListenerOptions() { diff --git a/test/kubernetes/testutils/assertions/pods.go b/test/kubernetes/testutils/assertions/pods.go index a39f8f99ff6..e578a0a1615 100644 --- a/test/kubernetes/testutils/assertions/pods.go +++ b/test/kubernetes/testutils/assertions/pods.go @@ -13,14 +13,14 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EventuallyPodsRunning asserts that the pod(s) are in the ready state +// EventuallyPodsRunning asserts that the pod(s) are running and ready. func (p *Provider) EventuallyPodsRunning( ctx context.Context, podNamespace string, listOpt metav1.ListOptions, timeout ...time.Duration, ) { - p.EventuallyPodsMatches(ctx, podNamespace, listOpt, matchers.PodMatches(matchers.ExpectedPod{Status: corev1.PodRunning}), timeout...) + p.EventuallyPodsMatches(ctx, podNamespace, listOpt, matchers.PodMatches(matchers.ExpectedPod{Status: corev1.PodRunning, Ready: true}), timeout...) } // EventuallyPodsMatches asserts that the pod(s) in the given namespace matches the provided matcher @@ -45,3 +45,22 @@ func (p *Provider) EventuallyPodsMatches( WithPolling(pollingInterval). Should(gomega.Succeed(), fmt.Sprintf("Failed to match pod in namespace %s", podNamespace)) } + +// EventuallyPodsNotExist asserts that the pod(s) are no longer present +func (p *Provider) EventuallyPodsNotExist( + ctx context.Context, + podNamespace string, + listOpt metav1.ListOptions, + timeout ...time.Duration, +) { + currentTimeout, pollingInterval := helper.GetTimeouts(timeout...) + + p.Gomega.Eventually(func(g gomega.Gomega) { + pods, err := p.clusterContext.Clientset.CoreV1().Pods(podNamespace).List(ctx, listOpt) + g.Expect(err).NotTo(gomega.HaveOccurred(), "Failed to list pods") + g.Expect(pods.Items).To(gomega.BeEmpty(), "Pods found") + }). + WithTimeout(currentTimeout). + WithPolling(pollingInterval). + Should(gomega.Succeed(), fmt.Sprintf("Failed to match pod in namespace %s", podNamespace)) +}