linkerd2/test/integration/deep/endpoints/endpoints_test.go

175 lines
4.7 KiB
Go

package endpoints
import (
"context"
"fmt"
"os"
"regexp"
"strings"
"testing"
"time"
"github.com/linkerd/linkerd2/testutil"
)
var TestHelper *testutil.TestHelper
type testCase struct {
name string
authority string
expectedRE string
ns string
}
func TestMain(m *testing.M) {
TestHelper = testutil.NewTestHelper()
// Block test execution until control plane is running
TestHelper.WaitUntilDeployReady(testutil.LinkerdDeployReplicasEdge)
os.Exit(m.Run())
}
func TestGoodEndpoints(t *testing.T) {
ctx := context.Background()
controlNs := TestHelper.GetLinkerdNamespace()
TestHelper.WithDataPlaneNamespace(ctx, "endpoints-test", map[string]string{}, t, func(t *testing.T, ns string) {
out, err := TestHelper.Kubectl("", "apply", "-f", "testdata/nginx.yaml", "-n", ns)
if err != nil {
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out)
}
err = TestHelper.CheckPods(ctx, ns, "nginx", 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)
}
}
endpointCases := createTestCaseTable(controlNs, ns)
for _, endpointCase := range endpointCases {
testName := fmt.Sprintf("expect endpoints created for %s", endpointCase.name)
t.Run(testName, func(t *testing.T) {
err = testutil.RetryFor(5*time.Second, func() error {
out, err = TestHelper.LinkerdRun("diagnostics", "endpoints", endpointCase.authority, "-ojson")
if err != nil {
return fmt.Errorf("failed to get endpoints for %s: %w", endpointCase.authority, err)
}
re := regexp.MustCompile(endpointCase.expectedRE)
if !re.MatchString(out) {
return fmt.Errorf("endpoint data does not match pattern\nexpected output:\n%s\nactual:\n%s", endpointCase.expectedRE, out)
}
matches := re.FindStringSubmatch(out)
if matches == nil || len(matches) < 2 {
return fmt.Errorf("invalid endpoint data\nexpected: \n%s\nactual: \n%s", endpointCase.expectedRE, out)
}
namespaceMatch := matches[1]
if namespaceMatch != endpointCase.ns {
return fmt.Errorf("endpoint namespace does not match\nexpected: %s, actual: %s", endpointCase.ns, namespaceMatch)
}
return nil
})
if err != nil {
testutil.AnnotatedErrorf(t, "unexpected error", "unexpected error: %v", err)
}
})
}
})
}
// TODO: when #3004 gets fixed, add a negative test for mismatched ports
func TestBadEndpoints(t *testing.T) {
_, stderr, err := TestHelper.PipeToLinkerdRun("", "diagnostics", "endpoints", "foo")
if err == nil {
testutil.AnnotatedFatalf(t, "was expecting an error", "was expecting an error: %v", err)
}
stderrOut := strings.Split(stderr, "\n")
if len(stderrOut) == 0 {
testutil.AnnotatedFatalf(t, "unexpected output", "unexpected output: %s", stderr)
}
if stderrOut[0] != "Destination API error: Invalid authority: foo" {
testutil.AnnotatedErrorf(t, "unexpected error string", "unexpected error string: %s", stderrOut[0])
}
}
func createTestCaseTable(controlNs, endpointNs string) []testCase {
return []testCase{
{
name: "linkerd-dst",
authority: fmt.Sprintf("linkerd-dst.%s.svc.cluster.local:8086", controlNs),
expectedRE: `\[
\{
"namespace": "(\S*)",
"ip": "[a-f0-9.:]+",
"port": 8086,
"pod": "linkerd-destination\-[a-f0-9]+\-[a-z0-9]+",
"service": "linkerd-dst\.\S*",
"weight": \d+,
"http2": \{(?s).*\},
"labels": \{(?s).*\}
\}
\]`,
ns: controlNs,
},
{
name: "linkerd-identity",
authority: fmt.Sprintf("linkerd-identity.%s.svc.cluster.local:8080", controlNs),
expectedRE: `\[
\{
"namespace": "(\S*)",
"ip": "[a-f0-9.:]+",
"port": 8080,
"pod": "linkerd-identity\-[a-f0-9]+\-[a-z0-9]+",
"service": "linkerd-identity\.\S*",
"weight": \d+,
"http2": \{(?s).*\},
"labels": \{(?s).*\}
\}
\]`,
ns: controlNs,
},
{
name: "linkerd-proxy-injector",
authority: fmt.Sprintf("linkerd-proxy-injector.%s.svc.cluster.local:443", controlNs),
expectedRE: `\[
\{
"namespace": "(\S*)",
"ip": "[a-f0-9.:]+",
"port": 8443,
"pod": "linkerd-proxy-injector-[a-f0-9]+\-[a-z0-9]+",
"service": "linkerd-proxy-injector\.\S*",
"weight": \d+,
"http2": \{(?s).*\},
"labels": \{(?s).*\}
\}
\]`,
ns: controlNs,
},
{
name: "nginx",
authority: fmt.Sprintf("nginx.%s.svc.cluster.local:8080", endpointNs),
expectedRE: `\[
\{
"namespace": "(\S*)",
"ip": "[a-f0-9.:]+",
"port": 8080,
"pod": "nginx-[a-f0-9]+\-[a-z0-9]+",
"service": "nginx\.\S*",
"weight": \d+,
"http2": \{(?s).*\},
"labels": \{(?s).*\}
\}
\]`,
ns: endpointNs,
},
}
}