m/test/e2e: test NodePort

This was originally written as a test for validating fixes for
the issue that NodePort was not working if any non-local pods were in
the NodePort service, even for externalTrafficPolicy: cluster services.
As it turns out CL:2795 fixed this, the changes in previous versions of
this CL broke it again. So now it just consists of the test itself,
which passes.

Change-Id: If4cf4ffc46a5456b4defa330776e043593e61b29
Reviewed-on: https://review.monogon.dev/c/monogon/+/1924
Tested-by: Jenkins CI
Reviewed-by: Tim Windelschmidt <tim@monogon.tech>
diff --git a/metropolis/test/e2e/main_test.go b/metropolis/test/e2e/main_test.go
index 2409aed..8617da4 100644
--- a/metropolis/test/e2e/main_test.go
+++ b/metropolis/test/e2e/main_test.go
@@ -36,6 +36,7 @@
 	"github.com/bazelbuild/rules_go/go/runfiles"
 	"google.golang.org/grpc"
 	corev1 "k8s.io/api/core/v1"
+	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/api/resource"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	podv1 "k8s.io/kubernetes/pkg/api/v1/pod"
@@ -393,6 +394,48 @@
 		}
 		return fmt.Errorf("job still running")
 	})
+	util.TestEventual(t, "Start NodePort test setup", ctx, smallTestTimeout, func(ctx context.Context) error {
+		_, err := clientSet.AppsV1().Deployments("default").Create(ctx, makeHTTPServerDeploymentSpec("nodeport-server"), metav1.CreateOptions{})
+		if err != nil && !kerrors.IsAlreadyExists(err) {
+			return err
+		}
+		_, err = clientSet.CoreV1().Services("default").Create(ctx, makeHTTPServerNodePortService("nodeport-server"), metav1.CreateOptions{})
+		if err != nil && !kerrors.IsAlreadyExists(err) {
+			return err
+		}
+		return nil
+	})
+	util.TestEventual(t, "NodePort accessible from all nodes", ctx, smallTestTimeout, func(ctx context.Context) error {
+		nodes, err := clientSet.CoreV1().Nodes().List(ctx, metav1.ListOptions{})
+		if err != nil {
+			return err
+		}
+		// Use a new client for each attempt
+		hc := http.Client{
+			Timeout: 2 * time.Second,
+			Transport: &http.Transport{
+				Dial: cluster.SOCKSDialer.Dial,
+			},
+		}
+		for _, n := range nodes.Items {
+			var addr string
+			for _, a := range n.Status.Addresses {
+				if a.Type == corev1.NodeInternalIP {
+					addr = a.Address
+				}
+			}
+			u := url.URL{Scheme: "http", Host: addr, Path: "/"}
+			res, err := hc.Get(u.String())
+			if err != nil {
+				return fmt.Errorf("failed getting from node %q: %w", n.Name, err)
+			}
+			if res.StatusCode != http.StatusOK {
+				return fmt.Errorf("getting from node %q: HTTP %d", n.Name, res.StatusCode)
+			}
+			t.Logf("Got response from %q", n.Name)
+		}
+		return nil
+	})
 	if os.Getenv("HAVE_NESTED_KVM") != "" {
 		util.TestEventual(t, "Pod for KVM/QEMU smoke test", ctx, smallTestTimeout, func(ctx context.Context) error {
 			runcRuntimeClass := "runc"