istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/telemetry/tracing/tracing.go (about) 1 //go:build integ 2 // +build integ 3 4 // Copyright Istio Authors. All Rights Reserved. 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package tracing 19 20 import ( 21 "fmt" 22 "strings" 23 24 "istio.io/istio/pkg/config/protocol" 25 "istio.io/istio/pkg/test/framework" 26 "istio.io/istio/pkg/test/framework/components/cluster" 27 "istio.io/istio/pkg/test/framework/components/echo" 28 "istio.io/istio/pkg/test/framework/components/echo/deployment" 29 "istio.io/istio/pkg/test/framework/components/echo/match" 30 "istio.io/istio/pkg/test/framework/components/istio" 31 "istio.io/istio/pkg/test/framework/components/istio/ingress" 32 "istio.io/istio/pkg/test/framework/components/namespace" 33 "istio.io/istio/pkg/test/framework/components/zipkin" 34 "istio.io/istio/pkg/test/framework/resource" 35 ) 36 37 var ( 38 client, server echo.Instances 39 ist istio.Instance 40 ingInst ingress.Instance 41 zipkinInst zipkin.Instance 42 appNsInst namespace.Instance 43 ) 44 45 const ( 46 TraceHeader = "x-client-trace-id" 47 ) 48 49 func GetIstioInstance() *istio.Instance { 50 return &ist 51 } 52 53 // GetAppNamespace gets echo app namespace instance. 54 func GetAppNamespace() namespace.Instance { 55 return appNsInst 56 } 57 58 func GetIngressInstance() ingress.Instance { 59 return ingInst 60 } 61 62 func GetZipkinInstance() zipkin.Instance { 63 return zipkinInst 64 } 65 66 func TestSetup(ctx resource.Context) (err error) { 67 appNsInst, err = namespace.New(ctx, namespace.Config{ 68 Prefix: "echo", 69 Inject: true, 70 }) 71 if err != nil { 72 return 73 } 74 builder := deployment.New(ctx) 75 for _, c := range ctx.Clusters() { 76 clName := c.Name() 77 builder = builder. 78 WithConfig(echo.Config{ 79 Service: fmt.Sprintf("client-%s", clName), 80 Namespace: appNsInst, 81 Cluster: c, 82 Ports: nil, 83 Subsets: []echo.SubsetConfig{{}}, 84 }). 85 WithConfig(echo.Config{ 86 Service: "server", 87 Namespace: appNsInst, 88 Cluster: c, 89 Subsets: []echo.SubsetConfig{{}}, 90 Ports: []echo.Port{ 91 { 92 Name: "http", 93 Protocol: protocol.HTTP, 94 WorkloadPort: 8090, 95 }, 96 { 97 Name: "tcp", 98 Protocol: protocol.TCP, 99 // We use a port > 1024 to not require root 100 WorkloadPort: 9000, 101 }, 102 }, 103 }) 104 } 105 echos, err := builder.Build() 106 if err != nil { 107 return err 108 } 109 110 servicePrefix := func(prefix string) match.Matcher { 111 return func(i echo.Instance) bool { 112 return strings.HasPrefix(i.Config().Service, prefix) 113 } 114 } 115 client = servicePrefix("client").GetMatches(echos) 116 server = match.ServiceName(echo.NamespacedName{Name: "server", Namespace: appNsInst}).GetMatches(echos) 117 ingInst = ist.IngressFor(ctx.Clusters().Default()) 118 addrs, _ := ingInst.HTTPAddresses() 119 zipkinInst, err = zipkin.New(ctx, zipkin.Config{Cluster: ctx.Clusters().Default(), IngressAddr: addrs[0]}) 120 if err != nil { 121 return 122 } 123 return nil 124 } 125 126 func VerifyEchoTraces(t framework.TestContext, namespace, clName string, traces []zipkin.Trace) bool { 127 t.Helper() 128 wtr := WantTraceRoot(namespace, clName) 129 for _, trace := range traces { 130 // compare each candidate trace with the wanted trace 131 for _, s := range trace.Spans { 132 // find the root span of candidate trace and do recursive comparison 133 if s.ParentSpanID == "" && CompareTrace(t, s, wtr) { 134 return true 135 } 136 } 137 } 138 139 return false 140 } 141 142 func VerifyOtelEchoTraces(t framework.TestContext, namespace, clName string, traces []zipkin.Trace) bool { 143 t.Helper() 144 wtr := WantOtelTraceRoot(namespace, clName) 145 for _, trace := range traces { 146 // compare each candidate trace with the wanted trace 147 for _, s := range trace.Spans { 148 // find the root span of candidate trace and do recursive comparison 149 if s.ParentSpanID == "" && CompareTrace(t, s, wtr) { 150 return true 151 } 152 } 153 } 154 155 return false 156 } 157 158 func WantOtelTraceRoot(namespace, clName string) (root zipkin.Span) { 159 serverSpan := zipkin.Span{ 160 Name: fmt.Sprintf("server.%s.svc.cluster.local:80/*", namespace), 161 ServiceName: fmt.Sprintf("server.%s", namespace), 162 } 163 164 root = zipkin.Span{ 165 Name: fmt.Sprintf("server.%s.svc.cluster.local:80/*", namespace), 166 ServiceName: fmt.Sprintf("client-%s.%s", clName, namespace), 167 ChildSpans: []*zipkin.Span{&serverSpan}, 168 } 169 return 170 } 171 172 // compareTrace recursively compares the two given spans 173 func CompareTrace(t framework.TestContext, got, want zipkin.Span) bool { 174 t.Helper() 175 if got.Name != want.Name || got.ServiceName != want.ServiceName { 176 t.Logf("got span %+v, want span %+v", got, want) 177 return false 178 } 179 if len(got.ChildSpans) < len(want.ChildSpans) { 180 t.Logf("got %d child spans from, want %d child spans, maybe trace has not be fully reported", 181 len(got.ChildSpans), len(want.ChildSpans)) 182 return false 183 } else if len(got.ChildSpans) > len(want.ChildSpans) { 184 t.Logf("got %d child spans from, want %d child spans, maybe destination rule has not became effective", 185 len(got.ChildSpans), len(want.ChildSpans)) 186 return false 187 } 188 for i := range got.ChildSpans { 189 if !CompareTrace(t, *got.ChildSpans[i], *want.ChildSpans[i]) { 190 return false 191 } 192 } 193 return true 194 } 195 196 // WantTraceRoot constructs the wanted trace and returns the root span of that trace 197 func WantTraceRoot(namespace, clName string) (root zipkin.Span) { 198 serverSpan := zipkin.Span{ 199 Name: fmt.Sprintf("server.%s.svc.cluster.local:80/*", namespace), 200 ServiceName: fmt.Sprintf("server.%s", namespace), 201 } 202 203 root = zipkin.Span{ 204 Name: fmt.Sprintf("server.%s.svc.cluster.local:80/*", namespace), 205 ServiceName: fmt.Sprintf("client-%s.%s", clName, namespace), 206 ChildSpans: []*zipkin.Span{&serverSpan}, 207 } 208 return 209 } 210 211 // SendTraffic makes a client call to the "server" service on the http port. 212 func SendTraffic(t framework.TestContext, headers map[string][]string, cl cluster.Cluster) error { 213 t.Helper() 214 t.Logf("Sending from %s...", cl.Name()) 215 for _, cltInstance := range client { 216 if cltInstance.Config().Cluster != cl { 217 continue 218 } 219 220 _, err := cltInstance.Call(echo.CallOptions{ 221 To: server, 222 Port: echo.Port{ 223 Name: "http", 224 }, 225 HTTP: echo.HTTP{ 226 Headers: headers, 227 }, 228 Retry: echo.Retry{ 229 NoRetry: true, 230 }, 231 }) 232 if err != nil { 233 return err 234 } 235 } 236 return nil 237 }