package tap import ( "context" "os" "strings" "testing" "github.com/linkerd/linkerd2/testutil" ) ////////////////////// /// TEST SETUP /// ////////////////////// var TestHelper *testutil.TestHelper func TestMain(m *testing.M) { TestHelper = testutil.NewTestHelper() os.Exit(m.Run()) } var ( expectedT1 = testutil.TapEvent{ Method: "POST", Authority: "t1-svc:9090", Path: "/buoyantio.bb.TheService/theFunction", HTTPStatus: "200", GrpcStatus: "OK", TLS: "true", LineCount: 3, } expectedT2 = testutil.TapEvent{ Method: "POST", Authority: "t2-svc:9090", Path: "/buoyantio.bb.TheService/theFunction", HTTPStatus: "200", GrpcStatus: "Unknown", TLS: "true", LineCount: 3, } expectedT3 = testutil.TapEvent{ Method: "POST", Authority: "t3-svc:8080", Path: "/", HTTPStatus: "200", GrpcStatus: "", TLS: "true", LineCount: 3, } expectedGateway = testutil.TapEvent{ Method: "GET", Authority: "gateway-svc:8080", Path: "/", HTTPStatus: "500", GrpcStatus: "", TLS: "true", LineCount: 3, } ) ////////////////////// /// TEST EXECUTION /// ////////////////////// func TestCliTap(t *testing.T) { out, err := TestHelper.LinkerdRun("inject", "--manual", "testdata/tap_application.yaml") if err != nil { testutil.AnnotatedFatal(t, "'linkerd inject' command failed", err) } ctx := context.Background() TestHelper.WithDataPlaneNamespace(ctx, "tap-test", map[string]string{}, t, func(t *testing.T, prefixedNs string) { out, err = TestHelper.KubectlApply(out, prefixedNs) if err != nil { testutil.AnnotatedFatalf(t, "'kubectl apply' command failed", "'kubectl apply' command failed\n%s", out) } // wait for deployments to start for _, deploy := range []string{"t1", "t2", "t3", "gateway"} { if err := TestHelper.CheckPods(ctx, prefixedNs, deploy, 1); err != nil { if rce, ok := err.(*testutil.RestartCountError); ok { testutil.AnnotatedWarn(t, "CheckPods timed-out", rce) } else { testutil.AnnotatedError(t, "CheckPods timed-out", err) } } if err := TestHelper.CheckDeployment(ctx, prefixedNs, deploy, 1); err != nil { testutil.AnnotatedErrorf(t, "CheckDeployment timed-out", "Error validating deployment [%s]:\n%s", deploy, err) } } t.Run("tap a deployment", func(t *testing.T) { events, err := testutil.Tap("deploy/t1", TestHelper, "--namespace", prefixedNs) if err != nil { testutil.AnnotatedFatal(t, "tap failed", err) } err = testutil.ValidateExpected(events, expectedT1) if err != nil { testutil.AnnotatedFatal(t, "validating tap failed", err) } }) t.Run("tap a deployment using context namespace", func(t *testing.T) { out, err := TestHelper.Kubectl("", "config", "set-context", "--namespace="+prefixedNs, "--current") if err != nil { testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out) } events, err := testutil.Tap("deploy/t1", TestHelper) if err != nil { testutil.AnnotatedFatal(t, "tap failed using context namespace", err) } err = testutil.ValidateExpected(events, expectedT1) if err != nil { testutil.AnnotatedFatal(t, "validating tap failed using context namespace", err) } out, err = TestHelper.Kubectl("", "config", "set-context", "--namespace=default", "--current") if err != nil { testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v output:\n%s", err, out) } }) t.Run("tap a disabled deployment", func(t *testing.T) { out, stderr, err := TestHelper.PipeToLinkerdRun("", "tap", "deploy/t4", "--namespace", prefixedNs) if out != "" { testutil.AnnotatedFatalf(t, "unexpected output", "unexpected output: %s", out) } if err == nil { testutil.Fatal(t, "expected an error, got none") } if stderr == "" { testutil.Fatal(t, "expected an error, got none") } expectedErr := "Error: all pods found for deployment/t4 have tapping disabled" if errs := strings.Split(stderr, "\n"); errs[0] != expectedErr { testutil.AnnotatedFatalf(t, "unexpected error", "expected [%s], got: %s", expectedErr, errs[0]) } }) t.Run("tap a service call", func(t *testing.T) { events, err := testutil.Tap("deploy/gateway", TestHelper, "--to", "svc/t2-svc", "--namespace", prefixedNs) if err != nil { testutil.AnnotatedFatal(t, "failed tapping a service call", err) } err = testutil.ValidateExpected(events, expectedT2) if err != nil { testutil.AnnotatedFatal(t, "failed validating tapping a service call", err) } }) t.Run("tap a pod", func(t *testing.T) { deploy := "t3" pods, err := TestHelper.GetPodNamesForDeployment(ctx, prefixedNs, deploy) if err != nil { testutil.AnnotatedFatalf(t, "failed to get pods for deployment t3", "failed to get pods for deployment [%s]\n%s", deploy, err) } if len(pods) != 1 { testutil.Fatalf(t, "expected exactly one pod for deployment [%s], got:\n%v", deploy, pods) } events, err := testutil.Tap("pod/"+pods[0], TestHelper, "--namespace", prefixedNs) if err != nil { testutil.AnnotatedFatal(t, "error tapping pod", err) } err = testutil.ValidateExpected(events, expectedT3) if err != nil { testutil.AnnotatedFatal(t, "error validating pod tap", err) } }) t.Run("filter tap events by method", func(t *testing.T) { events, err := testutil.Tap("deploy/gateway", TestHelper, "--namespace", prefixedNs, "--method", "GET") if err != nil { testutil.AnnotatedFatal(t, "error filtering tap events by method", err) } err = testutil.ValidateExpected(events, expectedGateway) if err != nil { testutil.AnnotatedFatal(t, "error validating filtered tap events by method", err) } }) t.Run("filter tap events by authority", func(t *testing.T) { events, err := testutil.Tap("deploy/gateway", TestHelper, "--namespace", prefixedNs, "--authority", "t1-svc:9090") if err != nil { testutil.AnnotatedFatal(t, "error filtering tap events by authority", err) } err = testutil.ValidateExpected(events, expectedT1) if err != nil { testutil.AnnotatedFatal(t, "error validating filtered tap events by authority", err) } }) }) }