...

Source file src/github.com/linkerd/linkerd2/test/integration/viz/tracing/tracing_test.go

Documentation: github.com/linkerd/linkerd2/test/integration/viz/tracing

     1  package tracing
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"fmt"
     9  	"html/template"
    10  	"os"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/linkerd/linkerd2/jaeger/pkg/labels"
    15  	"github.com/linkerd/linkerd2/pkg/healthcheck"
    16  	"github.com/linkerd/linkerd2/pkg/version"
    17  	"github.com/linkerd/linkerd2/testutil"
    18  )
    19  
    20  type (
    21  	traces struct {
    22  		Data []trace `json:"data"`
    23  	}
    24  
    25  	trace struct {
    26  		Processes map[string]process `json:"processes"`
    27  	}
    28  
    29  	process struct {
    30  		ServiceName string `json:"serviceName"`
    31  	}
    32  )
    33  
    34  //////////////////////
    35  ///   TEST SETUP   ///
    36  //////////////////////
    37  
    38  var TestHelper *testutil.TestHelper
    39  
    40  func TestMain(m *testing.M) {
    41  	TestHelper = testutil.NewTestHelper()
    42  	// Block test execution until viz extension is running
    43  	TestHelper.WaitUntilDeployReady(testutil.LinkerdVizDeployReplicas)
    44  	os.Exit(m.Run())
    45  }
    46  
    47  //////////////////////
    48  /// TEST EXECUTION ///
    49  //////////////////////
    50  
    51  func TestTracing(t *testing.T) {
    52  	ctx := context.Background()
    53  
    54  	// linkerd-jaeger extension
    55  	tracingNs := "linkerd-jaeger"
    56  	out, err := TestHelper.LinkerdRun("jaeger", "install")
    57  	if err != nil {
    58  		testutil.AnnotatedFatal(t, "'linkerd jaeger install' command failed", err)
    59  	}
    60  
    61  	out, err = TestHelper.KubectlApply(out, "")
    62  	if err != nil {
    63  		testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
    64  			"'kubectl apply' command failed\n%s", out)
    65  	}
    66  
    67  	// wait for the jaeger extension
    68  	checkCmd := []string{"jaeger", "check", "--wait=0"}
    69  	golden := "check.jaeger.golden"
    70  	timeout := time.Minute
    71  	err = testutil.RetryFor(timeout, func() error {
    72  		out, err := TestHelper.LinkerdRun(checkCmd...)
    73  		if err != nil {
    74  			return fmt.Errorf("'linkerd jaeger check' command failed\n%w\n%s", err, out)
    75  		}
    76  
    77  		pods, err := TestHelper.KubernetesHelper.GetPods(context.Background(), tracingNs, nil)
    78  		if err != nil {
    79  			testutil.AnnotatedFatal(t, fmt.Sprintf("failed to retrieve pods: %s", err), err)
    80  		}
    81  
    82  		tpl := template.Must(template.ParseFiles("testdata" + "/" + golden))
    83  		chs, err := version.NewChannels("test-99.88.77")
    84  		if err != nil {
    85  			panic(err.Error())
    86  		}
    87  		versionErr := healthcheck.CheckProxyVersionsUpToDate(pods, chs)
    88  		versionErrMsg := ""
    89  		if versionErr != nil {
    90  			versionErrMsg = versionErr.Error()
    91  		}
    92  		vars := struct {
    93  			ProxyVersionErr string
    94  			HintURL         string
    95  		}{
    96  			versionErrMsg,
    97  			healthcheck.HintBaseURL(TestHelper.GetVersion()),
    98  		}
    99  
   100  		var expected bytes.Buffer
   101  		if err := tpl.Execute(&expected, vars); err != nil {
   102  			testutil.AnnotatedFatal(t, fmt.Sprintf("failed to parse check.viz.golden template: %s", err), err)
   103  		}
   104  
   105  		if out != expected.String() {
   106  			return fmt.Errorf(
   107  				"Expected:\n%s\nActual:\n%s", expected.String(), out)
   108  		}
   109  		return nil
   110  	})
   111  	if err != nil {
   112  		testutil.AnnotatedFatal(t, fmt.Sprintf("'linkerd jaeger check' command timed-out (%s)", timeout), err)
   113  	}
   114  
   115  	TestHelper.WithDataPlaneNamespace(ctx, "tracing-test", map[string]string{}, t, func(t *testing.T, namespace string) {
   116  		emojivotoYaml, err := testutil.ReadFile("testdata/emojivoto.yaml")
   117  		if err != nil {
   118  			testutil.AnnotatedFatalf(t, "failed to read emojivoto yaml",
   119  				"failed to read emojivoto yaml\n%s\n", err)
   120  		}
   121  
   122  		out, err = TestHelper.KubectlApply(emojivotoYaml, namespace)
   123  		if err != nil {
   124  			testutil.AnnotatedFatalf(t, "'kubectl apply' command failed",
   125  				"'kubectl apply' command failed\n%s", out)
   126  		}
   127  
   128  		// wait for deployments to start
   129  		for _, deploy := range []struct {
   130  			ns   string
   131  			name string
   132  		}{
   133  			{ns: namespace, name: "vote-bot"},
   134  			{ns: namespace, name: "web"},
   135  			{ns: namespace, name: "emoji"},
   136  			{ns: namespace, name: "voting"},
   137  			{ns: tracingNs, name: "collector"},
   138  			{ns: tracingNs, name: "jaeger"},
   139  		} {
   140  			if err := TestHelper.CheckPods(ctx, deploy.ns, deploy.name, 1); err != nil {
   141  				//nolint:errorlint
   142  				if rce, ok := err.(*testutil.RestartCountError); ok {
   143  					testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
   144  				} else {
   145  					testutil.AnnotatedError(t, "CheckPods timed-out", err)
   146  				}
   147  			}
   148  
   149  			pods, err := TestHelper.GetPodsForDeployment(ctx, deploy.ns, deploy.name)
   150  			if err != nil {
   151  				testutil.AnnotatedWarn(t, "Failed to get pods", err)
   152  			}
   153  			for _, pod := range pods {
   154  				pod := pod
   155  				if !labels.IsTracingEnabled(&pod) {
   156  					testutil.AnnotatedWarn(t, "Tracing annotation not found on pod", pod.Namespace, pod.Name)
   157  					// XXX This test is super duper flakey, so for now we ignore failures when the
   158  					// annotation is missing See https://github.com/linkerd/linkerd2/issues/7538
   159  					t.SkipNow()
   160  				}
   161  			}
   162  		}
   163  
   164  		t.Run("expect full trace", func(t *testing.T) {
   165  			timeout := 3 * time.Minute
   166  			err = testutil.RetryFor(timeout, func() error {
   167  				url, err := TestHelper.URLFor(ctx, tracingNs, "jaeger", 16686)
   168  				if err != nil {
   169  					return err
   170  				}
   171  
   172  				tracesJSON, err := TestHelper.HTTPGetURL(url + "/jaeger/api/traces?lookback=1h&service=linkerd-proxy")
   173  				if err != nil {
   174  					return err
   175  				}
   176  				traces := traces{}
   177  
   178  				err = json.Unmarshal([]byte(tracesJSON), &traces)
   179  				if err != nil {
   180  					return err
   181  				}
   182  
   183  				if !hasTraceWithProcess(&traces, "linkerd-proxy") {
   184  					return noProxyTraceFound{}
   185  				}
   186  				return nil
   187  			})
   188  			if err != nil {
   189  				// XXX This test is super duper flakey, so for now we ignore failures when proxy
   190  				// traces are missing. See https://github.com/linkerd/linkerd2/issues/7538
   191  				var npte noProxyTraceFound
   192  				if errors.As(err, &npte) {
   193  					testutil.AnnotatedWarn(t, fmt.Sprintf("timed-out checking trace (%s)", timeout), err)
   194  					t.SkipNow()
   195  				}
   196  				testutil.AnnotatedFatal(t, fmt.Sprintf("timed-out checking trace (%s)", timeout), err)
   197  			}
   198  		})
   199  	})
   200  }
   201  
   202  func hasTraceWithProcess(traces *traces, ps string) bool {
   203  	for _, trace := range traces.Data {
   204  		for _, process := range trace.Processes {
   205  			if process.ServiceName == ps {
   206  				return true
   207  			}
   208  		}
   209  	}
   210  	return false
   211  }
   212  
   213  type noProxyTraceFound struct{}
   214  
   215  func (e noProxyTraceFound) Error() string {
   216  	return "no trace found with processes: linkerd-proxy"
   217  }
   218  

View as plain text