1 package api
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "testing"
8
9 pkgK8s "github.com/linkerd/linkerd2/pkg/k8s"
10 pb "github.com/linkerd/linkerd2/viz/metrics-api/gen/viz"
11 "github.com/prometheus/common/model"
12 "google.golang.org/protobuf/proto"
13 )
14
15 const (
16 serverIDLabel = model.LabelName("server_id")
17 resourceLabel = model.LabelName("deployment")
18 podLabel = model.LabelName("pod")
19 )
20
21 type edgesExpected struct {
22 expectedStatRPC
23 req *pb.EdgesRequest
24 expectedResponse *pb.EdgesResponse
25 }
26
27 func genOutboundPromSample(resourceNamespace, resourceName, resourceNameDst, resourceNamespaceDst, serverID string) *model.Sample {
28 dstResourceLabel := "dst_" + resourceLabel
29
30 return &model.Sample{
31 Metric: model.Metric{
32 resourceLabel: model.LabelValue(resourceName),
33 namespaceLabel: model.LabelValue(resourceNamespace),
34 dstNamespaceLabel: model.LabelValue(resourceNamespaceDst),
35 dstResourceLabel: model.LabelValue(resourceNameDst),
36 serverIDLabel: model.LabelValue(serverID),
37 podLabel: model.LabelValue(resourceName + "-0"),
38 },
39 Value: 123,
40 Timestamp: 456,
41 }
42 }
43
44 func genPod(name, namespace, sa string) string {
45 return fmt.Sprintf(`apiVersion: v1
46 kind: Pod
47 metadata:
48 name: %s
49 namespace: %s
50 spec:
51 containers:
52 - name: linkerd-proxy
53 env:
54 - name: LINKERD2_PROXY_IDENTITY_LOCAL_NAME
55 value: $(_pod_sa).$(_pod_ns).serviceaccount.identity.linkerd.cluster.local
56 serviceAccountName: %s
57 status:
58 phase: Running
59 `, name, namespace, sa)
60 }
61
62 func testEdges(t *testing.T, expectations []edgesExpected) {
63 for _, exp := range expectations {
64 mockProm, fakeGrpcServer, err := newMockGrpcServer(exp.expectedStatRPC)
65 if err != nil {
66 t.Fatalf("Error creating mock grpc server: %s", err)
67 }
68
69 rsp, err := fakeGrpcServer.Edges(context.TODO(), exp.req)
70 if !errors.Is(err, exp.err) {
71 t.Fatalf("Expected error: %s, Got: %s", exp.err, err)
72 }
73
74 err = exp.verifyPromQueries(mockProm)
75 if err != nil {
76 t.Fatal(err)
77 }
78
79 rspEdgeRows := rsp.GetOk().Edges
80
81 if len(rspEdgeRows) != len(exp.expectedResponse.GetOk().Edges) {
82 t.Fatalf(
83 "Expected [%d] edge rows, got [%d].\nExpected:\n%s\nGot:\n%s",
84 len(exp.expectedResponse.GetOk().Edges),
85 len(rspEdgeRows),
86 exp.expectedResponse.GetOk().Edges,
87 rspEdgeRows,
88 )
89 }
90
91 for i, st := range rspEdgeRows {
92 expected := exp.expectedResponse.GetOk().Edges[i]
93 if !proto.Equal(st, expected) {
94 t.Fatalf("Expected: %+v\n Got: %+v\n", expected, st)
95 }
96 }
97
98 if !proto.Equal(exp.expectedResponse.GetOk(), rsp.GetOk()) {
99 t.Fatalf("Expected edgesOkResp: %+v\n Got: %+v", &exp.expectedResponse, rsp)
100 }
101 }
102 }
103
104 func TestEdges(t *testing.T) {
105 mockPromResponse := model.Vector{
106 genOutboundPromSample("emojivoto", "web", "emoji", "emojivoto", "emoji.emojivoto.serviceaccount.identity.linkerd.cluster.local"),
107 genOutboundPromSample("emojivoto", "web", "voting", "emojivoto", "voting.emojivoto.serviceaccount.identity.linkerd.cluster.local"),
108 genOutboundPromSample("emojivoto", "vote-bot", "web", "emojivoto", "web.emojivoto.serviceaccount.identity.linkerd.cluster.local"),
109 genOutboundPromSample("linkerd", "linkerd-identity", "linkerd-prometheus", "linkerd", "linkerd-prometheus.linkerd.serviceaccount.identity.linkerd.cluster.local"),
110 }
111 pods := []string{
112 genPod("web-0", "emojivoto", "web"),
113 genPod("vote-bot-0", "emojivoto", "default"),
114 genPod("linkerd-identity-0", "linkerd", "linkerd-identity"),
115 }
116
117 t.Run("Successfully returns edges for resource type Deployment and namespace emojivoto", func(t *testing.T) {
118 expectations := []edgesExpected{
119 {
120 expectedStatRPC: expectedStatRPC{
121 err: nil,
122 mockPromResponse: mockPromResponse,
123 k8sConfigs: pods,
124 },
125 req: &pb.EdgesRequest{
126 Selector: &pb.ResourceSelection{
127 Resource: &pb.Resource{
128 Namespace: "emojivoto",
129 Type: pkgK8s.Deployment,
130 },
131 },
132 },
133 expectedResponse: GenEdgesResponse("deployment", "emojivoto"),
134 }}
135
136 testEdges(t, expectations)
137 })
138
139 t.Run("Successfully returns edges for resource type Deployment and namespace linkerd", func(t *testing.T) {
140 expectations := []edgesExpected{
141 {
142 expectedStatRPC: expectedStatRPC{
143 err: nil,
144 mockPromResponse: mockPromResponse,
145 k8sConfigs: pods,
146 },
147 req: &pb.EdgesRequest{
148 Selector: &pb.ResourceSelection{
149 Resource: &pb.Resource{
150 Namespace: "linkerd",
151 Type: pkgK8s.Deployment,
152 },
153 },
154 },
155 expectedResponse: GenEdgesResponse("deployment", "linkerd"),
156 }}
157
158 testEdges(t, expectations)
159 })
160
161 t.Run("Successfully returns edges for resource type Deployment and all namespaces", func(t *testing.T) {
162 expectations := []edgesExpected{
163 {
164 expectedStatRPC: expectedStatRPC{
165 err: nil,
166 mockPromResponse: mockPromResponse,
167 k8sConfigs: pods,
168 },
169 req: &pb.EdgesRequest{
170 Selector: &pb.ResourceSelection{
171 Resource: &pb.Resource{
172 Type: pkgK8s.Deployment,
173 },
174 },
175 },
176 expectedResponse: GenEdgesResponse("deployment", "all"),
177 }}
178
179 testEdges(t, expectations)
180 })
181 }
182
View as plain text