linkerd2/test/integration/deep/localhost/localhost_test.go

124 lines
4.0 KiB
Go

package localhost
import (
"context"
"fmt"
"os"
"regexp"
"testing"
"time"
"github.com/linkerd/linkerd2/testutil"
"github.com/linkerd/linkerd2/testutil/prommatch"
)
var TestHelper *testutil.TestHelper
func TestMain(m *testing.M) {
TestHelper = testutil.NewTestHelper()
// Block test execution until control plane is running
TestHelper.WaitUntilDeployReady(testutil.LinkerdDeployReplicasEdge)
os.Exit(m.Run())
}
var (
nginxPodRE = regexp.MustCompile(`nginx.*`)
nginxLabels = prommatch.Labels{
"direction": prommatch.Equals("outbound"),
"authority": prommatch.Equals("nginx.linkerd-localhost-test.svc.cluster.local:8080"),
"dst_deployment": prommatch.Equals("nginx"),
"dst_namespace": prommatch.Equals("linkerd-localhost-test"),
"dst_pod": prommatch.Like(nginxPodRE),
"dst_pod_template_hash": prommatch.Any(),
"dst_service": prommatch.Equals("nginx"),
"dst_serviceaccount": prommatch.Equals("default"),
}
requestsToNGINXMatcher = prommatch.NewMatcher("request_total",
nginxLabels,
prommatch.HasPositiveValue(),
)
failedResponsesFromNGINXMatcher = prommatch.NewMatcher("response_total",
nginxLabels,
prommatch.Labels{
"classification": prommatch.Equals("failure"),
},
prommatch.HasPositiveValue(),
)
successResponsesMatcher = prommatch.NewMatcher("response_total",
prommatch.Labels{
"direction": prommatch.Equals("outbound"),
"classification": prommatch.Equals("success"),
},
prommatch.HasPositiveValue(),
)
tcpOpenMatcher = prommatch.NewMatcher("tcp_open_connections",
nginxLabels,
prommatch.HasValueOf(0),
)
)
// TestLocalhostServer creates an nginx deployment which listens on localhost
// and a slow-cooker which attempts to send traffic to the nginx. Since
// slow-cooker should not be able to connect to nginx's localhost address,
// these requests should fail.
func TestLocalhostServer(t *testing.T) {
ctx := context.Background()
nginx, err := TestHelper.LinkerdRun("inject", "testdata/nginx.yaml")
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
slowcooker, err := TestHelper.LinkerdRun("inject", "testdata/slow-cooker.yaml")
if err != nil {
testutil.AnnotatedFatal(t, "unexpected error", err)
}
TestHelper.WithDataPlaneNamespace(ctx, "localhost-test", map[string]string{}, t, func(t *testing.T, ns string) {
out, err := TestHelper.KubectlApply(nginx, ns)
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
out, err = TestHelper.KubectlApply(slowcooker, ns)
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
for _, deploy := range []string{"nginx", "slow-cooker"} {
err = TestHelper.CheckPods(ctx, ns, deploy, 1)
if err != nil {
//nolint:errorlint
if rce, ok := err.(*testutil.RestartCountError); ok {
testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
} else {
testutil.AnnotatedError(t, "CheckPods timed-out", err)
}
}
}
err = testutil.RetryFor(50*time.Second, func() error {
// Use a short time window so that transient errors at startup
// fall out of the window.
metrics, err := TestHelper.LinkerdRun("diagnostics", "proxy-metrics", "-n", ns, "deploy/slow-cooker")
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected diagnostics error",
"unexpected diagnostics error: %s\n%s", err, out)
}
m := prommatch.Suite{}.
MustContain("requests from slowcooker to nginx", requestsToNGINXMatcher).
MustContain("failed responses returned to slowcooker from nginx", failedResponsesFromNGINXMatcher).
MustNotContain("success responses returned to slowcooker from nginx", successResponsesMatcher).
MustContain("zero open tcp connections to nginx", tcpOpenMatcher)
if err := m.CheckString(metrics); err != nil {
return fmt.Errorf("metrics check failed: %w", err)
}
return nil
})
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected stat output", "unexpected stat output: %v", err)
}
})
}