diff --git a/e2e/internal/kubeclient/deploy.go b/e2e/internal/kubeclient/deploy.go new file mode 100644 index 0000000000..43faca997b --- /dev/null +++ b/e2e/internal/kubeclient/deploy.go @@ -0,0 +1,75 @@ +package kubeclient + +import ( + "context" + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/watch" +) + +// WaitForPod watches the given pod and blocks until it meets the condition Ready=True or the +// context expires (is cancelled or times out). +func (c *Kubeclient) WaitForPod(ctx context.Context, namespace, name string) error { + watcher, err := c.client.CoreV1().Pods(namespace).Watch(ctx, metav1.ListOptions{FieldSelector: "metadata.name=" + name}) + if err != nil { + return err + } + for { + select { + case evt := <-watcher.ResultChan(): + switch evt.Type { + case watch.Added: + fallthrough + case watch.Modified: + pod, ok := evt.Object.(*corev1.Pod) + if !ok { + return fmt.Errorf("watcher received unexpected type %T", evt.Object) + } + for _, cond := range pod.Status.Conditions { + if cond.Type == corev1.PodReady && cond.Status == corev1.ConditionTrue { + return nil + } + } + default: + return fmt.Errorf("unexpected watch event while waiting for pod %s/%s: %#v", namespace, name, evt.Object) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} + +// WaitForDeployment watches the given deployment and blocks until it meets the condition +// Available=True or the context expires (is cancelled or times out). +func (c *Kubeclient) WaitForDeployment(ctx context.Context, namespace, name string) error { + watcher, err := c.client.AppsV1().Deployments(namespace).Watch(ctx, metav1.ListOptions{FieldSelector: "metadata.name=" + name}) + if err != nil { + return err + } + for { + select { + case evt := <-watcher.ResultChan(): + switch evt.Type { + case watch.Added: + fallthrough + case watch.Modified: + pod, ok := evt.Object.(*appsv1.Deployment) + if !ok { + return fmt.Errorf("watcher received unexpected type %T", evt.Object) + } + for _, cond := range pod.Status.Conditions { + if cond.Type == appsv1.DeploymentAvailable && cond.Status == corev1.ConditionTrue { + return nil + } + } + default: + return fmt.Errorf("unexpected watch event while waiting for deployment %s/%s: %#v", namespace, name, evt.Object) + } + case <-ctx.Done(): + return ctx.Err() + } + } +} diff --git a/e2e/openssl/openssl_test.go b/e2e/openssl/openssl_test.go index 3c15606f70..aaff54572b 100644 --- a/e2e/openssl/openssl_test.go +++ b/e2e/openssl/openssl_test.go @@ -41,6 +41,8 @@ func TestFrontend2Backend(t *testing.T) { require.NoError(err) require.Len(frontendPods, 1, "pod not found: %s/%s", namespace, "openssl-frontend") + require.NoError(c.WaitForPod(ctx, namespace, frontendPods[0].Name)) + // Call the backend server from the frontend. If this command produces no TLS error, we verified that // - the certificate in the frontend pod can be used as a client certificate // - the certificate in the backend pod can be used as a server certificate @@ -63,10 +65,13 @@ func TestFrontend(t *testing.T) { certs := make(map[string][]byte) t.Run("contrast verify", func(t *testing.T) { + require := require.New(t) + ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute) defer cancel() - require := require.New(t) + require.NoError(c.WaitForDeployment(ctx, namespace, "coordinator")) + coordinator, cancelPortForward, err := c.PortForwardPod(ctx, namespace, "port-forwarder-coordinator", "1313") require.NoError(err) defer cancelPortForward()