621f71981c
The e2e ingress test was very occasionally flaky. On looking at operator logs from one failure, you can see the default ProxyClass was not ready before the first reconcile loop for the exposed Service. The ProxyClass became ready soon after, but no additional reconciles were triggered for the exposed Service because we only triggered reconciles for Services that explicitly named their ProxyClass. This change adds additional list API calls for when it's the default ProxyClass that's been updated in order to catch Services that use it by default. It also adds indexes for the fields we need to search on to ensure the list is efficient. Fixes tailscale/corp#37533 Signed-off-by: Tom Proctor <tomhjp@users.noreply.github.com>
124 lines
3.0 KiB
Go
124 lines
3.0 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
appsv1 "k8s.io/api/apps/v1"
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"tailscale.com/cmd/testwrapper/flakytest"
|
|
kube "tailscale.com/k8s-operator"
|
|
"tailscale.com/tstest"
|
|
"tailscale.com/util/httpm"
|
|
)
|
|
|
|
// See [TestMain] for test requirements.
|
|
func TestIngress(t *testing.T) {
|
|
flakytest.Mark(t, "https://github.com/tailscale/corp/issues/37533")
|
|
if tnClient == nil {
|
|
t.Skip("TestIngress requires a working tailnet client")
|
|
}
|
|
|
|
// Apply nginx
|
|
createAndCleanup(t, kubeClient,
|
|
&appsv1.Deployment{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "nginx",
|
|
Namespace: "default",
|
|
Labels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Spec: appsv1.DeploymentSpec{
|
|
Replicas: new(int32(1)),
|
|
Selector: &metav1.LabelSelector{
|
|
MatchLabels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Template: corev1.PodTemplateSpec{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Labels: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
},
|
|
Spec: corev1.PodSpec{
|
|
Containers: []corev1.Container{
|
|
{
|
|
Name: "nginx",
|
|
Image: "nginx",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
// Apply service to expose it as ingress
|
|
svc := &corev1.Service{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: "test-ingress",
|
|
Namespace: "default",
|
|
Annotations: map[string]string{
|
|
"tailscale.com/expose": "true",
|
|
},
|
|
},
|
|
Spec: corev1.ServiceSpec{
|
|
Selector: map[string]string{
|
|
"app.kubernetes.io/name": "nginx",
|
|
},
|
|
Ports: []corev1.ServicePort{
|
|
{
|
|
Name: "http",
|
|
Protocol: "TCP",
|
|
Port: 80,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
createAndCleanup(t, kubeClient, svc)
|
|
|
|
if err := tstest.WaitFor(time.Minute, func() error {
|
|
maybeReadySvc := &corev1.Service{ObjectMeta: objectMeta("default", "test-ingress")}
|
|
if err := get(t.Context(), kubeClient, maybeReadySvc); err != nil {
|
|
return err
|
|
}
|
|
isReady := kube.SvcIsReady(maybeReadySvc)
|
|
if isReady {
|
|
t.Log("Service is ready")
|
|
return nil
|
|
}
|
|
return fmt.Errorf("Service is not ready yet")
|
|
}); err != nil {
|
|
t.Fatalf("error waiting for the Service to become Ready: %v", err)
|
|
}
|
|
|
|
var resp *http.Response
|
|
if err := tstest.WaitFor(time.Minute, func() error {
|
|
// TODO(tomhjp): Get the tailnet DNS name from the associated secret instead.
|
|
// If we are not the first tailnet node with the requested name, we'll get
|
|
// a -N suffix.
|
|
req, err := http.NewRequest(httpm.GET, fmt.Sprintf("http://%s-%s:80", svc.Namespace, svc.Name), nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
|
|
defer cancel()
|
|
resp, err = tnClient.HTTPClient().Do(req.WithContext(ctx))
|
|
return err
|
|
}); err != nil {
|
|
t.Fatalf("error trying to reach Service: %v", err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Fatalf("unexpected status: %v; response body s", resp.StatusCode)
|
|
}
|
|
}
|