github.com/kiali/kiali@v1.84.0/graph/api/api_test.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io" 9 "net/http" 10 "net/http/httptest" 11 "os" 12 "runtime" 13 "testing" 14 15 "github.com/google/go-cmp/cmp" 16 "github.com/gorilla/mux" 17 "github.com/prometheus/common/model" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/mock" 20 core_v1 "k8s.io/api/core/v1" 21 meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 "k8s.io/client-go/tools/clientcmd/api" 23 24 "github.com/kiali/kiali/business" 25 "github.com/kiali/kiali/business/authentication" 26 "github.com/kiali/kiali/config" 27 "github.com/kiali/kiali/graph" 28 "github.com/kiali/kiali/kubernetes" 29 "github.com/kiali/kiali/kubernetes/cache" 30 "github.com/kiali/kiali/kubernetes/kubetest" 31 "github.com/kiali/kiali/prometheus" 32 "github.com/kiali/kiali/prometheus/prometheustest" 33 ) 34 35 // Setup mock 36 37 func setupMocked(t *testing.T) (*prometheus.Client, *prometheustest.PromAPIMock) { 38 conf := config.NewConfig() 39 conf.KubernetesConfig.ClusterName = "east" 40 config.Set(conf) 41 42 k8s := kubetest.NewFakeK8sClient( 43 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 44 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "tutorial"}}, 45 ) 46 47 api := new(prometheustest.PromAPIMock) 48 client, err := prometheus.NewClient() 49 if err != nil { 50 t.Fatal(err) 51 } 52 client.Inject(api) 53 54 mockClientFactory := kubetest.NewK8SClientFactoryMock(k8s) 55 business.SetWithBackends(mockClientFactory, nil) 56 cache := cache.NewTestingCache(t, k8s, *conf) 57 business.WithKialiCache(cache) 58 59 return client, api 60 } 61 62 // firstKey returns the first key from the map. 63 // Useful when you don't care about ordering. 64 // Empty map returns empty K value. 65 func firstKey[K comparable, V any](m map[K]V) K { 66 var k K 67 for k = range m { 68 break 69 } 70 return k 71 } 72 73 func setupMockedWithIstioComponentNamespaces(t *testing.T, meshId string, userClients map[string]kubernetes.ClientInterface) (*prometheus.Client, *prometheustest.PromAPIMock, error) { 74 testConfig := config.NewConfig() 75 testConfig.KubernetesConfig.ClusterName = firstKey(userClients) 76 if meshId != "" { 77 testConfig.ExternalServices.Prometheus.QueryScope = map[string]string{"mesh_id": meshId} 78 } 79 config.Set(testConfig) 80 fmt.Println("!!! Set up complex mock") 81 82 api := new(prometheustest.PromAPIMock) 83 client, err := prometheus.NewClient() 84 if err != nil { 85 return nil, nil, err 86 } 87 client.Inject(api) 88 89 mockClientFactory := kubetest.NewK8SClientFactoryMock(nil) 90 mockClientFactory.SetClients(userClients) 91 92 cache := cache.NewTestingCacheWithFactory(t, mockClientFactory, *testConfig) 93 94 business.WithKialiCache(cache) 95 business.SetWithBackends(mockClientFactory, nil) 96 97 return client, api, nil 98 } 99 100 func mockQuery(api *prometheustest.PromAPIMock, query string, ret *model.Vector) { 101 api.On( 102 "Query", 103 mock.AnythingOfType("*context.emptyCtx"), 104 query, 105 mock.AnythingOfType("time.Time"), 106 ).Return(*ret, nil) 107 api.On( 108 "Query", 109 mock.AnythingOfType("*context.cancelCtx"), 110 query, 111 mock.AnythingOfType("time.Time"), 112 ).Return(*ret, nil) 113 } 114 115 // mockNamespaceGraph provides the same single-namespace mocks to be used for different graph types 116 func mockNamespaceGraph(t *testing.T) (*prometheus.Client, *prometheustest.PromAPIMock, error) { 117 q0 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 118 v0 := model.Vector{} 119 120 q1 := `round(sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 121 q1m0 := model.Metric{ 122 "source_workload_namespace": "istio-system", 123 "source_workload": "ingressgateway-unknown", 124 "source_canonical_service": "ingressgateway", 125 "source_canonical_revision": "latest", 126 "source_cluster": "east", 127 "destination_cluster": "east", 128 "destination_service_namespace": "bookinfo", 129 "destination_service": "productpage:9080", 130 "destination_service_name": "productpage", 131 "destination_workload_namespace": "bookinfo", 132 "destination_workload": "productpage-v1", 133 "destination_canonical_service": "productpage", 134 "destination_canonical_revision": "v1", 135 "request_protocol": "http", 136 "response_code": "200", 137 "grpc_response_status": "0", 138 "response_flags": "-", 139 } 140 q1m1 := model.Metric{ 141 "source_workload_namespace": "unknown", 142 "source_workload": "unknown", 143 "source_canonical_service": "unknown", 144 "source_canonical_revision": "unknown", 145 "source_cluster": "unknown", 146 "destination_cluster": "east", 147 "destination_service_namespace": "bookinfo", 148 "destination_service": "productpage:9080", 149 "destination_service_name": "productpage", 150 "destination_workload_namespace": "bookinfo", 151 "destination_workload": "productpage-v1", 152 "destination_canonical_service": "productpage", 153 "destination_canonical_revision": "v1", 154 "request_protocol": "http", 155 "response_code": "200", 156 "grpc_response_status": "0", 157 "response_flags": "-", 158 } 159 q1m2 := model.Metric{ 160 "source_workload_namespace": "unknown", 161 "source_workload": "unknown", 162 "source_canonical_service": "unknown", 163 "source_canonical_revision": "unknown", 164 "source_cluster": "unknown", 165 "destination_cluster": "east", 166 "destination_service_namespace": "bookinfo", 167 "destination_service": "", 168 "destination_service_name": "", 169 "destination_workload_namespace": "bookinfo", 170 "destination_workload": "kiali-2412", // test case when there is no destination_service_name 171 "destination_canonical_service": "", 172 "destination_canonical_revision": "", 173 "request_protocol": "http", 174 "response_code": "200", 175 "grpc_response_status": "0", 176 "response_flags": "-", 177 } 178 q1m3 := model.Metric{ 179 "source_workload_namespace": "bookinfo", 180 "source_workload": "productpage-v1", 181 "source_canonical_service": "productpage", 182 "source_canonical_revision": "v1", 183 "source_cluster": "east", 184 "destination_cluster": "east", 185 "destination_service_namespace": "bookinfo", 186 "destination_service": "reviews:9080", 187 "destination_service_name": "reviews", 188 "destination_workload_namespace": "bookinfo", 189 "destination_workload": "reviews-v1", 190 "destination_canonical_service": "reviews", 191 "destination_canonical_revision": "v1", 192 "request_protocol": "http", 193 "response_code": "200", 194 "grpc_response_status": "0", 195 "response_flags": "-", 196 } 197 q1m4 := model.Metric{ 198 "source_workload_namespace": "bookinfo", 199 "source_workload": "productpage-v1", 200 "source_canonical_service": "productpage", 201 "source_canonical_revision": "v1", 202 "source_cluster": "east", 203 "destination_cluster": "east", 204 "destination_service_namespace": "bookinfo", 205 "destination_service": "reviews:9080", 206 "destination_service_name": "reviews", 207 "destination_workload_namespace": "bookinfo", 208 "destination_workload": "reviews-v2", 209 "destination_canonical_service": "reviews", 210 "destination_canonical_revision": "v2", 211 "request_protocol": "http", 212 "response_code": "200", 213 "grpc_response_status": "0", 214 "response_flags": "-", 215 } 216 q1m5 := model.Metric{ 217 "source_workload_namespace": "bookinfo", 218 "source_workload": "productpage-v1", 219 "source_canonical_service": "productpage", 220 "source_canonical_revision": "v1", 221 "source_cluster": "east", 222 "destination_cluster": "east", 223 "destination_service_namespace": "bookinfo", 224 "destination_service": "reviews:9080", 225 "destination_service_name": "reviews", 226 "destination_workload_namespace": "bookinfo", 227 "destination_workload": "reviews-v3", 228 "destination_canonical_service": "reviews", 229 "destination_canonical_revision": "v3", 230 "request_protocol": "http", 231 "response_code": "200", 232 "grpc_response_status": "0", 233 "response_flags": "-", 234 } 235 q1m6 := model.Metric{ 236 "source_workload_namespace": "bookinfo", 237 "source_workload": "productpage-v1", 238 "source_canonical_service": "productpage", 239 "source_canonical_revision": "v1", 240 "source_cluster": "east", 241 "destination_cluster": "east", 242 "destination_service_namespace": "bookinfo", 243 "destination_service": "details:9080", 244 "destination_service_name": "details", 245 "destination_workload_namespace": "bookinfo", 246 "destination_workload": "details-v1", 247 "destination_canonical_service": "details", 248 "destination_canonical_revision": "v1", 249 "request_protocol": "http", 250 "response_code": "300", 251 "response_flags": "-", 252 } 253 q1m7 := model.Metric{ 254 "source_workload_namespace": "bookinfo", 255 "source_workload": "productpage-v1", 256 "source_canonical_service": "productpage", 257 "source_canonical_revision": "v1", 258 "source_cluster": "east", 259 "destination_cluster": "east", 260 "destination_service_namespace": "bookinfo", 261 "destination_service": "details:9080", 262 "destination_service_name": "details", 263 "destination_workload_namespace": "bookinfo", 264 "destination_workload": "details-v1", 265 "destination_canonical_service": "details", 266 "destination_canonical_revision": "v1", 267 "request_protocol": "http", 268 "response_code": "400", 269 "response_flags": "-", 270 } 271 q1m8 := model.Metric{ 272 "source_workload_namespace": "bookinfo", 273 "source_workload": "productpage-v1", 274 "source_canonical_service": "productpage", 275 "source_canonical_revision": "v1", 276 "source_cluster": "east", 277 "destination_cluster": "east", 278 "destination_service_namespace": "bookinfo", 279 "destination_service": "details:9080", 280 "destination_service_name": "details", 281 "destination_workload_namespace": "bookinfo", 282 "destination_workload": "details-v1", 283 "destination_canonical_service": "details", 284 "destination_canonical_revision": "v1", 285 "request_protocol": "http", 286 "response_code": "500", 287 "response_flags": "-", 288 } 289 q1m9 := model.Metric{ 290 "source_workload_namespace": "bookinfo", 291 "source_workload": "productpage-v1", 292 "source_canonical_service": "productpage", 293 "source_canonical_revision": "v1", 294 "source_cluster": "east", 295 "destination_cluster": "east", 296 "destination_service_namespace": "bookinfo", 297 "destination_service": "details:9080", 298 "destination_service_name": "details", 299 "destination_workload_namespace": "bookinfo", 300 "destination_workload": "details-v1", 301 "destination_canonical_service": "details", 302 "destination_canonical_revision": "v1", 303 "request_protocol": "http", 304 "response_code": "200", 305 "grpc_response_status": "0", 306 "response_flags": "-", 307 } 308 q1m10 := model.Metric{ 309 "source_workload_namespace": "bookinfo", 310 "source_workload": "productpage-v1", 311 "source_canonical_service": "productpage", 312 "source_canonical_revision": "v1", 313 "source_cluster": "east", 314 "destination_cluster": "east", 315 "destination_service_namespace": "bookinfo", 316 "destination_service": "productpage:9080", 317 "destination_service_name": "productpage", 318 "destination_workload_namespace": "bookinfo", 319 "destination_workload": "productpage-v1", 320 "destination_canonical_service": "productpage", 321 "destination_canonical_revision": "v1", 322 "request_protocol": "http", 323 "response_code": "200", 324 "grpc_response_status": "0", 325 "response_flags": "-", 326 } 327 q1m11 := model.Metric{ 328 "source_workload_namespace": "bookinfo", 329 "source_workload": "reviews-v2", 330 "source_canonical_service": "reviews", 331 "source_canonical_revision": "v2", 332 "source_cluster": "east", 333 "destination_cluster": "east", 334 "destination_service_namespace": "bookinfo", 335 "destination_service": "ratings:9080", 336 "destination_service_name": "ratings", 337 "destination_workload_namespace": "bookinfo", 338 "destination_workload": "ratings-v1", 339 "destination_canonical_service": "ratings", 340 "destination_canonical_revision": "v1", 341 "request_protocol": "http", 342 "response_code": "200", 343 "grpc_response_status": "0", 344 "response_flags": "-", 345 } 346 q1m12 := model.Metric{ 347 "source_workload_namespace": "bookinfo", 348 "source_workload": "reviews-v2", 349 "source_canonical_service": "reviews", 350 "source_canonical_revision": "v2", 351 "source_cluster": "east", 352 "destination_cluster": "east", 353 "destination_service_namespace": "bookinfo", 354 "destination_service": "ratings:9080", 355 "destination_service_name": "ratings", 356 "destination_workload_namespace": "bookinfo", 357 "destination_workload": "ratings-v1", 358 "destination_canonical_service": "ratings", 359 "destination_canonical_revision": "v1", 360 "request_protocol": "http", 361 "response_code": "500", 362 "response_flags": "-", 363 } 364 q1m13 := model.Metric{ 365 "source_workload_namespace": "bookinfo", 366 "source_workload": "reviews-v2", 367 "source_canonical_service": "reviews", 368 "source_canonical_revision": "v2", 369 "source_cluster": "east", 370 "destination_cluster": "east", 371 "destination_service_namespace": "bookinfo", 372 "destination_service": "reviews:9080", 373 "destination_service_name": "reviews", 374 "destination_workload_namespace": "bookinfo", 375 "destination_workload": "reviews-v2", 376 "destination_canonical_service": "reviews", 377 "destination_canonical_revision": "v2", 378 "request_protocol": "http", 379 "response_code": "200", 380 "grpc_response_status": "0", 381 "response_flags": "-", 382 } 383 q1m14 := model.Metric{ 384 "source_workload_namespace": "bookinfo", 385 "source_workload": "reviews-v3", 386 "source_canonical_service": "reviews", 387 "source_canonical_revision": "v3", 388 "source_cluster": "east", 389 "destination_cluster": "east", 390 "destination_service_namespace": "bookinfo", 391 "destination_service": "ratings:9080", 392 "destination_service_name": "ratings", 393 "destination_workload_namespace": "bookinfo", 394 "destination_workload": "ratings-v1", 395 "destination_canonical_service": "ratings", 396 "destination_canonical_revision": "v1", 397 "request_protocol": "http", 398 "response_code": "200", 399 "grpc_response_status": "0", 400 "response_flags": "-", 401 } 402 q1m15 := model.Metric{ 403 "source_workload_namespace": "bookinfo", 404 "source_workload": "reviews-v3", 405 "source_canonical_service": "reviews", 406 "source_canonical_revision": "v3", 407 "source_cluster": "east", 408 "destination_cluster": "east", 409 "destination_service_namespace": "bookinfo", 410 "destination_service": "ratings:9080", 411 "destination_service_name": "ratings", 412 "destination_workload_namespace": "bookinfo", 413 "destination_workload": "ratings-v1", 414 "destination_canonical_service": "ratings", 415 "destination_canonical_revision": "v1", 416 "request_protocol": "http", 417 "response_code": "500", 418 "response_flags": "-", 419 } 420 q1m16 := model.Metric{ 421 "source_workload_namespace": "bookinfo", 422 "source_workload": "reviews-v3", 423 "source_canonical_service": "reviews", 424 "source_canonical_revision": "v3", 425 "source_cluster": "east", 426 "destination_cluster": "east", 427 "destination_service_namespace": "bookinfo", 428 "destination_service": "reviews:9080", 429 "destination_service_name": "reviews", 430 "destination_workload_namespace": "bookinfo", 431 "destination_workload": "reviews-v3", 432 "destination_canonical_service": "reviews", 433 "destination_canonical_revision": "v3", 434 "request_protocol": "http", 435 "response_code": "200", 436 "grpc_response_status": "0", 437 "response_flags": "-", 438 } 439 v1 := model.Vector{ 440 &model.Sample{ 441 Metric: q1m0, 442 Value: 100, 443 }, 444 &model.Sample{ 445 Metric: q1m1, 446 Value: 50, 447 }, 448 &model.Sample{ 449 Metric: q1m2, 450 Value: 50, 451 }, 452 &model.Sample{ 453 Metric: q1m3, 454 Value: 20, 455 }, 456 &model.Sample{ 457 Metric: q1m4, 458 Value: 20, 459 }, 460 &model.Sample{ 461 Metric: q1m5, 462 Value: 20, 463 }, 464 &model.Sample{ 465 Metric: q1m6, 466 Value: 20, 467 }, 468 &model.Sample{ 469 Metric: q1m7, 470 Value: 20, 471 }, 472 &model.Sample{ 473 Metric: q1m8, 474 Value: 20, 475 }, 476 &model.Sample{ 477 Metric: q1m9, 478 Value: 20, 479 }, 480 &model.Sample{ 481 Metric: q1m10, 482 Value: 20, 483 }, 484 &model.Sample{ 485 Metric: q1m11, 486 Value: 20, 487 }, 488 &model.Sample{ 489 Metric: q1m12, 490 Value: 10, 491 }, 492 &model.Sample{ 493 Metric: q1m13, 494 Value: 20, 495 }, 496 &model.Sample{ 497 Metric: q1m14, 498 Value: 20, 499 }, 500 &model.Sample{ 501 Metric: q1m15, 502 Value: 10, 503 }, 504 &model.Sample{ 505 Metric: q1m16, 506 Value: 20, 507 }, 508 } 509 510 q2 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 511 q2m0 := model.Metric{ 512 "source_workload_namespace": "bookinfo", 513 "source_workload": "productpage-v1", 514 "source_canonical_service": "productpage", 515 "source_canonical_revision": "v1", 516 "source_cluster": "east", 517 "destination_cluster": "east", 518 "destination_service_namespace": "bookinfo", 519 "destination_service": "reviews:9080", 520 "destination_service_name": "reviews", 521 "destination_workload_namespace": "bookinfo", 522 "destination_workload": "reviews-v1", 523 "destination_canonical_service": "reviews", 524 "destination_canonical_revision": "v1", 525 "request_protocol": "http", 526 "response_code": "200", 527 "grpc_response_status": "0", 528 "response_flags": "-", 529 } 530 q2m1 := model.Metric{ 531 "source_workload_namespace": "bookinfo", 532 "source_workload": "productpage-v1", 533 "source_canonical_service": "productpage", 534 "source_canonical_revision": "v1", 535 "source_cluster": "east", 536 "destination_cluster": "east", 537 "destination_service_namespace": "bookinfo", 538 "destination_service": "reviews:9080", 539 "destination_service_name": "reviews", 540 "destination_workload_namespace": "bookinfo", 541 "destination_workload": "reviews-v2", 542 "destination_canonical_service": "reviews", 543 "destination_canonical_revision": "v2", 544 "request_protocol": "http", 545 "response_code": "200", 546 "grpc_response_status": "0", 547 "response_flags": "-", 548 } 549 q2m2 := model.Metric{ 550 "source_workload_namespace": "bookinfo", 551 "source_workload": "productpage-v1", 552 "source_canonical_service": "productpage", 553 "source_canonical_revision": "v1", 554 "source_cluster": "east", 555 "destination_cluster": "east", 556 "destination_service_namespace": "bookinfo", 557 "destination_service": "reviews:9080", 558 "destination_service_name": "reviews", 559 "destination_workload_namespace": "bookinfo", 560 "destination_workload": "reviews-v3", 561 "destination_canonical_service": "reviews", 562 "destination_canonical_revision": "v3", 563 "request_protocol": "http", 564 "response_code": "200", 565 "grpc_response_status": "0", 566 "response_flags": "-", 567 } 568 q2m3 := model.Metric{ 569 "source_workload_namespace": "bookinfo", 570 "source_workload": "productpage-v1", 571 "source_canonical_service": "productpage", 572 "source_canonical_revision": "v1", 573 "source_cluster": "east", 574 "destination_cluster": "east", 575 "destination_service_namespace": "bookinfo", 576 "destination_service": "details:9080", 577 "destination_service_name": "details", 578 "destination_workload_namespace": "bookinfo", 579 "destination_workload": "details-v1", 580 "destination_canonical_service": "details", 581 "destination_canonical_revision": "v1", 582 "request_protocol": "http", 583 "response_code": "300", 584 "response_flags": "-", 585 } 586 q2m4 := model.Metric{ 587 "source_workload_namespace": "bookinfo", 588 "source_workload": "productpage-v1", 589 "source_canonical_service": "productpage", 590 "source_canonical_revision": "v1", 591 "source_cluster": "east", 592 "destination_cluster": "east", 593 "destination_service_namespace": "bookinfo", 594 "destination_service": "details:9080", 595 "destination_service_name": "details", 596 "destination_workload_namespace": "bookinfo", 597 "destination_workload": "details-v1", 598 "destination_canonical_service": "details", 599 "destination_canonical_revision": "v1", 600 "request_protocol": "http", 601 "response_code": "400", 602 "response_flags": "-", 603 } 604 q2m5 := model.Metric{ 605 "source_workload_namespace": "bookinfo", 606 "source_workload": "productpage-v1", 607 "source_canonical_service": "productpage", 608 "source_canonical_revision": "v1", 609 "source_cluster": "east", 610 "destination_cluster": "east", 611 "destination_service_namespace": "bookinfo", 612 "destination_service": "details:9080", 613 "destination_service_name": "details", 614 "destination_workload_namespace": "bookinfo", 615 "destination_workload": "details-v1", 616 "destination_canonical_service": "details", 617 "destination_canonical_revision": "v1", 618 "request_protocol": "http", 619 "response_code": "500", 620 "response_flags": "-", 621 } 622 q2m6 := model.Metric{ 623 "source_workload_namespace": "bookinfo", 624 "source_workload": "productpage-v1", 625 "source_canonical_service": "productpage", 626 "source_canonical_revision": "v1", 627 "source_cluster": "east", 628 "destination_cluster": "east", 629 "destination_service_namespace": "bookinfo", 630 "destination_service": "details:9080", 631 "destination_service_name": "details", 632 "destination_workload_namespace": "bookinfo", 633 "destination_workload": "details-v1", 634 "destination_canonical_service": "details", 635 "destination_canonical_revision": "v1", 636 "request_protocol": "http", 637 "response_code": "200", 638 "grpc_response_status": "0", 639 "response_flags": "-", 640 } 641 q2m7 := model.Metric{ 642 "source_workload_namespace": "bookinfo", 643 "source_workload": "productpage-v1", 644 "source_canonical_service": "productpage", 645 "source_canonical_revision": "v1", 646 "source_cluster": "east", 647 "destination_cluster": "east", 648 "destination_service_namespace": "bookinfo", 649 "destination_service": "productpage:9080", 650 "destination_service_name": "productpage", 651 "destination_workload_namespace": "bookinfo", 652 "destination_workload": "productpage-v1", 653 "destination_canonical_service": "productpage", 654 "destination_canonical_revision": "v1", 655 "request_protocol": "http", 656 "response_code": "200", 657 "grpc_response_status": "0", 658 "response_flags": "-", 659 } 660 q2m8 := model.Metric{ 661 "source_workload_namespace": "bookinfo", 662 "source_workload": "reviews-v2", 663 "source_canonical_service": "reviews", 664 "source_canonical_revision": "v2", 665 "source_cluster": "east", 666 "destination_cluster": "east", 667 "destination_service_namespace": "bookinfo", 668 "destination_service": "ratings:9080", 669 "destination_service_name": "ratings", 670 "destination_workload_namespace": "bookinfo", 671 "destination_workload": "ratings-v1", 672 "destination_canonical_service": "ratings", 673 "destination_canonical_revision": "v1", 674 "request_protocol": "http", 675 "response_code": "200", 676 "grpc_response_status": "0", 677 "response_flags": "-", 678 } 679 q2m9 := model.Metric{ 680 "source_workload_namespace": "bookinfo", 681 "source_workload": "reviews-v2", 682 "source_canonical_service": "reviews", 683 "source_canonical_revision": "v2", 684 "source_cluster": "east", 685 "destination_cluster": "east", 686 "destination_service_namespace": "bookinfo", 687 "destination_service": "ratings:9080", 688 "destination_service_name": "ratings", 689 "destination_workload_namespace": "bookinfo", 690 "destination_workload": "ratings-v1", 691 "destination_canonical_service": "ratings", 692 "destination_canonical_revision": "v1", 693 "request_protocol": "http", 694 "response_code": "500", 695 "response_flags": "-", 696 } 697 q2m10 := model.Metric{ 698 "source_workload_namespace": "bookinfo", 699 "source_workload": "reviews-v2", 700 "source_canonical_service": "reviews", 701 "source_canonical_revision": "v2", 702 "source_cluster": "east", 703 "destination_cluster": "east", 704 "destination_service_namespace": "bookinfo", 705 "destination_service": "reviews:9080", 706 "destination_service_name": "reviews", 707 "destination_workload_namespace": "bookinfo", 708 "destination_workload": "reviews-v2", 709 "destination_canonical_service": "reviews", 710 "destination_canonical_revision": "v2", 711 "request_protocol": "http", 712 "response_code": "200", 713 "grpc_response_status": "0", 714 "response_flags": "-", 715 } 716 q2m11 := model.Metric{ 717 "source_workload_namespace": "bookinfo", 718 "source_workload": "reviews-v3", 719 "source_canonical_service": "reviews", 720 "source_canonical_revision": "v3", 721 "source_cluster": "east", 722 "destination_cluster": "east", 723 "destination_service_namespace": "bookinfo", 724 "destination_service": "ratings:9080", 725 "destination_service_name": "ratings", 726 "destination_workload_namespace": "bookinfo", 727 "destination_workload": "ratings-v1", 728 "destination_canonical_service": "ratings", 729 "destination_canonical_revision": "v1", 730 "request_protocol": "http", 731 "response_code": "200", 732 "grpc_response_status": "0", 733 "response_flags": "-", 734 } 735 q2m12 := model.Metric{ 736 "source_workload_namespace": "bookinfo", 737 "source_workload": "reviews-v3", 738 "source_canonical_service": "reviews", 739 "source_canonical_revision": "v3", 740 "source_cluster": "east", 741 "destination_cluster": "east", 742 "destination_service_namespace": "bookinfo", 743 "destination_service": "ratings:9080", 744 "destination_service_name": "ratings", 745 "destination_workload_namespace": "bookinfo", 746 "destination_workload": "ratings-v1", 747 "destination_canonical_service": "ratings", 748 "destination_canonical_revision": "v1", 749 "request_protocol": "http", 750 "response_code": "500", 751 "response_flags": "-", 752 } 753 q2m13 := model.Metric{ 754 "source_workload_namespace": "bookinfo", 755 "source_workload": "reviews-v3", 756 "source_canonical_service": "reviews", 757 "source_canonical_revision": "v3", 758 "source_cluster": "east", 759 "destination_cluster": "east", 760 "destination_service_namespace": "bookinfo", 761 "destination_service": "reviews:9080", 762 "destination_service_name": "reviews", 763 "destination_workload_namespace": "bookinfo", 764 "destination_workload": "reviews-v3", 765 "destination_canonical_service": "reviews", 766 "destination_canonical_revision": "v3", 767 "request_protocol": "http", 768 "response_code": "200", 769 "grpc_response_status": "0", 770 "response_flags": "-", 771 } 772 q2m14 := model.Metric{ 773 "source_workload_namespace": "bookinfo", 774 "source_workload": "reviews-v3", 775 "source_canonical_service": "reviews", 776 "source_canonical_revision": "v3", 777 "source_cluster": "east", 778 "destination_cluster": "east", 779 "destination_service_namespace": "bankapp", 780 "destination_service": "pricing:9080", 781 "destination_service_name": "pricing", 782 "destination_workload_namespace": "bankapp", 783 "destination_workload": "pricing-v1", 784 "destination_canonical_service": "pricing", 785 "destination_canonical_revision": "v1", 786 "request_protocol": "http", 787 "response_code": "200", 788 "grpc_response_status": "0", 789 "response_flags": "-", 790 } 791 q2m15 := model.Metric{ 792 "source_workload_namespace": "bookinfo", 793 "source_workload": "reviews-v3", 794 "source_canonical_service": "reviews", 795 "source_canonical_revision": "v3", 796 "source_cluster": "east", 797 "destination_cluster": "unknown", 798 "destination_service_namespace": "unknown", 799 "destination_service": "unknown", 800 "destination_service_name": "unknown", 801 "destination_workload_namespace": "unknown", 802 "destination_workload": "unknown", 803 "destination_canonical_service": "unknown", 804 "destination_canonical_revision": "unknown", 805 "request_protocol": "http", 806 "response_code": "404", 807 "response_flags": "NR", 808 } 809 q2m16 := model.Metric{ 810 "source_workload_namespace": "bookinfo", 811 "source_workload": "reviews-v3", 812 "source_canonical_service": "reviews", 813 "source_canonical_revision": "v3", 814 "source_cluster": "east", 815 "destination_cluster": "east", 816 "destination_service_namespace": "bankapp", 817 "destination_service": "deposit:9080", 818 "destination_service_name": "deposit", 819 "destination_workload_namespace": "bankapp", 820 "destination_workload": "deposit-v1", 821 "destination_canonical_service": "deposit", 822 "destination_canonical_revision": "v1", 823 "request_protocol": "grpc", 824 "response_code": "200", 825 "grpc_response_status": "0", 826 "response_flags": "-", 827 } 828 v2 := model.Vector{ 829 &model.Sample{ 830 Metric: q2m0, 831 Value: 20, 832 }, 833 &model.Sample{ 834 Metric: q2m1, 835 Value: 20, 836 }, 837 &model.Sample{ 838 Metric: q2m2, 839 Value: 20, 840 }, 841 &model.Sample{ 842 Metric: q2m3, 843 Value: 20, 844 }, 845 &model.Sample{ 846 Metric: q2m4, 847 Value: 20, 848 }, 849 &model.Sample{ 850 Metric: q2m5, 851 Value: 20, 852 }, 853 &model.Sample{ 854 Metric: q2m6, 855 Value: 20, 856 }, 857 &model.Sample{ 858 Metric: q2m7, 859 Value: 20, 860 }, 861 &model.Sample{ 862 Metric: q2m8, 863 Value: 20, 864 }, 865 &model.Sample{ 866 Metric: q2m9, 867 Value: 10, 868 }, 869 &model.Sample{ 870 Metric: q2m10, 871 Value: 20, 872 }, 873 &model.Sample{ 874 Metric: q2m11, 875 Value: 20, 876 }, 877 &model.Sample{ 878 Metric: q2m12, 879 Value: 10, 880 }, 881 &model.Sample{ 882 Metric: q2m13, 883 Value: 20, 884 }, 885 &model.Sample{ 886 Metric: q2m14, 887 Value: 20, 888 }, 889 &model.Sample{ 890 Metric: q2m15, 891 Value: 4, 892 }, 893 &model.Sample{ 894 Metric: q2m16, 895 Value: 50, 896 }, 897 } 898 899 q3 := `round(sum(rate(istio_tcp_sent_bytes_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 900 v3 := model.Vector{} 901 902 q4 := `round(sum(rate(istio_tcp_sent_bytes_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 903 q4m0 := model.Metric{ 904 "source_workload_namespace": "istio-system", 905 "source_workload": "ingressgateway-unknown", 906 "source_canonical_service": "ingressgateway", 907 "source_canonical_revision": "latest", 908 "source_cluster": "east", 909 "destination_cluster": "east", 910 "destination_service_namespace": "bookinfo", 911 "destination_service": "tcp:9080", 912 "destination_service_name": "tcp", 913 "destination_workload_namespace": "bookinfo", 914 "destination_workload": "tcp-v1", 915 "destination_canonical_service": "tcp", 916 "destination_canonical_revision": "v1", 917 "response_flags": "-", 918 } 919 q4m1 := model.Metric{ 920 "source_workload_namespace": "unknown", 921 "source_workload": "unknown", 922 "source_canonical_service": "unknown", 923 "source_canonical_revision": "unknown", 924 "source_cluster": "unknown", 925 "destination_cluster": "east", 926 "destination_service_namespace": "bookinfo", 927 "destination_service": "tcp:9080", 928 "destination_service_name": "tcp", 929 "destination_workload_namespace": "bookinfo", 930 "destination_workload": "tcp-v1", 931 "destination_canonical_service": "tcp", 932 "destination_canonical_revision": "v1", 933 "response_flags": "-", 934 } 935 q4m2 := model.Metric{ 936 "source_workload_namespace": "bookinfo", 937 "source_workload": "productpage-v1", 938 "source_canonical_service": "productpage", 939 "source_canonical_revision": "v1", 940 "source_cluster": "east", 941 "destination_cluster": "east", 942 "destination_service_namespace": "bookinfo", 943 "destination_service": "tcp:9080", 944 "destination_service_name": "tcp", 945 "destination_workload_namespace": "bookinfo", 946 "destination_workload": "tcp-v1", 947 "destination_canonical_service": "tcp", 948 "destination_canonical_revision": "v1", 949 "response_flags": "-", 950 } 951 v4 := model.Vector{ 952 &model.Sample{ 953 Metric: q4m0, 954 Value: 150, 955 }, 956 &model.Sample{ 957 Metric: q4m1, 958 Value: 400, 959 }, 960 &model.Sample{ 961 Metric: q4m2, 962 Value: 31, 963 }, 964 } 965 966 q5 := `round(sum(rate(istio_tcp_sent_bytes_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 967 q5m0 := model.Metric{ 968 "source_workload_namespace": "bookinfo", 969 "source_workload": "productpage-v1", 970 "source_canonical_service": "productpage", 971 "source_canonical_revision": "v1", 972 "source_cluster": "east", 973 "destination_cluster": "east", 974 "destination_service_namespace": "bookinfo", 975 "destination_service": "tcp:9080", 976 "destination_service_name": "tcp", 977 "destination_workload_namespace": "bookinfo", 978 "destination_workload": "tcp-v1", 979 "destination_canonical_service": "tcp", 980 "destination_canonical_revision": "v1", 981 "response_flags": "-", 982 } 983 v5 := model.Vector{ 984 &model.Sample{ 985 Metric: q5m0, 986 Value: 31, 987 }, 988 } 989 990 client, api := setupMocked(t) 991 992 mockQuery(api, q0, &v0) 993 mockQuery(api, q1, &v1) 994 mockQuery(api, q2, &v2) 995 mockQuery(api, q3, &v3) 996 mockQuery(api, q4, &v4) 997 mockQuery(api, q5, &v5) 998 999 return client, api, nil 1000 } 1001 1002 // mockNamespaceRatesGraph adds additional queries to mockNamespaceGraph to test non-default rates for graph-gen. Basic approach 1003 // is for "sent" to use the same traffic/rates as is done for the default traffic. This produces the same rates (and nearly the 1004 // same graph as for the defaults). Use double the rates for "received". And so "total" should be triple the "sent" rates. 1005 func mockNamespaceRatesGraph(t *testing.T) (*prometheus.Client, *prometheustest.PromAPIMock, error) { 1006 client, api, err := mockNamespaceGraph(t) 1007 if err != nil { 1008 return client, api, err 1009 } 1010 1011 q6 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 1012 v6 := model.Vector{} 1013 1014 q7 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 1015 q7m0 := model.Metric{ 1016 "source_workload_namespace": "istio-system", 1017 "source_workload": "ingressgateway-unknown", 1018 "source_canonical_service": "ingressgateway", 1019 "source_canonical_revision": "latest", 1020 "source_cluster": "east", 1021 "destination_cluster": "east", 1022 "destination_service_namespace": "bookinfo", 1023 "destination_service": "tcp:9080", 1024 "destination_service_name": "tcp", 1025 "destination_workload_namespace": "bookinfo", 1026 "destination_workload": "tcp-v1", 1027 "destination_canonical_service": "tcp", 1028 "destination_canonical_revision": "v1", 1029 "response_flags": "-", 1030 } 1031 q7m1 := model.Metric{ 1032 "source_workload_namespace": "unknown", 1033 "source_workload": "unknown", 1034 "source_canonical_service": "unknown", 1035 "source_canonical_revision": "unknown", 1036 "source_cluster": "unknown", 1037 "destination_cluster": "east", 1038 "destination_service_namespace": "bookinfo", 1039 "destination_service": "tcp:9080", 1040 "destination_service_name": "tcp", 1041 "destination_workload_namespace": "bookinfo", 1042 "destination_workload": "tcp-v1", 1043 "destination_canonical_service": "tcp", 1044 "destination_canonical_revision": "v1", 1045 "response_flags": "-", 1046 } 1047 q7m2 := model.Metric{ 1048 "source_workload_namespace": "bookinfo", 1049 "source_workload": "productpage-v1", 1050 "source_canonical_service": "productpage", 1051 "source_canonical_revision": "v1", 1052 "source_cluster": "east", 1053 "destination_cluster": "east", 1054 "destination_service_namespace": "bookinfo", 1055 "destination_service": "tcp:9080", 1056 "destination_service_name": "tcp", 1057 "destination_workload_namespace": "bookinfo", 1058 "destination_workload": "tcp-v1", 1059 "destination_canonical_service": "tcp", 1060 "destination_canonical_revision": "v1", 1061 "response_flags": "-", 1062 } 1063 v7 := model.Vector{ 1064 &model.Sample{ 1065 Metric: q7m0, 1066 Value: 300, 1067 }, 1068 &model.Sample{ 1069 Metric: q7m1, 1070 Value: 800, 1071 }, 1072 &model.Sample{ 1073 Metric: q7m2, 1074 Value: 62, 1075 }, 1076 } 1077 1078 q8 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 1079 q8m0 := model.Metric{ 1080 "source_workload_namespace": "bookinfo", 1081 "source_workload": "productpage-v1", 1082 "source_canonical_service": "productpage", 1083 "source_canonical_revision": "v1", 1084 "source_cluster": "east", 1085 "destination_cluster": "east", 1086 "destination_service_namespace": "bookinfo", 1087 "destination_service": "tcp:9080", 1088 "destination_service_name": "tcp", 1089 "destination_workload_namespace": "bookinfo", 1090 "destination_workload": "tcp-v1", 1091 "destination_canonical_service": "tcp", 1092 "destination_canonical_revision": "v1", 1093 "response_flags": "-", 1094 } 1095 v8 := model.Vector{ 1096 &model.Sample{ 1097 Metric: q8m0, 1098 Value: 62, 1099 }, 1100 } 1101 1102 q9 := `round(sum(rate(istio_request_messages_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1103 v9 := model.Vector{} 1104 1105 q10 := `round(sum(rate(istio_request_messages_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1106 v10 := model.Vector{} 1107 1108 q11 := `round(sum(rate(istio_request_messages_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1109 q11m0 := model.Metric{ 1110 "source_workload_namespace": "bookinfo", 1111 "source_workload": "reviews-v3", 1112 "source_canonical_service": "reviews", 1113 "source_canonical_revision": "v3", 1114 "source_cluster": "east", 1115 "destination_cluster": "east", 1116 "destination_service_namespace": "bankapp", 1117 "destination_service": "deposit:9080", 1118 "destination_service_name": "deposit", 1119 "destination_workload_namespace": "bankapp", 1120 "destination_workload": "deposit-v1", 1121 "destination_canonical_service": "deposit", 1122 "destination_canonical_revision": "v1", 1123 } 1124 v11 := model.Vector{ 1125 &model.Sample{ 1126 Metric: q11m0, 1127 Value: 50, 1128 }, 1129 } 1130 1131 q12 := `round(sum(rate(istio_response_messages_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1132 v12 := model.Vector{} 1133 1134 q13 := `round(sum(rate(istio_response_messages_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1135 v13 := model.Vector{} 1136 1137 q14 := `round(sum(rate(istio_response_messages_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 1138 q14m0 := model.Metric{ 1139 "source_workload_namespace": "bookinfo", 1140 "source_workload": "reviews-v3", 1141 "source_canonical_service": "reviews", 1142 "source_canonical_revision": "v3", 1143 "source_cluster": "east", 1144 "destination_cluster": "east", 1145 "destination_service_namespace": "bankapp", 1146 "destination_service": "deposit:9080", 1147 "destination_service_name": "deposit", 1148 "destination_workload_namespace": "bankapp", 1149 "destination_workload": "deposit-v1", 1150 "destination_canonical_service": "deposit", 1151 "destination_canonical_revision": "v1", 1152 } 1153 v14 := model.Vector{ 1154 &model.Sample{ 1155 Metric: q14m0, 1156 Value: 100, 1157 }, 1158 } 1159 1160 mockQuery(api, q6, &v6) 1161 mockQuery(api, q7, &v7) 1162 mockQuery(api, q8, &v8) 1163 mockQuery(api, q9, &v9) 1164 mockQuery(api, q10, &v10) 1165 mockQuery(api, q11, &v11) 1166 mockQuery(api, q12, &v12) 1167 mockQuery(api, q13, &v13) 1168 mockQuery(api, q14, &v14) 1169 1170 return client, api, nil 1171 } 1172 1173 func respond(w http.ResponseWriter, code int, payload interface{}) { 1174 response, err := json.MarshalIndent(payload, "", " ") 1175 if err != nil { 1176 response = []byte(err.Error()) 1177 code = http.StatusInternalServerError 1178 } 1179 1180 w.Header().Set("Content-Type", "application/json") 1181 w.WriteHeader(code) 1182 _, _ = w.Write(response) 1183 } 1184 1185 // Helper method that tests the objects are equal and if they aren't will 1186 // unmarshal them into a json object and diff them. This way the output of the failure 1187 // is actually useful. Otherwise printing the byte slice results is incomprehensible. 1188 func assertObjectsEqual(t *testing.T, expected, actual []byte) { 1189 if !assert.ObjectsAreEqual(expected, actual) { 1190 t.Log("Actual response does not equal expected golden copy. If you've updated the golden copy, ensure it ends with a newline.") 1191 t.Fail() 1192 1193 var ( 1194 ev any 1195 av any 1196 ) 1197 err := func() error { 1198 if err := json.Unmarshal(expected, &ev); err != nil { 1199 t.Logf("Failed to unmarshal expected value: %s", err) 1200 return err 1201 } 1202 1203 if err := json.Unmarshal(actual, &av); err != nil { 1204 t.Logf("Failed to unmarshal actual value: %s", err) 1205 return err 1206 } 1207 1208 return nil 1209 }() 1210 if err != nil { 1211 t.Logf("Failed to unmarshal expected or actual value. Falling back to string comparison.\nExpected: %s\nActual: %s", string(expected), string(actual)) 1212 return 1213 } 1214 1215 t.Logf("Diff: %s", cmp.Diff(ev, av)) 1216 } 1217 } 1218 1219 func TestAppGraph(t *testing.T) { 1220 client, _, err := mockNamespaceRatesGraph(t) 1221 if err != nil { 1222 t.Error(err) 1223 return 1224 } 1225 1226 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1227 1228 mr := mux.NewRouter() 1229 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1230 func(w http.ResponseWriter, r *http.Request) { 1231 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1232 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1233 respond(w, code, config) 1234 })) 1235 1236 ts := httptest.NewServer(mr) 1237 defer ts.Close() 1238 1239 fut = graphNamespacesIstio 1240 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=app&appenders&queryTime=1523364075" 1241 resp, err := http.Get(url) 1242 if err != nil { 1243 t.Fatal(err) 1244 } 1245 actual, _ := io.ReadAll(resp.Body) 1246 expected, _ := os.ReadFile("testdata/test_app_graph.expected") 1247 if runtime.GOOS == "windows" { 1248 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1249 } 1250 expected = expected[:len(expected)-1] // remove EOF byte 1251 1252 assertObjectsEqual(t, expected, actual) 1253 assert.Equal(t, 200, resp.StatusCode) 1254 } 1255 1256 func TestVersionedAppGraph(t *testing.T) { 1257 client, _, err := mockNamespaceRatesGraph(t) 1258 if err != nil { 1259 t.Error(err) 1260 return 1261 } 1262 1263 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1264 1265 mr := mux.NewRouter() 1266 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1267 func(w http.ResponseWriter, r *http.Request) { 1268 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1269 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1270 respond(w, code, config) 1271 })) 1272 1273 ts := httptest.NewServer(mr) 1274 defer ts.Close() 1275 1276 fut = graphNamespacesIstio 1277 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=versionedApp&appenders&queryTime=1523364075" 1278 resp, err := http.Get(url) 1279 if err != nil { 1280 t.Fatal(err) 1281 } 1282 actual, _ := io.ReadAll(resp.Body) 1283 expected, _ := os.ReadFile("testdata/test_versioned_app_graph.expected") 1284 if runtime.GOOS == "windows" { 1285 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1286 } 1287 expected = expected[:len(expected)-1] // remove EOF byte 1288 1289 assertObjectsEqual(t, expected, actual) 1290 assert.Equal(t, 200, resp.StatusCode) 1291 } 1292 1293 func TestServiceGraph(t *testing.T) { 1294 client, _, err := mockNamespaceRatesGraph(t) 1295 if err != nil { 1296 t.Error(err) 1297 return 1298 } 1299 1300 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1301 1302 mr := mux.NewRouter() 1303 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1304 func(w http.ResponseWriter, r *http.Request) { 1305 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1306 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1307 respond(w, code, config) 1308 })) 1309 1310 ts := httptest.NewServer(mr) 1311 defer ts.Close() 1312 1313 fut = graphNamespacesIstio 1314 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=service&appenders&queryTime=1523364075" 1315 resp, err := http.Get(url) 1316 if err != nil { 1317 t.Fatal(err) 1318 } 1319 actual, _ := io.ReadAll(resp.Body) 1320 expected, _ := os.ReadFile("testdata/test_service_graph.expected") 1321 if runtime.GOOS == "windows" { 1322 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1323 } 1324 expected = expected[:len(expected)-1] // remove EOF byte 1325 1326 assertObjectsEqual(t, expected, actual) 1327 assert.Equal(t, 200, resp.StatusCode) 1328 } 1329 1330 func TestWorkloadGraph(t *testing.T) { 1331 client, _, err := mockNamespaceRatesGraph(t) 1332 if err != nil { 1333 t.Error(err) 1334 return 1335 } 1336 1337 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1338 1339 mr := mux.NewRouter() 1340 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1341 func(w http.ResponseWriter, r *http.Request) { 1342 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1343 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1344 respond(w, code, config) 1345 })) 1346 1347 ts := httptest.NewServer(mr) 1348 defer ts.Close() 1349 1350 fut = graphNamespacesIstio 1351 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=workload&appenders&queryTime=1523364075" 1352 resp, err := http.Get(url) 1353 if err != nil { 1354 t.Fatal(err) 1355 } 1356 actual, _ := io.ReadAll(resp.Body) 1357 expected, _ := os.ReadFile("testdata/test_workload_graph.expected") 1358 if runtime.GOOS == "windows" { 1359 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1360 } 1361 expected = expected[:len(expected)-1] // remove EOF byte 1362 1363 assertObjectsEqual(t, expected, actual) 1364 assert.Equal(t, 200, resp.StatusCode) 1365 } 1366 1367 func TestRatesGraphSent(t *testing.T) { 1368 client, _, err := mockNamespaceRatesGraph(t) 1369 if err != nil { 1370 t.Error(err) 1371 return 1372 } 1373 1374 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1375 1376 mr := mux.NewRouter() 1377 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1378 func(w http.ResponseWriter, r *http.Request) { 1379 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1380 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1381 respond(w, code, config) 1382 })) 1383 1384 ts := httptest.NewServer(mr) 1385 defer ts.Close() 1386 1387 fut = graphNamespacesIstio 1388 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=workload&appenders&queryTime=1523364075&rateGrpc=sent&rateHttp=requests&rateTcp=sent" 1389 resp, err := http.Get(url) 1390 if err != nil { 1391 t.Fatal(err) 1392 } 1393 actual, _ := io.ReadAll(resp.Body) 1394 expected, _ := os.ReadFile("testdata/test_rates_sent_graph.expected") 1395 if runtime.GOOS == "windows" { 1396 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1397 } 1398 expected = expected[:len(expected)-1] // remove EOF byte 1399 1400 assertObjectsEqual(t, expected, actual) 1401 assert.Equal(t, 200, resp.StatusCode) 1402 } 1403 1404 func TestRatesGraphReceived(t *testing.T) { 1405 client, _, err := mockNamespaceRatesGraph(t) 1406 if err != nil { 1407 t.Error(err) 1408 return 1409 } 1410 1411 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1412 1413 mr := mux.NewRouter() 1414 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1415 func(w http.ResponseWriter, r *http.Request) { 1416 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1417 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1418 respond(w, code, config) 1419 })) 1420 1421 ts := httptest.NewServer(mr) 1422 defer ts.Close() 1423 1424 fut = graphNamespacesIstio 1425 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=workload&appenders&queryTime=1523364075&rateGrpc=received&rateHttp=requests&rateTcp=received" 1426 resp, err := http.Get(url) 1427 if err != nil { 1428 t.Fatal(err) 1429 } 1430 actual, _ := io.ReadAll(resp.Body) 1431 expected, _ := os.ReadFile("testdata/test_rates_received_graph.expected") 1432 if runtime.GOOS == "windows" { 1433 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1434 } 1435 expected = expected[:len(expected)-1] // remove EOF byte 1436 1437 assertObjectsEqual(t, expected, actual) 1438 assert.Equal(t, 200, resp.StatusCode) 1439 } 1440 1441 func TestRatesGraphTotal(t *testing.T) { 1442 client, _, err := mockNamespaceRatesGraph(t) 1443 if err != nil { 1444 t.Error(err) 1445 return 1446 } 1447 1448 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1449 1450 mr := mux.NewRouter() 1451 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1452 func(w http.ResponseWriter, r *http.Request) { 1453 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1454 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1455 respond(w, code, config) 1456 })) 1457 1458 ts := httptest.NewServer(mr) 1459 defer ts.Close() 1460 1461 fut = graphNamespacesIstio 1462 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=workload&appenders&queryTime=1523364075&rateGrpc=total&rateHttp=requests&rateTcp=total" 1463 resp, err := http.Get(url) 1464 if err != nil { 1465 t.Fatal(err) 1466 } 1467 actual, _ := io.ReadAll(resp.Body) 1468 expected, _ := os.ReadFile("testdata/test_rates_total_graph.expected") 1469 if runtime.GOOS == "windows" { 1470 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1471 } 1472 expected = expected[:len(expected)-1] // remove EOF byte 1473 1474 assertObjectsEqual(t, expected, actual) 1475 assert.Equal(t, 200, resp.StatusCode) 1476 } 1477 1478 func TestRatesGraphNone(t *testing.T) { 1479 client, _, err := mockNamespaceRatesGraph(t) 1480 if err != nil { 1481 t.Error(err) 1482 return 1483 } 1484 1485 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1486 1487 mr := mux.NewRouter() 1488 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 1489 func(w http.ResponseWriter, r *http.Request) { 1490 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1491 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1492 respond(w, code, config) 1493 })) 1494 1495 ts := httptest.NewServer(mr) 1496 defer ts.Close() 1497 1498 fut = graphNamespacesIstio 1499 url := ts.URL + "/api/namespaces/graph?namespaces=bookinfo&graphType=workload&appenders&queryTime=1523364075&rateGrpc=total&rateHttp=none&rateTcp=total" 1500 resp, err := http.Get(url) 1501 if err != nil { 1502 t.Fatal(err) 1503 } 1504 actual, _ := io.ReadAll(resp.Body) 1505 expected, _ := os.ReadFile("testdata/test_rates_none_graph.expected") 1506 if runtime.GOOS == "windows" { 1507 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1508 } 1509 expected = expected[:len(expected)-1] // remove EOF byte 1510 1511 assertObjectsEqual(t, expected, actual) 1512 assert.Equal(t, 200, resp.StatusCode) 1513 } 1514 1515 func TestWorkloadNodeGraph(t *testing.T) { 1516 q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 1517 q0m0 := model.Metric{ 1518 "source_workload_namespace": "unknown", 1519 "source_workload": "unknown", 1520 "source_canonical_service": "unknown", 1521 "source_canonical_revision": "unknown", 1522 "source_cluster": "unknown", 1523 "destination_cluster": "east", 1524 "destination_service_namespace": "bookinfo", 1525 "destination_service": "productpage:9080", 1526 "destination_service_name": "productpage", 1527 "destination_workload_namespace": "bookinfo", 1528 "destination_workload": "productpage-v1", 1529 "destination_canonical_service": "productpage", 1530 "destination_canonical_revision": "v1", 1531 "request_protocol": "http", 1532 "response_code": "200", 1533 "grpc_response_status": "0", 1534 "response_flags": "-", 1535 } 1536 q0m1 := model.Metric{ 1537 "source_workload_namespace": "istio-system", 1538 "source_workload": "ingressgateway-unknown", 1539 "source_canonical_service": "ingressgateway", 1540 "source_canonical_revision": "latest", 1541 "source_cluster": "east", 1542 "destination_cluster": "east", 1543 "destination_service_namespace": "bookinfo", 1544 "destination_service": "productpage:9080", 1545 "destination_service_name": "productpage", 1546 "destination_workload_namespace": "bookinfo", 1547 "destination_workload": "productpage-v1", 1548 "destination_canonical_service": "productpage", 1549 "destination_canonical_revision": "v1", 1550 "request_protocol": "http", 1551 "response_code": "200", 1552 "grpc_response_status": "0", 1553 "response_flags": "-", 1554 } 1555 v0 := model.Vector{ 1556 &model.Sample{ 1557 Metric: q0m0, 1558 Value: 50, 1559 }, 1560 &model.Sample{ 1561 Metric: q0m1, 1562 Value: 100, 1563 }, 1564 } 1565 1566 q1 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 1567 q1m0 := model.Metric{ 1568 "source_workload_namespace": "bookinfo", 1569 "source_workload": "productpage-v1", 1570 "source_canonical_service": "productpage", 1571 "source_canonical_revision": "v1", 1572 "source_cluster": "east", 1573 "destination_cluster": "east", 1574 "destination_service_namespace": "bookinfo", 1575 "destination_service": "reviews:9080", 1576 "destination_service_name": "reviews", 1577 "destination_workload_namespace": "bookinfo", 1578 "destination_workload": "reviews-v1", 1579 "destination_canonical_service": "reviews", 1580 "destination_canonical_revision": "v1", 1581 "request_protocol": "http", 1582 "response_code": "200", 1583 "grpc_response_status": "0", 1584 "response_flags": "-", 1585 } 1586 q1m1 := model.Metric{ 1587 "source_workload_namespace": "bookinfo", 1588 "source_workload": "productpage-v1", 1589 "source_canonical_service": "productpage", 1590 "source_canonical_revision": "v1", 1591 "source_cluster": "east", 1592 "destination_cluster": "east", 1593 "destination_service_namespace": "bookinfo", 1594 "destination_service": "reviews:9080", 1595 "destination_service_name": "reviews", 1596 "destination_workload_namespace": "bookinfo", 1597 "destination_workload": "reviews-v2", 1598 "destination_canonical_service": "reviews", 1599 "destination_canonical_revision": "v2", 1600 "request_protocol": "http", 1601 "response_code": "200", 1602 "grpc_response_status": "0", 1603 "response_flags": "-", 1604 } 1605 q1m2 := model.Metric{ 1606 "source_workload_namespace": "bookinfo", 1607 "source_workload": "productpage-v1", 1608 "source_canonical_service": "productpage", 1609 "source_canonical_revision": "v1", 1610 "source_cluster": "east", 1611 "destination_cluster": "east", 1612 "destination_service_namespace": "bookinfo", 1613 "destination_service": "reviews:9080", 1614 "destination_service_name": "reviews", 1615 "destination_workload_namespace": "bookinfo", 1616 "destination_workload": "reviews-v3", 1617 "destination_canonical_service": "reviews", 1618 "destination_canonical_revision": "v3", 1619 "request_protocol": "http", 1620 "response_code": "200", 1621 "grpc_response_status": "0", 1622 "response_flags": "-", 1623 } 1624 q1m3 := model.Metric{ 1625 "source_workload_namespace": "bookinfo", 1626 "source_workload": "productpage-v1", 1627 "source_canonical_service": "productpage", 1628 "source_canonical_revision": "v1", 1629 "source_cluster": "east", 1630 "destination_cluster": "east", 1631 "destination_service_namespace": "bookinfo", 1632 "destination_service": "details:9080", 1633 "destination_service_name": "details", 1634 "destination_workload_namespace": "bookinfo", 1635 "destination_workload": "details-v1", 1636 "destination_canonical_service": "details", 1637 "destination_canonical_revision": "v1", 1638 "request_protocol": "http", 1639 "response_code": "300", 1640 "response_flags": "-", 1641 } 1642 q1m4 := model.Metric{ 1643 "source_workload_namespace": "bookinfo", 1644 "source_workload": "productpage-v1", 1645 "source_canonical_service": "productpage", 1646 "source_canonical_revision": "v1", 1647 "source_cluster": "east", 1648 "destination_cluster": "east", 1649 "destination_service_namespace": "bookinfo", 1650 "destination_service": "details:9080", 1651 "destination_service_name": "details", 1652 "destination_workload_namespace": "bookinfo", 1653 "destination_workload": "details-v1", 1654 "destination_canonical_service": "details", 1655 "destination_canonical_revision": "v1", 1656 "request_protocol": "http", 1657 "response_code": "400", 1658 "response_flags": "-", 1659 } 1660 q1m5 := model.Metric{ 1661 "source_workload_namespace": "bookinfo", 1662 "source_workload": "productpage-v1", 1663 "source_canonical_service": "productpage", 1664 "source_canonical_revision": "v1", 1665 "source_cluster": "east", 1666 "destination_cluster": "east", 1667 "destination_service_namespace": "bookinfo", 1668 "destination_service": "details:9080", 1669 "destination_service_name": "details", 1670 "destination_workload_namespace": "bookinfo", 1671 "destination_workload": "details-v1", 1672 "destination_canonical_service": "details", 1673 "destination_canonical_revision": "v1", 1674 "request_protocol": "http", 1675 "response_code": "500", 1676 "response_flags": "-", 1677 } 1678 q1m6 := model.Metric{ 1679 "source_workload_namespace": "bookinfo", 1680 "source_workload": "productpage-v1", 1681 "source_canonical_service": "productpage", 1682 "source_canonical_revision": "v1", 1683 "source_cluster": "east", 1684 "destination_cluster": "east", 1685 "destination_service_namespace": "bookinfo", 1686 "destination_service": "details:9080", 1687 "destination_service_name": "details", 1688 "destination_workload_namespace": "bookinfo", 1689 "destination_workload": "details-v1", 1690 "destination_canonical_service": "details", 1691 "destination_canonical_revision": "v1", 1692 "request_protocol": "http", 1693 "response_code": "200", 1694 "grpc_response_status": "0", 1695 "response_flags": "-", 1696 } 1697 q1m7 := model.Metric{ 1698 "source_workload_namespace": "bookinfo", 1699 "source_workload": "productpage-v1", 1700 "source_canonical_service": "productpage", 1701 "source_canonical_revision": "v1", 1702 "source_cluster": "east", 1703 "destination_cluster": "east", 1704 "destination_service_namespace": "bookinfo", 1705 "destination_service": "productpage:9080", 1706 "destination_service_name": "productpage", 1707 "destination_workload_namespace": "bookinfo", 1708 "destination_workload": "productpage-v1", 1709 "destination_canonical_service": "productpage", 1710 "destination_canonical_revision": "v1", 1711 "request_protocol": "http", 1712 "response_code": "200", 1713 "grpc_response_status": "0", 1714 "response_flags": "-", 1715 } 1716 q1m8 := model.Metric{ 1717 "source_workload_namespace": "bookinfo", 1718 "source_workload": "productpage-v1", 1719 "source_canonical_service": "productpage", 1720 "source_canonical_revision": "v1", 1721 "source_cluster": "east", 1722 "destination_cluster": "unknown", 1723 "destination_service_namespace": "unknown", 1724 "destination_service": "unknown", 1725 "destination_service_name": "unknown", 1726 "destination_workload_namespace": "unknown", 1727 "destination_workload": "unknown", 1728 "destination_canonical_service": "unknown", 1729 "destination_canonical_revision": "unknown", 1730 "request_protocol": "http", 1731 "response_code": "404", 1732 "response_flags": "NR", 1733 } 1734 v1 := model.Vector{ 1735 &model.Sample{ 1736 Metric: q1m0, 1737 Value: 20, 1738 }, 1739 &model.Sample{ 1740 Metric: q1m1, 1741 Value: 20, 1742 }, 1743 &model.Sample{ 1744 Metric: q1m2, 1745 Value: 20, 1746 }, 1747 &model.Sample{ 1748 Metric: q1m3, 1749 Value: 20, 1750 }, 1751 &model.Sample{ 1752 Metric: q1m4, 1753 Value: 20, 1754 }, 1755 &model.Sample{ 1756 Metric: q1m5, 1757 Value: 20, 1758 }, 1759 &model.Sample{ 1760 Metric: q1m6, 1761 Value: 20, 1762 }, 1763 &model.Sample{ 1764 Metric: q1m7, 1765 Value: 20, 1766 }, 1767 &model.Sample{ 1768 Metric: q1m8, 1769 Value: 4, 1770 }, 1771 } 1772 1773 q2 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 1774 v2 := model.Vector{} 1775 1776 q3 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 1777 q3m0 := model.Metric{ 1778 "source_workload_namespace": "bookinfo", 1779 "source_workload": "productpage-v1", 1780 "source_canonical_service": "productpage", 1781 "source_canonical_revision": "v1", 1782 "source_cluster": "east", 1783 "destination_cluster": "east", 1784 "destination_service_namespace": "bookinfo", 1785 "destination_service": "tcp:9080", 1786 "destination_service_name": "tcp", 1787 "destination_workload_namespace": "bookinfo", 1788 "destination_workload": "tcp-v1", 1789 "destination_canonical_service": "tcp", 1790 "destination_canonical_revision": "v1", 1791 "response_flags": "-", 1792 } 1793 v3 := model.Vector{ 1794 &model.Sample{ 1795 Metric: q3m0, 1796 Value: 31, 1797 }, 1798 } 1799 1800 client, xapi := setupMocked(t) 1801 1802 mockQuery(xapi, q0, &v0) 1803 mockQuery(xapi, q1, &v1) 1804 mockQuery(xapi, q2, &v2) 1805 mockQuery(xapi, q3, &v3) 1806 1807 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 1808 1809 mr := mux.NewRouter() 1810 mr.HandleFunc("/api/namespaces/{namespace}/workloads/{workload}/graph", http.HandlerFunc( 1811 func(w http.ResponseWriter, r *http.Request) { 1812 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 1813 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 1814 respond(w, code, config) 1815 })) 1816 1817 ts := httptest.NewServer(mr) 1818 defer ts.Close() 1819 1820 fut = graphNodeIstio 1821 url := ts.URL + "/api/namespaces/bookinfo/workloads/productpage-v1/graph?graphType=workload&appenders&queryTime=1523364075" 1822 resp, err := http.Get(url) 1823 if err != nil { 1824 t.Fatal(err) 1825 } 1826 actual, _ := io.ReadAll(resp.Body) 1827 expected, _ := os.ReadFile("testdata/test_workload_node_graph.expected") 1828 if runtime.GOOS == "windows" { 1829 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 1830 } 1831 expected = expected[:len(expected)-1] // remove EOF byte 1832 1833 assertObjectsEqual(t, expected, actual) 1834 assert.Equal(t, 200, resp.StatusCode) 1835 } 1836 1837 func TestAppNodeGraph(t *testing.T) { 1838 q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",destination_canonical_service="productpage"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 1839 q0m0 := model.Metric{ 1840 "source_workload_namespace": "unknown", 1841 "source_workload": "unknown", 1842 "source_canonical_service": "unknown", 1843 "source_canonical_revision": "unknown", 1844 "source_cluster": "unknown", 1845 "destination_cluster": "east", 1846 "destination_service_namespace": "bookinfo", 1847 "destination_service": "productpage:9080", 1848 "destination_service_name": "productpage", 1849 "destination_workload_namespace": "bookinfo", 1850 "destination_workload": "productpage-v1", 1851 "destination_canonical_service": "productpage", 1852 "destination_canonical_revision": "v1", 1853 "request_protocol": "http", 1854 "response_code": "200", 1855 "grpc_response_status": "0", 1856 "response_flags": "-", 1857 } 1858 q0m1 := model.Metric{ 1859 "source_workload_namespace": "istio-system", 1860 "source_workload": "ingressgateway-unknown", 1861 "source_canonical_service": "ingressgateway", 1862 "source_canonical_revision": "latest", 1863 "source_cluster": "east", 1864 "destination_cluster": "east", 1865 "destination_service_namespace": "bookinfo", 1866 "destination_service": "productpage:9080", 1867 "destination_service_name": "productpage", 1868 "destination_workload_namespace": "bookinfo", 1869 "destination_workload": "productpage-v1", 1870 "destination_canonical_service": "productpage", 1871 "destination_canonical_revision": "v1", 1872 "request_protocol": "http", 1873 "response_code": "200", 1874 "grpc_response_status": "0", 1875 "response_flags": "-", 1876 } 1877 v0 := model.Vector{ 1878 &model.Sample{ 1879 Metric: q0m0, 1880 Value: 50, 1881 }, 1882 &model.Sample{ 1883 Metric: q0m1, 1884 Value: 100, 1885 }, 1886 } 1887 1888 q1 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo",source_canonical_service="productpage"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 1889 q1m0 := model.Metric{ 1890 "source_workload_namespace": "bookinfo", 1891 "source_workload": "productpage-v1", 1892 "source_canonical_service": "productpage", 1893 "source_canonical_revision": "v1", 1894 "source_cluster": "east", 1895 "destination_cluster": "east", 1896 "destination_service_namespace": "bookinfo", 1897 "destination_service": "reviews:9080", 1898 "destination_service_name": "reviews", 1899 "destination_workload_namespace": "bookinfo", 1900 "destination_workload": "reviews-v1", 1901 "destination_canonical_service": "reviews", 1902 "destination_canonical_revision": "v1", 1903 "request_protocol": "http", 1904 "response_code": "200", 1905 "grpc_response_status": "0", 1906 "response_flags": "-", 1907 } 1908 q1m1 := model.Metric{ 1909 "source_workload_namespace": "bookinfo", 1910 "source_workload": "productpage-v1", 1911 "source_canonical_service": "productpage", 1912 "source_canonical_revision": "v1", 1913 "source_cluster": "east", 1914 "destination_cluster": "east", 1915 "destination_service_namespace": "bookinfo", 1916 "destination_service": "reviews:9080", 1917 "destination_service_name": "reviews", 1918 "destination_workload_namespace": "bookinfo", 1919 "destination_workload": "reviews-v2", 1920 "destination_canonical_service": "reviews", 1921 "destination_canonical_revision": "v2", 1922 "request_protocol": "http", 1923 "response_code": "200", 1924 "grpc_response_status": "0", 1925 "response_flags": "-", 1926 } 1927 q1m2 := model.Metric{ 1928 "source_workload_namespace": "bookinfo", 1929 "source_workload": "productpage-v1", 1930 "source_canonical_service": "productpage", 1931 "source_canonical_revision": "v1", 1932 "source_cluster": "east", 1933 "destination_cluster": "east", 1934 "destination_service_namespace": "bookinfo", 1935 "destination_service": "reviews:9080", 1936 "destination_service_name": "reviews", 1937 "destination_workload_namespace": "bookinfo", 1938 "destination_workload": "reviews-v3", 1939 "destination_canonical_service": "reviews", 1940 "destination_canonical_revision": "v3", 1941 "request_protocol": "http", 1942 "response_code": "200", 1943 "grpc_response_status": "0", 1944 "response_flags": "-", 1945 } 1946 q1m3 := model.Metric{ 1947 "source_workload_namespace": "bookinfo", 1948 "source_workload": "productpage-v1", 1949 "source_canonical_service": "productpage", 1950 "source_canonical_revision": "v1", 1951 "source_cluster": "east", 1952 "destination_cluster": "east", 1953 "destination_service_namespace": "bookinfo", 1954 "destination_service": "details:9080", 1955 "destination_service_name": "details", 1956 "destination_workload_namespace": "bookinfo", 1957 "destination_workload": "details-v1", 1958 "destination_canonical_service": "details", 1959 "destination_canonical_revision": "v1", 1960 "request_protocol": "http", 1961 "response_code": "300", 1962 "response_flags": "-", 1963 } 1964 q1m4 := model.Metric{ 1965 "source_workload_namespace": "bookinfo", 1966 "source_workload": "productpage-v1", 1967 "source_canonical_service": "productpage", 1968 "source_canonical_revision": "v1", 1969 "source_cluster": "east", 1970 "destination_cluster": "east", 1971 "destination_service_namespace": "bookinfo", 1972 "destination_service": "details:9080", 1973 "destination_service_name": "details", 1974 "destination_workload_namespace": "bookinfo", 1975 "destination_workload": "details-v1", 1976 "destination_canonical_service": "details", 1977 "destination_canonical_revision": "v1", 1978 "request_protocol": "http", 1979 "response_code": "400", 1980 "response_flags": "-", 1981 } 1982 q1m5 := model.Metric{ 1983 "source_workload_namespace": "bookinfo", 1984 "source_workload": "productpage-v1", 1985 "source_canonical_service": "productpage", 1986 "source_canonical_revision": "v1", 1987 "source_cluster": "east", 1988 "destination_cluster": "east", 1989 "destination_service_namespace": "bookinfo", 1990 "destination_service": "details:9080", 1991 "destination_service_name": "details", 1992 "destination_workload_namespace": "bookinfo", 1993 "destination_workload": "details-v1", 1994 "destination_canonical_service": "details", 1995 "destination_canonical_revision": "v1", 1996 "request_protocol": "http", 1997 "response_code": "500", 1998 "response_flags": "-", 1999 } 2000 q1m6 := model.Metric{ 2001 "source_workload_namespace": "bookinfo", 2002 "source_workload": "productpage-v1", 2003 "source_canonical_service": "productpage", 2004 "source_canonical_revision": "v1", 2005 "source_cluster": "east", 2006 "destination_cluster": "east", 2007 "destination_service_namespace": "bookinfo", 2008 "destination_service": "details:9080", 2009 "destination_service_name": "details", 2010 "destination_workload_namespace": "bookinfo", 2011 "destination_workload": "details-v1", 2012 "destination_canonical_service": "details", 2013 "destination_canonical_revision": "v1", 2014 "request_protocol": "http", 2015 "response_code": "200", 2016 "grpc_response_status": "0", 2017 "response_flags": "-", 2018 } 2019 q1m7 := model.Metric{ 2020 "source_workload_namespace": "bookinfo", 2021 "source_workload": "productpage-v1", 2022 "source_canonical_service": "productpage", 2023 "source_canonical_revision": "v1", 2024 "source_cluster": "east", 2025 "destination_cluster": "east", 2026 "destination_service_namespace": "bookinfo", 2027 "destination_service": "productpage:9080", 2028 "destination_service_name": "productpage", 2029 "destination_workload_namespace": "bookinfo", 2030 "destination_workload": "productpage-v1", 2031 "destination_canonical_service": "productpage", 2032 "destination_canonical_revision": "v1", 2033 "request_protocol": "http", 2034 "response_code": "200", 2035 "grpc_response_status": "0", 2036 "response_flags": "-", 2037 } 2038 q1m8 := model.Metric{ 2039 "source_workload_namespace": "bookinfo", 2040 "source_workload": "productpage-v1", 2041 "source_canonical_service": "productpage", 2042 "source_canonical_revision": "v1", 2043 "source_cluster": "east", 2044 "destination_cluster": "unknown", 2045 "destination_service_namespace": "unknown", 2046 "destination_service": "unknown", 2047 "destination_service_name": "unknown", 2048 "destination_workload_namespace": "unknown", 2049 "destination_workload": "unknown", 2050 "destination_canonical_service": "unknown", 2051 "destination_canonical_revision": "unknown", 2052 "request_protocol": "http", 2053 "response_code": "404", 2054 "response_flags": "NR", 2055 } 2056 v1 := model.Vector{ 2057 &model.Sample{ 2058 Metric: q1m0, 2059 Value: 20, 2060 }, 2061 &model.Sample{ 2062 Metric: q1m1, 2063 Value: 20, 2064 }, 2065 &model.Sample{ 2066 Metric: q1m2, 2067 Value: 20, 2068 }, 2069 &model.Sample{ 2070 Metric: q1m3, 2071 Value: 20, 2072 }, 2073 &model.Sample{ 2074 Metric: q1m4, 2075 Value: 20, 2076 }, 2077 &model.Sample{ 2078 Metric: q1m5, 2079 Value: 20, 2080 }, 2081 &model.Sample{ 2082 Metric: q1m6, 2083 Value: 20, 2084 }, 2085 &model.Sample{ 2086 Metric: q1m7, 2087 Value: 20, 2088 }, 2089 &model.Sample{ 2090 Metric: q1m8, 2091 Value: 4, 2092 }, 2093 } 2094 2095 q2 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_service_namespace="bookinfo",destination_canonical_service="productpage"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2096 v2 := model.Vector{} 2097 2098 q3 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo",source_canonical_service="productpage"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2099 q3m0 := model.Metric{ 2100 "source_workload_namespace": "bookinfo", 2101 "source_workload": "productpage-v1", 2102 "source_canonical_service": "productpage", 2103 "source_canonical_revision": "v1", 2104 "source_cluster": "east", 2105 "destination_cluster": "east", 2106 "destination_service_namespace": "bookinfo", 2107 "destination_service": "tcp:9080", 2108 "destination_service_name": "tcp", 2109 "destination_workload_namespace": "bookinfo", 2110 "destination_workload": "tcp-v1", 2111 "destination_canonical_service": "tcp", 2112 "destination_canonical_revision": "v1", 2113 "response_flags": "-", 2114 } 2115 v3 := model.Vector{ 2116 &model.Sample{ 2117 Metric: q3m0, 2118 Value: 31, 2119 }, 2120 } 2121 2122 client, xapi := setupMocked(t) 2123 2124 mockQuery(xapi, q0, &v0) 2125 mockQuery(xapi, q1, &v1) 2126 mockQuery(xapi, q2, &v2) 2127 mockQuery(xapi, q3, &v3) 2128 2129 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 2130 2131 mr := mux.NewRouter() 2132 mr.HandleFunc("/api/namespaces/{namespace}/applications/{app}/graph", http.HandlerFunc( 2133 func(w http.ResponseWriter, r *http.Request) { 2134 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 2135 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 2136 respond(w, code, config) 2137 })) 2138 2139 ts := httptest.NewServer(mr) 2140 defer ts.Close() 2141 2142 fut = graphNodeIstio 2143 url := ts.URL + "/api/namespaces/bookinfo/applications/productpage/graph?graphType=versionedApp&appenders&queryTime=1523364075" 2144 resp, err := http.Get(url) 2145 if err != nil { 2146 t.Fatal(err) 2147 } 2148 actual, _ := io.ReadAll(resp.Body) 2149 expected, _ := os.ReadFile("testdata/test_app_node_graph.expected") 2150 if runtime.GOOS == "windows" { 2151 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 2152 } 2153 expected = expected[:len(expected)-1] // remove EOF byte 2154 2155 assertObjectsEqual(t, expected, actual) 2156 assert.Equal(t, 200, resp.StatusCode) 2157 } 2158 2159 func TestVersionedAppNodeGraph(t *testing.T) { 2160 q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",destination_canonical_service="productpage",destination_canonical_revision="v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2161 q0m0 := model.Metric{ 2162 "source_workload_namespace": "unknown", 2163 "source_workload": "unknown", 2164 "source_canonical_service": "unknown", 2165 "source_canonical_revision": "unknown", 2166 "source_cluster": "unknown", 2167 "destination_cluster": "east", 2168 "destination_service_namespace": "bookinfo", 2169 "destination_service": "productpage:9080", 2170 "destination_service_name": "productpage", 2171 "destination_workload_namespace": "bookinfo", 2172 "destination_workload": "productpage-v1", 2173 "destination_canonical_service": "productpage", 2174 "destination_canonical_revision": "v1", 2175 "request_protocol": "http", 2176 "response_code": "200", 2177 "grpc_response_status": "0", 2178 "response_flags": "-", 2179 } 2180 q0m1 := model.Metric{ 2181 "source_workload_namespace": "istio-system", 2182 "source_workload": "ingressgateway-unknown", 2183 "source_canonical_service": "ingressgateway", 2184 "source_canonical_revision": "latest", 2185 "source_cluster": "east", 2186 "destination_cluster": "east", 2187 "destination_service_namespace": "bookinfo", 2188 "destination_service": "productpage:9080", 2189 "destination_service_name": "productpage", 2190 "destination_workload_namespace": "bookinfo", 2191 "destination_workload": "productpage-v1", 2192 "destination_canonical_service": "productpage", 2193 "destination_canonical_revision": "v1", 2194 "request_protocol": "http", 2195 "response_code": "200", 2196 "grpc_response_status": "0", 2197 "response_flags": "-", 2198 } 2199 v0 := model.Vector{ 2200 &model.Sample{ 2201 Metric: q0m0, 2202 Value: 50, 2203 }, 2204 &model.Sample{ 2205 Metric: q0m1, 2206 Value: 100, 2207 }, 2208 } 2209 2210 q1 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo",source_canonical_service="productpage",source_canonical_revision="v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2211 q1m0 := model.Metric{ 2212 "source_workload_namespace": "bookinfo", 2213 "source_workload": "productpage-v1", 2214 "source_canonical_service": "productpage", 2215 "source_canonical_revision": "v1", 2216 "source_cluster": "east", 2217 "destination_cluster": "east", 2218 "destination_service_namespace": "bookinfo", 2219 "destination_service": "reviews:9080", 2220 "destination_service_name": "reviews", 2221 "destination_workload_namespace": "bookinfo", 2222 "destination_workload": "reviews-v1", 2223 "destination_canonical_service": "reviews", 2224 "destination_canonical_revision": "v1", 2225 "request_protocol": "http", 2226 "response_code": "200", 2227 "grpc_response_status": "0", 2228 "response_flags": "-", 2229 } 2230 q1m1 := model.Metric{ 2231 "source_workload_namespace": "bookinfo", 2232 "source_workload": "productpage-v1", 2233 "source_canonical_service": "productpage", 2234 "source_canonical_revision": "v1", 2235 "source_cluster": "east", 2236 "destination_cluster": "east", 2237 "destination_service_namespace": "bookinfo", 2238 "destination_service": "reviews:9080", 2239 "destination_service_name": "reviews", 2240 "destination_workload_namespace": "bookinfo", 2241 "destination_workload": "reviews-v2", 2242 "destination_canonical_service": "reviews", 2243 "destination_canonical_revision": "v2", 2244 "request_protocol": "http", 2245 "response_code": "200", 2246 "grpc_response_status": "0", 2247 "response_flags": "-", 2248 } 2249 q1m2 := model.Metric{ 2250 "source_workload_namespace": "bookinfo", 2251 "source_workload": "productpage-v1", 2252 "source_canonical_service": "productpage", 2253 "source_canonical_revision": "v1", 2254 "source_cluster": "east", 2255 "destination_cluster": "east", 2256 "destination_service_namespace": "bookinfo", 2257 "destination_service": "reviews:9080", 2258 "destination_service_name": "reviews", 2259 "destination_workload_namespace": "bookinfo", 2260 "destination_workload": "reviews-v3", 2261 "destination_canonical_service": "reviews", 2262 "destination_canonical_revision": "v3", 2263 "request_protocol": "http", 2264 "response_code": "200", 2265 "grpc_response_status": "0", 2266 "response_flags": "-", 2267 } 2268 q1m3 := model.Metric{ 2269 "source_workload_namespace": "bookinfo", 2270 "source_workload": "productpage-v1", 2271 "source_canonical_service": "productpage", 2272 "source_canonical_revision": "v1", 2273 "source_cluster": "east", 2274 "destination_cluster": "east", 2275 "destination_service_namespace": "bookinfo", 2276 "destination_service": "details:9080", 2277 "destination_service_name": "details", 2278 "destination_workload_namespace": "bookinfo", 2279 "destination_workload": "details-v1", 2280 "destination_canonical_service": "details", 2281 "destination_canonical_revision": "v1", 2282 "request_protocol": "http", 2283 "response_code": "300", 2284 "response_flags": "-", 2285 } 2286 q1m4 := model.Metric{ 2287 "source_workload_namespace": "bookinfo", 2288 "source_workload": "productpage-v1", 2289 "source_canonical_service": "productpage", 2290 "source_canonical_revision": "v1", 2291 "source_cluster": "east", 2292 "destination_cluster": "east", 2293 "destination_service_namespace": "bookinfo", 2294 "destination_service": "details:9080", 2295 "destination_service_name": "details", 2296 "destination_workload_namespace": "bookinfo", 2297 "destination_workload": "details-v1", 2298 "destination_canonical_service": "details", 2299 "destination_canonical_revision": "v1", 2300 "request_protocol": "http", 2301 "response_code": "400", 2302 "response_flags": "-", 2303 } 2304 q1m5 := model.Metric{ 2305 "source_workload_namespace": "bookinfo", 2306 "source_workload": "productpage-v1", 2307 "source_canonical_service": "productpage", 2308 "source_canonical_revision": "v1", 2309 "source_cluster": "east", 2310 "destination_cluster": "east", 2311 "destination_service_namespace": "bookinfo", 2312 "destination_service": "details:9080", 2313 "destination_service_name": "details", 2314 "destination_workload_namespace": "bookinfo", 2315 "destination_workload": "details-v1", 2316 "destination_canonical_service": "details", 2317 "destination_canonical_revision": "v1", 2318 "request_protocol": "http", 2319 "response_code": "500", 2320 "response_flags": "-", 2321 } 2322 q1m6 := model.Metric{ 2323 "source_workload_namespace": "bookinfo", 2324 "source_workload": "productpage-v1", 2325 "source_canonical_service": "productpage", 2326 "source_canonical_revision": "v1", 2327 "source_cluster": "east", 2328 "destination_cluster": "east", 2329 "destination_service_namespace": "bookinfo", 2330 "destination_service": "details:9080", 2331 "destination_service_name": "details", 2332 "destination_workload_namespace": "bookinfo", 2333 "destination_workload": "details-v1", 2334 "destination_canonical_service": "details", 2335 "destination_canonical_revision": "v1", 2336 "request_protocol": "http", 2337 "response_code": "200", 2338 "grpc_response_status": "0", 2339 "response_flags": "-", 2340 } 2341 q1m7 := model.Metric{ 2342 "source_workload_namespace": "bookinfo", 2343 "source_workload": "productpage-v1", 2344 "source_canonical_service": "productpage", 2345 "source_canonical_revision": "v1", 2346 "source_cluster": "east", 2347 "destination_cluster": "east", 2348 "destination_service_namespace": "bookinfo", 2349 "destination_service": "productpage:9080", 2350 "destination_service_name": "productpage", 2351 "destination_workload_namespace": "bookinfo", 2352 "destination_workload": "productpage-v1", 2353 "destination_canonical_service": "productpage", 2354 "destination_canonical_revision": "v1", 2355 "request_protocol": "http", 2356 "response_code": "200", 2357 "grpc_response_status": "0", 2358 "response_flags": "-", 2359 } 2360 q1m8 := model.Metric{ 2361 "source_workload_namespace": "bookinfo", 2362 "source_workload": "productpage-v1", 2363 "source_canonical_service": "productpage", 2364 "source_canonical_revision": "v1", 2365 "source_cluster": "east", 2366 "destination_cluster": "unknown", 2367 "destination_service_namespace": "unknown", 2368 "destination_service": "unknown", 2369 "destination_service_name": "unknown", 2370 "destination_workload_namespace": "unknown", 2371 "destination_workload": "unknown", 2372 "destination_canonical_service": "unknown", 2373 "destination_canonical_revision": "unknown", 2374 "request_protocol": "http", 2375 "response_code": "404", 2376 "response_flags": "NR", 2377 } 2378 v1 := model.Vector{ 2379 &model.Sample{ 2380 Metric: q1m0, 2381 Value: 20, 2382 }, 2383 &model.Sample{ 2384 Metric: q1m1, 2385 Value: 20, 2386 }, 2387 &model.Sample{ 2388 Metric: q1m2, 2389 Value: 20, 2390 }, 2391 &model.Sample{ 2392 Metric: q1m3, 2393 Value: 20, 2394 }, 2395 &model.Sample{ 2396 Metric: q1m4, 2397 Value: 20, 2398 }, 2399 &model.Sample{ 2400 Metric: q1m5, 2401 Value: 20, 2402 }, 2403 &model.Sample{ 2404 Metric: q1m6, 2405 Value: 20, 2406 }, 2407 &model.Sample{ 2408 Metric: q1m7, 2409 Value: 20, 2410 }, 2411 &model.Sample{ 2412 Metric: q1m8, 2413 Value: 4, 2414 }, 2415 } 2416 2417 q2 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_service_namespace="bookinfo",destination_canonical_service="productpage",destination_canonical_revision="v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2418 v2 := model.Vector{} 2419 2420 q3 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo",source_canonical_service="productpage",source_canonical_revision="v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2421 q3m0 := model.Metric{ 2422 "source_workload_namespace": "bookinfo", 2423 "source_workload": "productpage-v1", 2424 "source_canonical_service": "productpage", 2425 "source_canonical_revision": "v1", 2426 "source_cluster": "east", 2427 "destination_cluster": "east", 2428 "destination_service_namespace": "bookinfo", 2429 "destination_service": "tcp:9080", 2430 "destination_service_name": "tcp", 2431 "destination_workload_namespace": "bookinfo", 2432 "destination_workload": "tcp-v1", 2433 "destination_canonical_service": "tcp", 2434 "destination_canonical_revision": "v1", 2435 "response_flags": "-", 2436 } 2437 v3 := model.Vector{ 2438 &model.Sample{ 2439 Metric: q3m0, 2440 Value: 31, 2441 }, 2442 } 2443 2444 client, xapi := setupMocked(t) 2445 2446 mockQuery(xapi, q0, &v0) 2447 mockQuery(xapi, q1, &v1) 2448 mockQuery(xapi, q2, &v2) 2449 mockQuery(xapi, q3, &v3) 2450 2451 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 2452 2453 mr := mux.NewRouter() 2454 mr.HandleFunc("/api/namespaces/{namespace}/applications/{app}/versions/{version}/graph", http.HandlerFunc( 2455 func(w http.ResponseWriter, r *http.Request) { 2456 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 2457 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 2458 respond(w, code, config) 2459 })) 2460 2461 ts := httptest.NewServer(mr) 2462 defer ts.Close() 2463 2464 fut = graphNodeIstio 2465 url := ts.URL + "/api/namespaces/bookinfo/applications/productpage/versions/v1/graph?graphType=versionedApp&appenders&queryTime=1523364075" 2466 resp, err := http.Get(url) 2467 if err != nil { 2468 t.Fatal(err) 2469 } 2470 actual, _ := io.ReadAll(resp.Body) 2471 expected, _ := os.ReadFile("testdata/test_versioned_app_node_graph.expected") 2472 if runtime.GOOS == "windows" { 2473 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 2474 } 2475 expected = expected[:len(expected)-1] // remove EOF byte 2476 2477 assertObjectsEqual(t, expected, actual) 2478 assert.Equal(t, 200, resp.StatusCode) 2479 } 2480 2481 func TestServiceNodeGraph(t *testing.T) { 2482 q0 := `round(sum(rate(istio_requests_total{reporter="source",destination_workload="unknown",destination_service=~"^productpage\\.bookinfo\\..*$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2483 v0 := model.Vector{} 2484 2485 q1 := `round(sum(rate(istio_requests_total{reporter="destination",destination_service_namespace="bookinfo",destination_service=~"^productpage\\.bookinfo\\..*$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2486 q1m0 := model.Metric{ 2487 "source_workload_namespace": "istio-system", 2488 "source_workload": "ingressgateway-unknown", 2489 "source_canonical_service": "ingressgateway", 2490 "source_canonical_revision": "latest", 2491 "source_cluster": "east", 2492 "destination_cluster": "east", 2493 "destination_service_namespace": "bookinfo", 2494 "destination_service": "productpage:9080", 2495 "destination_service_name": "productpage", 2496 "destination_workload_namespace": "bookinfo", 2497 "destination_workload": "productpage-v1", 2498 "destination_canonical_service": "productpage", 2499 "destination_canonical_revision": "v1", 2500 "request_protocol": "http", 2501 "response_code": "200", 2502 "grpc_response_status": "0", 2503 "response_flags": "-", 2504 } 2505 v1 := model.Vector{ 2506 &model.Sample{ 2507 Metric: q1m0, 2508 Value: 100, 2509 }, 2510 } 2511 2512 q2 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_service_namespace="bookinfo",destination_service=~"^productpage\\.bookinfo\\..*$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2513 q2m0 := model.Metric{ 2514 "source_workload_namespace": "istio-system", 2515 "source_workload": "ingressgateway-unknown", 2516 "source_canonical_service": "ingressgateway", 2517 "source_canonical_revision": "latest", 2518 "source_cluster": "east", 2519 "destination_cluster": "east", 2520 "destination_service_namespace": "bookinfo", 2521 "destination_service": "productpage:9080", 2522 "destination_service_name": "productpage", 2523 "destination_workload_namespace": "bookinfo", 2524 "destination_workload": "productpage-v1", 2525 "destination_canonical_service": "productpage", 2526 "destination_canonical_revision": "v1", 2527 "response_flags": "-", 2528 } 2529 v2 := model.Vector{ 2530 &model.Sample{ 2531 Metric: q2m0, 2532 Value: 31, 2533 }, 2534 } 2535 2536 client, xapi := setupMocked(t) 2537 2538 mockQuery(xapi, q0, &v0) 2539 mockQuery(xapi, q1, &v1) 2540 mockQuery(xapi, q2, &v2) 2541 2542 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 2543 2544 mr := mux.NewRouter() 2545 mr.HandleFunc("/api/namespaces/{namespace}/services/{service}/graph", http.HandlerFunc( 2546 func(w http.ResponseWriter, r *http.Request) { 2547 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 2548 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 2549 respond(w, code, config) 2550 })) 2551 2552 ts := httptest.NewServer(mr) 2553 defer ts.Close() 2554 2555 fut = graphNodeIstio 2556 url := ts.URL + "/api/namespaces/bookinfo/services/productpage/graph?graphType=workload&appenders&queryTime=1523364075" 2557 resp, err := http.Get(url) 2558 if err != nil { 2559 t.Fatal(err) 2560 } 2561 actual, _ := io.ReadAll(resp.Body) 2562 expected, _ := os.ReadFile("testdata/test_service_node_graph.expected") 2563 if runtime.GOOS == "windows" { 2564 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 2565 } 2566 expected = expected[:len(expected)-1] // remove EOF byte 2567 2568 assertObjectsEqual(t, expected, actual) 2569 assert.Equal(t, 200, resp.StatusCode) 2570 } 2571 2572 func TestRatesNodeGraphTotal(t *testing.T) { 2573 q0 := `round(sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2574 q0m0 := model.Metric{ 2575 "source_workload_namespace": "unknown", 2576 "source_workload": "unknown", 2577 "source_canonical_service": "unknown", 2578 "source_canonical_revision": "unknown", 2579 "source_cluster": "unknown", 2580 "destination_cluster": "east", 2581 "destination_service_namespace": "bookinfo", 2582 "destination_service": "productpage:9080", 2583 "destination_service_name": "productpage", 2584 "destination_workload_namespace": "bookinfo", 2585 "destination_workload": "productpage-v1", 2586 "destination_canonical_service": "productpage", 2587 "destination_canonical_revision": "v1", 2588 "request_protocol": "http", 2589 "response_code": "200", 2590 "grpc_response_status": "0", 2591 "response_flags": "-", 2592 } 2593 q0m1 := model.Metric{ 2594 "source_workload_namespace": "istio-system", 2595 "source_workload": "ingressgateway-unknown", 2596 "source_canonical_service": "ingressgateway", 2597 "source_canonical_revision": "latest", 2598 "source_cluster": "east", 2599 "destination_cluster": "east", 2600 "destination_service_namespace": "bookinfo", 2601 "destination_service": "productpage:9080", 2602 "destination_service_name": "productpage", 2603 "destination_workload_namespace": "bookinfo", 2604 "destination_workload": "productpage-v1", 2605 "destination_canonical_service": "productpage", 2606 "destination_canonical_revision": "v1", 2607 "request_protocol": "http", 2608 "response_code": "200", 2609 "grpc_response_status": "0", 2610 "response_flags": "-", 2611 } 2612 v0 := model.Vector{ 2613 &model.Sample{ 2614 Metric: q0m0, 2615 Value: 50, 2616 }, 2617 &model.Sample{ 2618 Metric: q0m1, 2619 Value: 100, 2620 }, 2621 } 2622 2623 q1 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 2624 q1m0 := model.Metric{ 2625 "source_workload_namespace": "bookinfo", 2626 "source_workload": "productpage-v1", 2627 "source_canonical_service": "productpage", 2628 "source_canonical_revision": "v1", 2629 "source_cluster": "east", 2630 "destination_cluster": "east", 2631 "destination_service_namespace": "bookinfo", 2632 "destination_service": "reviews:9080", 2633 "destination_service_name": "reviews", 2634 "destination_workload_namespace": "bookinfo", 2635 "destination_workload": "reviews-v1", 2636 "destination_canonical_service": "reviews", 2637 "destination_canonical_revision": "v1", 2638 "request_protocol": "http", 2639 "response_code": "200", 2640 "grpc_response_status": "0", 2641 "response_flags": "-", 2642 } 2643 q1m1 := model.Metric{ 2644 "source_workload_namespace": "bookinfo", 2645 "source_workload": "productpage-v1", 2646 "source_canonical_service": "productpage", 2647 "source_canonical_revision": "v1", 2648 "source_cluster": "east", 2649 "destination_cluster": "east", 2650 "destination_service_namespace": "bookinfo", 2651 "destination_service": "reviews:9080", 2652 "destination_service_name": "reviews", 2653 "destination_workload_namespace": "bookinfo", 2654 "destination_workload": "reviews-v2", 2655 "destination_canonical_service": "reviews", 2656 "destination_canonical_revision": "v2", 2657 "request_protocol": "http", 2658 "response_code": "200", 2659 "grpc_response_status": "0", 2660 "response_flags": "-", 2661 } 2662 q1m2 := model.Metric{ 2663 "source_workload_namespace": "bookinfo", 2664 "source_workload": "productpage-v1", 2665 "source_canonical_service": "productpage", 2666 "source_canonical_revision": "v1", 2667 "source_cluster": "east", 2668 "destination_cluster": "east", 2669 "destination_service_namespace": "bookinfo", 2670 "destination_service": "reviews:9080", 2671 "destination_service_name": "reviews", 2672 "destination_workload_namespace": "bookinfo", 2673 "destination_workload": "reviews-v3", 2674 "destination_canonical_service": "reviews", 2675 "destination_canonical_revision": "v3", 2676 "request_protocol": "http", 2677 "response_code": "200", 2678 "grpc_response_status": "0", 2679 "response_flags": "-", 2680 } 2681 q1m3 := model.Metric{ 2682 "source_workload_namespace": "bookinfo", 2683 "source_workload": "productpage-v1", 2684 "source_canonical_service": "productpage", 2685 "source_canonical_revision": "v1", 2686 "source_cluster": "east", 2687 "destination_cluster": "east", 2688 "destination_service_namespace": "bookinfo", 2689 "destination_service": "details:9080", 2690 "destination_service_name": "details", 2691 "destination_workload_namespace": "bookinfo", 2692 "destination_workload": "details-v1", 2693 "destination_canonical_service": "details", 2694 "destination_canonical_revision": "v1", 2695 "request_protocol": "http", 2696 "response_code": "300", 2697 "response_flags": "-", 2698 } 2699 q1m4 := model.Metric{ 2700 "source_workload_namespace": "bookinfo", 2701 "source_workload": "productpage-v1", 2702 "source_canonical_service": "productpage", 2703 "source_canonical_revision": "v1", 2704 "source_cluster": "east", 2705 "destination_cluster": "east", 2706 "destination_service_namespace": "bookinfo", 2707 "destination_service": "details:9080", 2708 "destination_service_name": "details", 2709 "destination_workload_namespace": "bookinfo", 2710 "destination_workload": "details-v1", 2711 "destination_canonical_service": "details", 2712 "destination_canonical_revision": "v1", 2713 "request_protocol": "http", 2714 "response_code": "400", 2715 "response_flags": "-", 2716 } 2717 q1m5 := model.Metric{ 2718 "source_workload_namespace": "bookinfo", 2719 "source_workload": "productpage-v1", 2720 "source_canonical_service": "productpage", 2721 "source_canonical_revision": "v1", 2722 "source_cluster": "east", 2723 "destination_cluster": "east", 2724 "destination_service_namespace": "bookinfo", 2725 "destination_service": "details:9080", 2726 "destination_service_name": "details", 2727 "destination_workload_namespace": "bookinfo", 2728 "destination_workload": "details-v1", 2729 "destination_canonical_service": "details", 2730 "destination_canonical_revision": "v1", 2731 "request_protocol": "http", 2732 "response_code": "500", 2733 "response_flags": "-", 2734 } 2735 q1m6 := model.Metric{ 2736 "source_workload_namespace": "bookinfo", 2737 "source_workload": "productpage-v1", 2738 "source_canonical_service": "productpage", 2739 "source_canonical_revision": "v1", 2740 "source_cluster": "east", 2741 "destination_cluster": "east", 2742 "destination_service_namespace": "bookinfo", 2743 "destination_service": "details:9080", 2744 "destination_service_name": "details", 2745 "destination_workload_namespace": "bookinfo", 2746 "destination_workload": "details-v1", 2747 "destination_canonical_service": "details", 2748 "destination_canonical_revision": "v1", 2749 "request_protocol": "http", 2750 "response_code": "200", 2751 "grpc_response_status": "0", 2752 "response_flags": "-", 2753 } 2754 q1m7 := model.Metric{ 2755 "source_workload_namespace": "bookinfo", 2756 "source_workload": "productpage-v1", 2757 "source_canonical_service": "productpage", 2758 "source_canonical_revision": "v1", 2759 "source_cluster": "east", 2760 "destination_cluster": "east", 2761 "destination_service_namespace": "bookinfo", 2762 "destination_service": "productpage:9080", 2763 "destination_service_name": "productpage", 2764 "destination_workload_namespace": "bookinfo", 2765 "destination_workload": "productpage-v1", 2766 "destination_canonical_service": "productpage", 2767 "destination_canonical_revision": "v1", 2768 "request_protocol": "http", 2769 "response_code": "200", 2770 "grpc_response_status": "0", 2771 "response_flags": "-", 2772 } 2773 q1m8 := model.Metric{ 2774 "source_workload_namespace": "bookinfo", 2775 "source_workload": "productpage-v1", 2776 "source_canonical_service": "productpage", 2777 "source_canonical_revision": "v1", 2778 "source_cluster": "east", 2779 "destination_cluster": "unknown", 2780 "destination_service_namespace": "unknown", 2781 "destination_service": "unknown", 2782 "destination_service_name": "unknown", 2783 "destination_workload_namespace": "unknown", 2784 "destination_workload": "unknown", 2785 "destination_canonical_service": "unknown", 2786 "destination_canonical_revision": "unknown", 2787 "request_protocol": "http", 2788 "response_code": "404", 2789 "response_flags": "NR", 2790 } 2791 q1m9 := model.Metric{ 2792 "source_workload_namespace": "bookinfo", 2793 "source_workload": "productpage-v1", 2794 "source_canonical_service": "productpage", 2795 "source_canonical_revision": "v1", 2796 "source_cluster": "east", 2797 "destination_cluster": "east", 2798 "destination_service_namespace": "bankapp", 2799 "destination_service": "deposit:9080", 2800 "destination_service_name": "deposit", 2801 "destination_workload_namespace": "bankapp", 2802 "destination_workload": "deposit-v1", 2803 "destination_canonical_service": "deposit", 2804 "destination_canonical_revision": "v1", 2805 "request_protocol": "grpc", 2806 "response_code": "200", 2807 "grpc_response_status": "0", 2808 "response_flags": "-", 2809 } 2810 v1 := model.Vector{ 2811 &model.Sample{ 2812 Metric: q1m0, 2813 Value: 20, 2814 }, 2815 &model.Sample{ 2816 Metric: q1m1, 2817 Value: 20, 2818 }, 2819 &model.Sample{ 2820 Metric: q1m2, 2821 Value: 20, 2822 }, 2823 &model.Sample{ 2824 Metric: q1m3, 2825 Value: 20, 2826 }, 2827 &model.Sample{ 2828 Metric: q1m4, 2829 Value: 20, 2830 }, 2831 &model.Sample{ 2832 Metric: q1m5, 2833 Value: 20, 2834 }, 2835 &model.Sample{ 2836 Metric: q1m6, 2837 Value: 20, 2838 }, 2839 &model.Sample{ 2840 Metric: q1m7, 2841 Value: 20, 2842 }, 2843 &model.Sample{ 2844 Metric: q1m8, 2845 Value: 4, 2846 }, 2847 &model.Sample{ 2848 Metric: q1m9, 2849 Value: 50, 2850 }, 2851 } 2852 2853 q2 := `round(sum(rate(istio_request_messages_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 2854 v2 := model.Vector{} 2855 2856 q3 := `round(sum(rate(istio_request_messages_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 2857 q3m0 := model.Metric{ 2858 "source_workload_namespace": "bookinfo", 2859 "source_workload": "productpage-v1", 2860 "source_canonical_service": "productpage", 2861 "source_canonical_revision": "v1", 2862 "source_cluster": "east", 2863 "destination_cluster": "east", 2864 "destination_service_namespace": "bookinfo", 2865 "destination_service": "tcp:9080", 2866 "destination_service_name": "tcp", 2867 "destination_workload_namespace": "bookinfo", 2868 "destination_workload": "tcp-v1", 2869 "destination_canonical_service": "tcp", 2870 "destination_canonical_revision": "v1", 2871 "response_flags": "-", 2872 } 2873 v3 := model.Vector{ 2874 &model.Sample{ 2875 Metric: q3m0, 2876 Value: 31, 2877 }, 2878 } 2879 2880 q4 := `round(sum(rate(istio_response_messages_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 2881 v4 := model.Vector{} 2882 2883 q5 := `round(sum(rate(istio_response_messages_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision) > 0,0.001)` 2884 q5m0 := model.Metric{ 2885 "source_workload_namespace": "bookinfo", 2886 "source_workload": "productpage-v1", 2887 "source_canonical_service": "productpage", 2888 "source_canonical_revision": "v1", 2889 "source_cluster": "east", 2890 "destination_cluster": "east", 2891 "destination_service_namespace": "bookinfo", 2892 "destination_service": "tcp:9080", 2893 "destination_service_name": "tcp", 2894 "destination_workload_namespace": "bookinfo", 2895 "destination_workload": "tcp-v1", 2896 "destination_canonical_service": "tcp", 2897 "destination_canonical_revision": "v1", 2898 "response_flags": "-", 2899 } 2900 v5 := model.Vector{ 2901 &model.Sample{ 2902 Metric: q5m0, 2903 Value: 62, 2904 }, 2905 } 2906 2907 q6 := `round(sum(rate(istio_tcp_sent_bytes_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2908 v6 := model.Vector{} 2909 2910 q7 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2911 q7m0 := model.Metric{ 2912 "source_workload_namespace": "bookinfo", 2913 "source_workload": "productpage-v1", 2914 "source_canonical_service": "productpage", 2915 "source_canonical_revision": "v1", 2916 "source_cluster": "east", 2917 "destination_cluster": "east", 2918 "destination_service_namespace": "bookinfo", 2919 "destination_service": "tcp:9080", 2920 "destination_service_name": "tcp", 2921 "destination_workload_namespace": "bookinfo", 2922 "destination_workload": "tcp-v1", 2923 "destination_canonical_service": "tcp", 2924 "destination_canonical_revision": "v1", 2925 "response_flags": "-", 2926 } 2927 v7 := model.Vector{ 2928 &model.Sample{ 2929 Metric: q7m0, 2930 Value: 31, 2931 }, 2932 } 2933 2934 q8 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_workload_namespace="bookinfo",destination_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2935 v8 := model.Vector{} 2936 2937 q9 := `round(sum(rate(istio_tcp_sent_bytes_total{reporter="source",source_workload_namespace="bookinfo",source_workload="productpage-v1"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 2938 q9m0 := model.Metric{ 2939 "source_workload_namespace": "bookinfo", 2940 "source_workload": "productpage-v1", 2941 "source_canonical_service": "productpage", 2942 "source_canonical_revision": "v1", 2943 "source_cluster": "east", 2944 "destination_cluster": "east", 2945 "destination_service_namespace": "bookinfo", 2946 "destination_service": "tcp:9080", 2947 "destination_service_name": "tcp", 2948 "destination_workload_namespace": "bookinfo", 2949 "destination_workload": "tcp-v1", 2950 "destination_canonical_service": "tcp", 2951 "destination_canonical_revision": "v1", 2952 "response_flags": "-", 2953 } 2954 v9 := model.Vector{ 2955 &model.Sample{ 2956 Metric: q9m0, 2957 Value: 62, 2958 }, 2959 } 2960 2961 client, xapi := setupMocked(t) 2962 2963 mockQuery(xapi, q0, &v0) 2964 mockQuery(xapi, q1, &v1) 2965 mockQuery(xapi, q2, &v2) 2966 mockQuery(xapi, q3, &v3) 2967 mockQuery(xapi, q4, &v4) 2968 mockQuery(xapi, q5, &v5) 2969 mockQuery(xapi, q6, &v6) 2970 mockQuery(xapi, q7, &v7) 2971 mockQuery(xapi, q8, &v8) 2972 mockQuery(xapi, q9, &v9) 2973 2974 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 2975 2976 mr := mux.NewRouter() 2977 mr.HandleFunc("/api/namespaces/{namespace}/workloads/{workload}/graph", http.HandlerFunc( 2978 func(w http.ResponseWriter, r *http.Request) { 2979 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 2980 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 2981 respond(w, code, config) 2982 })) 2983 2984 ts := httptest.NewServer(mr) 2985 defer ts.Close() 2986 2987 fut = graphNodeIstio 2988 url := ts.URL + "/api/namespaces/bookinfo/workloads/productpage-v1/graph?rateGrpc=total&rateHttp=requests&rateTcp=total&graphType=workload&appenders&queryTime=1523364075" 2989 resp, err := http.Get(url) 2990 if err != nil { 2991 t.Fatal(err) 2992 } 2993 actual, _ := io.ReadAll(resp.Body) 2994 expected, _ := os.ReadFile("testdata/test_rates_node_graph_total.expected") 2995 if runtime.GOOS == "windows" { 2996 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 2997 } 2998 expected = expected[:len(expected)-1] // remove EOF byte 2999 3000 assertObjectsEqual(t, expected, actual) 3001 assert.Equal(t, 200, resp.StatusCode) 3002 } 3003 3004 // TestComplexGraph aims to provide test coverage for a more robust graph and specific corner cases. Listed below are coverage cases 3005 // - multi-cluster graph 3006 // - multi-namespace graph 3007 // - istio namespace 3008 // - a "shared" node (internal in ns-1, outsider in ns-2) 3009 // - request.host 3010 // - bad dest telemetry filtering 3011 // - bad source telemetry filtering 3012 // - workload -> egress -> service-entry traffic 3013 // - 0 response code (no response) 3014 // - queryScope 3015 // note: appenders still tested in separate unit tests given that they create their own new business/kube clients 3016 func TestComplexGraph(t *testing.T) { 3017 // bookinfo 3018 q0 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3019 q0m0 := model.Metric{ // outsider request that fails to reach workload 3020 "source_cluster": "cluster-tutorial", 3021 "source_workload_namespace": "outsider", 3022 "source_workload": "outsider-ingress", 3023 "source_canonical_service": "outsider-ingress", 3024 "source_canonical_revision": "latest", 3025 "destination_cluster": "unknown", // this reflects real ingress reporting at this time, 3026 "destination_service_namespace": "unknown", // although it seems like a possible bug to me 3027 "destination_service": "reviews.bookinfo.svc.cluster.local", 3028 "destination_service_name": "reviews", 3029 "destination_workload_namespace": "unknown", 3030 "destination_workload": "unknown", 3031 "destination_canonical_service": "unknown", 3032 "destination_canonical_revision": "latest", 3033 "request_protocol": "http", 3034 "response_code": "503", 3035 "response_flags": "-", 3036 } 3037 v0 := model.Vector{ 3038 &model.Sample{ 3039 Metric: q0m0, 3040 Value: 50, 3041 }, 3042 } 3043 3044 q1 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3045 q1m0 := model.Metric{ 3046 "source_cluster": "unknown", 3047 "source_workload_namespace": "unknown", 3048 "source_workload": "unknown", 3049 "source_canonical_service": "unknown", 3050 "source_canonical_revision": "unknown", 3051 "destination_cluster": "cluster-bookinfo", 3052 "destination_service_namespace": "bookinfo", 3053 "destination_service": "productpage:9080", 3054 "destination_service_name": "productpage", 3055 "destination_workload_namespace": "bookinfo", 3056 "destination_workload": "productpage-v1", 3057 "destination_canonical_service": "productpage", 3058 "destination_canonical_revision": "v1", 3059 "request_protocol": "http", 3060 "response_code": "200", 3061 "grpc_response_status": "0", 3062 "response_flags": "-", 3063 } 3064 q1m1 := model.Metric{ 3065 "source_cluster": "cluster-tutorial", 3066 "source_workload_namespace": "tutorial", 3067 "source_workload": "customer-v1", 3068 "source_canonical_service": "customer", 3069 "source_canonical_revision": "v1", 3070 "destination_cluster": "cluster-bookinfo", 3071 "destination_service_namespace": "bookinfo", 3072 "destination_service": "productpage:9080", 3073 "destination_service_name": "productpage", 3074 "destination_workload_namespace": "bookinfo", 3075 "destination_workload": "productpage-v1", 3076 "destination_canonical_service": "productpage", 3077 "destination_canonical_revision": "v1", 3078 "request_protocol": "http", 3079 "response_code": "200", 3080 "grpc_response_status": "0", 3081 "response_flags": "-", 3082 } 3083 q1m2 := model.Metric{ // bad dest telem (variant 1.0) 3084 "source_cluster": "cluster-tutorial", 3085 "source_workload_namespace": "tutorial", 3086 "source_workload": "customer-v1", 3087 "source_canonical_service": "customer", 3088 "source_canonical_revision": "v1", 3089 "destination_cluster": "cluster-bookinfo", 3090 "destination_service_namespace": "bookinfo", 3091 "destination_service": "10.20.30.40:9080", 3092 "destination_service_name": "10.20.30.40:9080", 3093 "destination_workload_namespace": "unknown", 3094 "destination_workload": "unknown", 3095 "destination_canonical_service": "unknown", 3096 "destination_canonical_revision": "unknown", 3097 "request_protocol": "http", 3098 "response_code": "200", 3099 "response_flags": "-", 3100 } 3101 q1m3 := model.Metric{ // bad dest telem (variant 1.2) 3102 "source_cluster": "cluster-tutorial", 3103 "source_workload_namespace": "tutorial", 3104 "source_workload": "customer-v1", 3105 "source_canonical_service": "customer", 3106 "source_canonical_revision": "v1", 3107 "destination_cluster": "cluster-bookinfo", 3108 "destination_service_namespace": "bookinfo", 3109 "destination_service": "10.20.30.40", 3110 "destination_service_name": "10.20.30.40", 3111 "destination_workload_namespace": "unknown", 3112 "destination_workload": "unknown", 3113 "destination_canonical_service": "unknown", 3114 "destination_canonical_revision": "unknown", 3115 "request_protocol": "http", 3116 "response_code": "200", 3117 "response_flags": "-", 3118 } 3119 q1m4 := model.Metric{ // good telem (mock service entry) 3120 "source_cluster": "cluster-tutorial", 3121 "source_workload_namespace": "tutorial", 3122 "source_workload": "customer-v1", 3123 "source_canonical_service": "customer", 3124 "source_canonical_revision": "v1", 3125 "destination_cluster": "cluster-bookinfo", 3126 "destination_service_namespace": "bookinfo", 3127 "destination_service": "app.example.com", 3128 "destination_service_name": "app.example.com", 3129 "destination_workload_namespace": "unknown", 3130 "destination_workload": "unknown", 3131 "destination_canonical_service": "unknown", 3132 "destination_canonical_revision": "unknown", 3133 "request_protocol": "http", 3134 "response_code": "200", 3135 "response_flags": "-", 3136 } 3137 // TODO: This is bad telemetry that should normally be filtered. But due to https://github.com/istio/istio/issues/29373 3138 // we are currently not filtering but rather setting dest_cluster = source_cluster. When 29373 is fixed for all 3139 // supported versions and we remove the workaround, results of the test will change. 3140 q1m5 := model.Metric{ // bad dest telem (variant 2.1) 3141 "source_cluster": "cluster-tutorial", 3142 "source_workload_namespace": "tutorial", 3143 "source_workload": "customer-v1", 3144 "source_canonical_service": "customer", 3145 "source_canonical_revision": "v1", 3146 "destination_cluster": "unknown", 3147 "destination_service_namespace": "bookinfo", 3148 "destination_service": "reviews", 3149 "destination_service_name": "reviews", 3150 "destination_workload_namespace": "bookinfo", 3151 "destination_workload": "reviews-v1", 3152 "destination_canonical_service": "reviews", 3153 "destination_canonical_revision": "v1", 3154 "request_protocol": "http", 3155 "response_code": "200", 3156 "response_flags": "-", 3157 } 3158 v1 := model.Vector{ 3159 &model.Sample{ 3160 Metric: q1m0, 3161 Value: 50, 3162 }, 3163 &model.Sample{ 3164 Metric: q1m1, 3165 Value: 50, 3166 }, 3167 &model.Sample{ 3168 Metric: q1m2, 3169 Value: 100, 3170 }, 3171 &model.Sample{ 3172 Metric: q1m3, 3173 Value: 200, 3174 }, 3175 &model.Sample{ 3176 Metric: q1m4, 3177 Value: 300, 3178 }, 3179 // see above 3180 &model.Sample{ 3181 Metric: q1m5, 3182 Value: 700, 3183 }, 3184 } 3185 3186 q2 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3187 v2 := model.Vector{} 3188 3189 q3 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3190 v3 := model.Vector{} 3191 3192 q4 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3193 v4 := model.Vector{} 3194 3195 q5 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3196 v5 := model.Vector{} 3197 3198 // tutorial 3199 q6 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="tutorial",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.tutorial\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3200 v6 := model.Vector{} 3201 3202 q7 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="tutorial"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3203 q7m0 := model.Metric{ 3204 "source_cluster": "unknown", 3205 "source_workload_namespace": "unknown", 3206 "source_workload": "unknown", 3207 "source_canonical_service": "unknown", 3208 "source_canonical_revision": "unknown", 3209 "destination_cluster": "cluster-tutorial", 3210 "destination_service_namespace": "tutorial", 3211 "destination_service": "customer:9080", 3212 "destination_service_name": "customer", 3213 "destination_workload_namespace": "tutorial", 3214 "destination_workload": "customer-v1", 3215 "destination_canonical_service": "customer", 3216 "destination_canonical_revision": "v1", 3217 "request_protocol": "grpc", 3218 "response_code": "200", 3219 "grpc_response_status": "0", 3220 "response_flags": "-", 3221 } 3222 q7m1 := model.Metric{ 3223 "source_cluster": "cluster-tutorial", 3224 "source_workload_namespace": "bad-source-telemetry-case-1", 3225 "source_workload": "unknown", 3226 "source_canonical_service": "unknown", 3227 "source_canonical_revision": "unknown", 3228 "destination_cluster": "cluster-tutorial", 3229 "destination_service_namespace": "tutorial", 3230 "destination_service": "customer:9080", 3231 "destination_service_name": "customer", 3232 "destination_workload_namespace": "tutorial", 3233 "destination_workload": "customer-v1", 3234 "destination_canonical_service": "customer", 3235 "destination_canonical_revision": "v1", 3236 "request_protocol": "grpc", 3237 "response_code": "200", 3238 "grpc_response_status": "0", 3239 "response_flags": "-", 3240 } 3241 v7 := model.Vector{ 3242 &model.Sample{ 3243 Metric: q7m0, 3244 Value: 50, 3245 }, 3246 &model.Sample{ 3247 Metric: q7m1, 3248 Value: 50, 3249 }, 3250 } 3251 3252 q8 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace="tutorial"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3253 q8m0 := model.Metric{ 3254 "source_cluster": "cluster-tutorial", 3255 "source_workload_namespace": "tutorial", 3256 "source_workload": "customer-v1", 3257 "source_canonical_service": "customer", 3258 "source_canonical_revision": "v1", 3259 "destination_cluster": "cluster-bookinfo", 3260 "destination_service_namespace": "bookinfo", 3261 "destination_service": "productpage:9080", 3262 "destination_service_name": "productpage", 3263 "destination_workload_namespace": "bookinfo", 3264 "destination_workload": "productpage-v1", 3265 "destination_canonical_service": "productpage", 3266 "destination_canonical_revision": "v1", 3267 "request_protocol": "http", 3268 "response_code": "200", 3269 "grpc_response_status": "0", 3270 "response_flags": "-", 3271 } 3272 q8m1 := model.Metric{ // bad dest telem (variant 1.0) 3273 "source_cluster": "cluster-tutorial", 3274 "source_workload_namespace": "tutorial", 3275 "source_workload": "customer-v1", 3276 "source_canonical_service": "customer", 3277 "source_canonical_revision": "v1", 3278 "destination_cluster": "cluster-bookinfo", 3279 "destination_service_namespace": "bookinfo", 3280 "destination_service": "10.20.30.40:9080", 3281 "destination_service_name": "10.20.30.40:9080", 3282 "destination_workload_namespace": "unknown", 3283 "destination_workload": "unknown", 3284 "destination_canonical_service": "unknown", 3285 "destination_canonical_revision": "unknown", 3286 "request_protocol": "http", 3287 "response_code": "200", 3288 "response_flags": "-", 3289 } 3290 q8m2 := model.Metric{ // bad dest telem (variant 1.2) 3291 "source_cluster": "cluster-tutorial", 3292 "source_workload_namespace": "tutorial", 3293 "source_workload": "customer-v1", 3294 "source_canonical_service": "customer", 3295 "source_canonical_revision": "v1", 3296 "destination_cluster": "cluster-bookinfo", 3297 "destination_service_namespace": "bookinfo", 3298 "destination_service": "10.20.30.40", 3299 "destination_service_name": "10.20.30.40", 3300 "destination_workload_namespace": "unknown", 3301 "destination_workload": "unknown", 3302 "destination_canonical_service": "unknown", 3303 "destination_canonical_revision": "unknown", 3304 "request_protocol": "http", 3305 "response_code": "200", 3306 "response_flags": "-", 3307 } 3308 q8m3 := model.Metric{ // good telem (mock service entry) 3309 "source_cluster": "cluster-tutorial", 3310 "source_workload_namespace": "tutorial", 3311 "source_workload": "customer-v1", 3312 "source_canonical_service": "customer", 3313 "source_canonical_revision": "v1", 3314 "destination_cluster": "cluster-bookinfo", 3315 "destination_service_namespace": "bookinfo", 3316 "destination_service": "app.example.com", 3317 "destination_service_name": "app.example.com", 3318 "destination_workload_namespace": "unknown", 3319 "destination_workload": "unknown", 3320 "destination_canonical_service": "unknown", 3321 "destination_canonical_revision": "unknown", 3322 "request_protocol": "http", 3323 "response_code": "200", 3324 "response_flags": "-", 3325 } 3326 q8m4 := model.Metric{ // good telem (service entry via egressgateway, see the second hop below) 3327 "source_cluster": "cluster-tutorial", 3328 "source_workload_namespace": "tutorial", 3329 "source_workload": "customer-v1", 3330 "source_canonical_service": "customer", 3331 "source_canonical_revision": "v1", 3332 "destination_cluster": "unknown", 3333 "destination_service_namespace": "unknown", 3334 "destination_service": "istio-egressgateway.istio-system.svc.cluster.local", 3335 "destination_service_name": "istio-egressgateway.istio-system.svc.cluster.local", 3336 "destination_workload_namespace": "unknown", 3337 "destination_workload": "unknown", 3338 "destination_canonical_service": "unknown", 3339 "destination_canonical_revision": "unknown", 3340 "request_protocol": "http", 3341 "response_code": "200", 3342 "response_flags": "-", 3343 } 3344 q8m5 := model.Metric{ // no response http 3345 "source_cluster": "cluster-tutorial", 3346 "source_workload_namespace": "tutorial", 3347 "source_workload": "customer-v1", 3348 "source_canonical_service": "customer", 3349 "source_canonical_revision": "v1", 3350 "destination_cluster": "unknown", 3351 "destination_service_namespace": "unknown", 3352 "destination_service": "istio-egressgateway.istio-system.svc.cluster.local", 3353 "destination_service_name": "istio-egressgateway.istio-system.svc.cluster.local", 3354 "destination_workload_namespace": "unknown", 3355 "destination_workload": "unknown", 3356 "destination_canonical_service": "unknown", 3357 "destination_canonical_revision": "unknown", 3358 "request_protocol": "http", 3359 "response_code": "0", 3360 "response_flags": "DC", 3361 } 3362 q8m6 := model.Metric{ // no response grpc 3363 "source_cluster": "cluster-tutorial", 3364 "source_workload_namespace": "tutorial", 3365 "source_workload": "customer-v1", 3366 "source_canonical_service": "customer", 3367 "source_canonical_revision": "v1", 3368 "destination_cluster": "unknown", 3369 "destination_service_namespace": "unknown", 3370 "destination_service": "istio-egressgateway.istio-system.svc.cluster.local", 3371 "destination_service_name": "istio-egressgateway.istio-system.svc.cluster.local", 3372 "destination_workload_namespace": "unknown", 3373 "destination_workload": "unknown", 3374 "destination_canonical_service": "unknown", 3375 "destination_canonical_revision": "unknown", 3376 "request_protocol": "grpc", 3377 "response_code": "0", // note, grpc_response_status is not reported for grpc with no response 3378 "response_flags": "DC", 3379 } 3380 // TODO: This is bad telemetry that should normally be filtered. But due to https://github.com/istio/istio/issues/29373 3381 // we are currently not filtering but rather setting dest_cluster = source_cluster. When 29373 is fixed for all 3382 // supported versions and we remove the workaround, results of the test will change. 3383 q8m7 := model.Metric{ // bad dest telem (variant 2.1) 3384 "source_cluster": "cluster-tutorial", 3385 "source_workload_namespace": "tutorial", 3386 "source_workload": "customer-v1", 3387 "source_canonical_service": "customer", 3388 "source_canonical_revision": "v1", 3389 "destination_cluster": "unknown", 3390 "destination_service_namespace": "bookinfo", 3391 "destination_service": "reviews", 3392 "destination_service_name": "reviews", 3393 "destination_workload_namespace": "bookinfo", 3394 "destination_workload": "reviews-v1", 3395 "destination_canonical_service": "reviews", 3396 "destination_canonical_revision": "v1", 3397 "request_protocol": "http", 3398 "response_code": "200", 3399 "response_flags": "-", 3400 } 3401 v8 := model.Vector{ 3402 &model.Sample{ 3403 Metric: q8m0, 3404 Value: 50, 3405 }, 3406 &model.Sample{ 3407 Metric: q8m1, 3408 Value: 100, 3409 }, 3410 &model.Sample{ 3411 Metric: q8m2, 3412 Value: 200, 3413 }, 3414 &model.Sample{ 3415 Metric: q8m3, 3416 Value: 300, 3417 }, 3418 &model.Sample{ 3419 Metric: q8m4, 3420 Value: 400, 3421 }, 3422 &model.Sample{ 3423 Metric: q8m5, 3424 Value: 500, 3425 }, 3426 &model.Sample{ 3427 Metric: q8m6, 3428 Value: 600, 3429 }, 3430 // see above 3431 &model.Sample{ 3432 Metric: q8m7, 3433 Value: 700, 3434 }, 3435 } 3436 3437 q9 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="tutorial",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.tutorial\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3438 v9 := model.Vector{} 3439 3440 q10 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="tutorial"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3441 v10 := model.Vector{} 3442 3443 q11 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace="tutorial"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3444 v11 := model.Vector{} 3445 3446 // istio-system 3447 q12 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="istio-system",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.istio-system\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3448 v12 := model.Vector{} 3449 3450 q13 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="istio-system"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3451 v13 := model.Vector{} 3452 3453 q14 := `round(sum(rate(istio_requests_total{mesh_id="mesh1",reporter="source",source_workload_namespace="istio-system"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) > 0,0.001)` 3454 q14m0 := model.Metric{ // good telem (service entry via egressgateway, the second hop) 3455 "source_cluster": "cluster-cp", 3456 "source_workload_namespace": "istio-system", 3457 "source_workload": "istio-egressgateway", 3458 "source_canonical_service": "istio-egressgateway", 3459 "source_canonical_revision": "latest", 3460 "destination_cluster": "unknown", 3461 "destination_service_namespace": "unknown", 3462 "destination_service": "app.example-2.com", 3463 "destination_service_name": "app.example-2.com", 3464 "destination_workload_namespace": "unknown", 3465 "destination_workload": "unknown", 3466 "destination_canonical_service": "unknown", 3467 "destination_canonical_revision": "unknown", 3468 "request_protocol": "http", 3469 "response_code": "200", 3470 "response_flags": "-", 3471 } 3472 v14 := model.Vector{ 3473 &model.Sample{ 3474 Metric: q14m0, 3475 Value: 400, 3476 }, 3477 } 3478 3479 q15 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace!="istio-system",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.istio-system\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3480 v15 := model.Vector{} 3481 3482 q16 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="destination",destination_workload_namespace="istio-system"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3483 v16 := model.Vector{} 3484 3485 q17 := `round(sum(rate(istio_tcp_received_bytes_total{mesh_id="mesh1",reporter="source",source_workload_namespace="istio-system"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) > 0,0.001)` 3486 v17 := model.Vector{} 3487 3488 clients := map[string]kubernetes.ClientInterface{ 3489 "cluster-tutorial": kubetest.NewFakeK8sClient( 3490 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 3491 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}, 3492 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "tutorial"}}, 3493 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-telemetry"}}, 3494 ), 3495 "cluster-cp": kubetest.NewFakeK8sClient( 3496 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 3497 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}, 3498 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "tutorial"}}, 3499 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-telemetry"}}, 3500 ), 3501 "cluster-bookinfo": kubetest.NewFakeK8sClient( 3502 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 3503 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}, 3504 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "tutorial"}}, 3505 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-telemetry"}}, 3506 ), 3507 } 3508 client, xapi, err := setupMockedWithIstioComponentNamespaces(t, "mesh1", clients) 3509 if err != nil { 3510 t.Error(err) 3511 return 3512 } 3513 mockQuery(xapi, q0, &v0) 3514 mockQuery(xapi, q1, &v1) 3515 mockQuery(xapi, q2, &v2) 3516 mockQuery(xapi, q3, &v3) 3517 mockQuery(xapi, q4, &v4) 3518 mockQuery(xapi, q5, &v5) 3519 mockQuery(xapi, q6, &v6) 3520 mockQuery(xapi, q7, &v7) 3521 mockQuery(xapi, q8, &v8) 3522 mockQuery(xapi, q9, &v9) 3523 mockQuery(xapi, q10, &v10) 3524 mockQuery(xapi, q11, &v11) 3525 mockQuery(xapi, q12, &v12) 3526 mockQuery(xapi, q13, &v13) 3527 mockQuery(xapi, q14, &v14) 3528 mockQuery(xapi, q15, &v15) 3529 mockQuery(xapi, q16, &v16) 3530 mockQuery(xapi, q17, &v17) 3531 3532 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 3533 3534 mr := mux.NewRouter() 3535 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 3536 func(w http.ResponseWriter, r *http.Request) { 3537 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 3538 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 3539 respond(w, code, config) 3540 })) 3541 3542 ts := httptest.NewServer(mr) 3543 defer ts.Close() 3544 3545 fut = graphNamespacesIstio 3546 url := ts.URL + "/api/namespaces/graph?graphType=versionedApp&appenders=&queryTime=1523364075&namespaces=bookinfo,tutorial,istio-system" 3547 resp, err := http.Get(url) 3548 if err != nil { 3549 t.Fatal(err) 3550 } 3551 actual, _ := io.ReadAll(resp.Body) 3552 expected, _ := os.ReadFile("testdata/test_complex_graph.expected") 3553 if runtime.GOOS == "windows" { 3554 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 3555 } 3556 expected = expected[:len(expected)-1] // remove EOF byte 3557 3558 assertObjectsEqual(t, expected, actual) 3559 assert.Equal(t, 200, resp.StatusCode) 3560 } 3561 3562 func TestMultiClusterSourceGraph(t *testing.T) { 3563 // bookinfo 3564 q0 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) ,0.001)` 3565 v0 := model.Vector{} 3566 3567 q1 := `round(sum(rate(istio_requests_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) ,0.001)` 3568 q1m0 := model.Metric{ 3569 "destination_canonical_revision": "v2", 3570 "destination_canonical_service": "reviews", 3571 "destination_cluster": "kukulcan", 3572 "destination_service": "reviews.bookinfo.svc.cluster.local", 3573 "destination_service_name": "reviews", 3574 "destination_service_namespace": "bookinfo", 3575 "destination_workload": "reviews-v2", 3576 "destination_workload_namespace": "bookinfo", 3577 "request_protocol": "http", 3578 "response_code": "200", 3579 "response_flags": "-", 3580 "source_canonical_revision": "v1", 3581 "source_canonical_service": "productpage", 3582 "source_cluster": "kukulcan", 3583 "source_workload": "productpage-v1", 3584 "source_workload_namespace": "bookinfo", 3585 } 3586 q1m1 := model.Metric{ 3587 "destination_canonical_revision": "v1", 3588 "destination_canonical_service": "details", 3589 "destination_cluster": "kukulcan", 3590 "destination_service": "details.bookinfo.svc.cluster.local", 3591 "destination_service_name": "details", 3592 "destination_service_namespace": "bookinfo", 3593 "destination_workload": "details-v1", 3594 "destination_workload_namespace": "bookinfo", 3595 "request_protocol": "http", 3596 "response_code": "200", 3597 "response_flags": "-", 3598 "source_canonical_revision": "v1", 3599 "source_canonical_service": "productpage", 3600 "source_cluster": "kukulcan", 3601 "source_workload": "productpage-v1", 3602 "source_workload_namespace": "bookinfo", 3603 } 3604 q1m2 := model.Metric{ 3605 "destination_canonical_revision": "v1", 3606 "destination_canonical_service": "productpage", 3607 "destination_cluster": "kukulcan", 3608 "destination_service": "productpage.bookinfo.svc.cluster.local", 3609 "destination_service_name": "productpage", 3610 "destination_service_namespace": "bookinfo", 3611 "destination_workload": "productpage-v1", 3612 "destination_workload_namespace": "bookinfo", 3613 "request_protocol": "http", 3614 "response_code": "200", 3615 "response_flags": "-", 3616 "source_canonical_revision": "latest", 3617 "source_canonical_service": "istio-ingressgateway", 3618 "source_cluster": "kukulcan", 3619 "source_workload": "istio-ingressgateway", 3620 "source_workload_namespace": "istio-system", 3621 } 3622 q1m3 := model.Metric{ 3623 "destination_canonical_revision": "v1", 3624 "destination_canonical_service": "reviews", 3625 "destination_cluster": "kukulcan", 3626 "destination_service": "reviews.bookinfo.svc.cluster.local", 3627 "destination_service_name": "reviews", 3628 "destination_service_namespace": "bookinfo", 3629 "destination_workload": "reviews-v1", 3630 "destination_workload_namespace": "bookinfo", 3631 "request_protocol": "http", 3632 "response_code": "200", 3633 "response_flags": "-", 3634 "source_canonical_revision": "v1", 3635 "source_canonical_service": "productpage", 3636 "source_cluster": "kukulcan", 3637 "source_workload": "productpage-v1", 3638 "source_workload_namespace": "bookinfo", 3639 } 3640 v1 := model.Vector{ 3641 &model.Sample{ 3642 Metric: q1m0, 3643 Value: 100, 3644 }, 3645 &model.Sample{ 3646 Metric: q1m1, 3647 Value: 100, 3648 }, 3649 &model.Sample{ 3650 Metric: q1m2, 3651 Value: 100, 3652 }, 3653 &model.Sample{ 3654 Metric: q1m3, 3655 Value: 100, 3656 }, 3657 } 3658 3659 q2 := `round(sum(rate(istio_requests_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,request_protocol,response_code,grpc_response_status,response_flags) ,0.001)` 3660 q2m0 := model.Metric{ 3661 "destination_canonical_revision": "v3", 3662 "destination_canonical_service": "reviews", 3663 "destination_cluster": "tzotz", 3664 "destination_service": "reviews.bookinfo.svc.cluster.local", 3665 "destination_service_name": "reviews", 3666 "destination_service_namespace": "bookinfo", 3667 "destination_workload": "reviews-v3", 3668 "destination_workload_namespace": "bookinfo", 3669 "request_protocol": "http", 3670 "response_code": "200", 3671 "response_flags": "-", 3672 "source_canonical_revision": "v1", 3673 "source_canonical_service": "productpage", 3674 "source_cluster": "kukulcan", 3675 "source_workload": "productpage-v1", 3676 "source_workload_namespace": "bookinfo", 3677 } 3678 q2m1 := model.Metric{ 3679 "destination_canonical_revision": "v1", 3680 "destination_canonical_service": "ratings", 3681 "destination_cluster": "tzotz", 3682 "destination_service": "ratings.bookinfo.svc.cluster.local", 3683 "destination_service_name": "ratings", 3684 "destination_service_namespace": "bookinfo", 3685 "destination_workload": "ratings-v1", 3686 "destination_workload_namespace": "bookinfo", 3687 "request_protocol": "http", 3688 "response_code": "200", 3689 "response_flags": "-", 3690 "source_canonical_revision": "v2", 3691 "source_canonical_service": "reviews", 3692 "source_cluster": "kukulcan", 3693 "source_workload": "reviews-v2", 3694 "source_workload_namespace": "bookinfo", 3695 } 3696 q2m2 := model.Metric{ 3697 "destination_canonical_revision": "v1", 3698 "destination_canonical_service": "details", 3699 "destination_cluster": "kukulcan", 3700 "destination_service": "details.bookinfo.svc.cluster.local", 3701 "destination_service_name": "details", 3702 "destination_service_namespace": "bookinfo", 3703 "destination_workload": "details-v1", 3704 "destination_workload_namespace": "bookinfo", 3705 "request_protocol": "http", 3706 "response_code": "200", 3707 "response_flags": "-", 3708 "source_canonical_revision": "v1", 3709 "source_canonical_service": "productpage", 3710 "source_cluster": "kukulcan", 3711 "source_workload": "productpage-v1", 3712 "source_workload_namespace": "bookinfo", 3713 } 3714 q2m3 := model.Metric{ 3715 "destination_canonical_revision": "v1", 3716 "destination_canonical_service": "reviews", 3717 "destination_cluster": "kukulcan", 3718 "destination_service": "reviews.bookinfo.svc.cluster.local", 3719 "destination_service_name": "reviews", 3720 "destination_service_namespace": "bookinfo", 3721 "destination_workload": "reviews-v1", 3722 "destination_workload_namespace": "bookinfo", 3723 "request_protocol": "http", 3724 "response_code": "200", 3725 "response_flags": "-", 3726 "source_canonical_revision": "v1", 3727 "source_canonical_service": "productpage", 3728 "source_cluster": "kukulcan", 3729 "source_workload": "productpage-v1", 3730 "source_workload_namespace": "bookinfo", 3731 } 3732 q2m4 := model.Metric{ 3733 "destination_canonical_revision": "v2", 3734 "destination_canonical_service": "reviews", 3735 "destination_cluster": "kukulcan", 3736 "destination_service": "reviews.bookinfo.svc.cluster.local", 3737 "destination_service_name": "reviews", 3738 "destination_service_namespace": "bookinfo", 3739 "destination_workload": "reviews-v2", 3740 "destination_workload_namespace": "bookinfo", 3741 "request_protocol": "http", 3742 "response_code": "200", 3743 "response_flags": "-", 3744 "source_canonical_revision": "v1", 3745 "source_canonical_service": "productpage", 3746 "source_cluster": "kukulcan", 3747 "source_workload": "productpage-v1", 3748 "source_workload_namespace": "bookinfo", 3749 } 3750 q2m5 := model.Metric{ 3751 "destination_canonical_revision": "v2", 3752 "destination_canonical_service": "reviews", 3753 "destination_cluster": "tzotz", 3754 "destination_service": "reviews.bookinfo.svc.cluster.local", 3755 "destination_service_name": "reviews", 3756 "destination_service_namespace": "bookinfo", 3757 "destination_workload": "reviews-v2", 3758 "destination_workload_namespace": "bookinfo", 3759 "request_protocol": "http", 3760 "response_code": "200", 3761 "response_flags": "-", 3762 "source_canonical_revision": "v1", 3763 "source_canonical_service": "productpage", 3764 "source_cluster": "kukulcan", 3765 "source_workload": "productpage-v1", 3766 "source_workload_namespace": "bookinfo", 3767 } 3768 // This is an additional test for #4488, done here because, unlike the other tests, this test is injecting service nodes 3769 q2m6 := model.Metric{ 3770 "destination_canonical_revision": "v1", 3771 "destination_canonical_service": "kiali#4488-dest", 3772 "destination_cluster": "tzotz", 3773 "destination_service": "10.2.3.4:8080", 3774 "destination_service_name": "PassthroughCluster", 3775 "destination_service_namespace": "bookinfo", 3776 "destination_workload": "kiali#4488-dest-v1", 3777 "destination_workload_namespace": "bookinfo", 3778 "request_protocol": "http", 3779 "response_code": "200", 3780 "response_flags": "-", 3781 "source_canonical_revision": "v1", 3782 "source_canonical_service": "kiali#4488-source", 3783 "source_cluster": "tzotz", 3784 "source_workload": "kiali#4488-source-v1", 3785 "source_workload_namespace": "bookinfo", 3786 } 3787 v2 := model.Vector{ 3788 &model.Sample{ 3789 Metric: q2m0, 3790 Value: 100, 3791 }, 3792 &model.Sample{ 3793 Metric: q2m1, 3794 Value: 100, 3795 }, 3796 &model.Sample{ 3797 Metric: q2m2, 3798 Value: 100, 3799 }, 3800 &model.Sample{ 3801 Metric: q2m3, 3802 Value: 100, 3803 }, 3804 &model.Sample{ 3805 Metric: q2m4, 3806 Value: 100, 3807 }, 3808 &model.Sample{ 3809 Metric: q2m5, 3810 Value: 100, 3811 }, 3812 &model.Sample{ 3813 Metric: q2m6, 3814 Value: 100, 3815 }, 3816 } 3817 3818 q3 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace!="bookinfo",destination_workload_namespace="unknown",destination_workload="unknown",destination_service=~"^.+\\.bookinfo\\..+$"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) ,0.001)` 3819 v3 := model.Vector{} 3820 3821 q4 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="destination",destination_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) ,0.001)` 3822 v4 := model.Vector{} 3823 3824 q5 := `round(sum(rate(istio_tcp_received_bytes_total{reporter="source",source_workload_namespace="bookinfo"} [600s])) by (source_cluster,source_workload_namespace,source_workload,source_canonical_service,source_canonical_revision,destination_cluster,destination_service_namespace,destination_service,destination_service_name,destination_workload_namespace,destination_workload,destination_canonical_service,destination_canonical_revision,response_flags) ,0.001)` 3825 v5 := model.Vector{} 3826 3827 clients := map[string]kubernetes.ClientInterface{ 3828 "kukulcan": kubetest.NewFakeK8sClient( 3829 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 3830 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}, 3831 ), 3832 "tzotz": kubetest.NewFakeK8sClient( 3833 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "bookinfo"}}, 3834 &core_v1.Namespace{ObjectMeta: meta_v1.ObjectMeta{Name: "istio-system"}}, 3835 ), 3836 } 3837 client, xapi, err := setupMockedWithIstioComponentNamespaces(t, "", clients) 3838 if err != nil { 3839 t.Error(err) 3840 return 3841 } 3842 3843 mockQuery(xapi, q0, &v0) 3844 mockQuery(xapi, q1, &v1) 3845 mockQuery(xapi, q2, &v2) 3846 mockQuery(xapi, q3, &v3) 3847 mockQuery(xapi, q4, &v4) 3848 mockQuery(xapi, q5, &v5) 3849 3850 var fut func(ctx context.Context, b *business.Layer, p *prometheus.Client, o graph.Options) (int, interface{}) 3851 3852 mr := mux.NewRouter() 3853 mr.HandleFunc("/api/namespaces/graph", http.HandlerFunc( 3854 func(w http.ResponseWriter, r *http.Request) { 3855 context := authentication.SetAuthInfoContext(r.Context(), &api.AuthInfo{Token: "test"}) 3856 code, config := fut(context, nil, client, graph.NewOptions(r.WithContext(context))) 3857 respond(w, code, config) 3858 })) 3859 3860 ts := httptest.NewServer(mr) 3861 defer ts.Close() 3862 3863 fut = graphNamespacesIstio 3864 url := ts.URL + "/api/namespaces/graph?graphType=versionedApp&injectServiceNodes=true&includeIdleEdges=true&appenders=&queryTime=1523364075&namespaces=bookinfo" 3865 resp, err := http.Get(url) 3866 if err != nil { 3867 t.Fatal(err) 3868 } 3869 actual, _ := io.ReadAll(resp.Body) 3870 expected, _ := os.ReadFile("testdata/test_mc_source_graph.expected") 3871 if runtime.GOOS == "windows" { 3872 expected = bytes.Replace(expected, []byte("\r\n"), []byte("\n"), -1) 3873 } 3874 expected = expected[:len(expected)-1] // remove EOF byte 3875 3876 assertObjectsEqual(t, expected, actual) 3877 assert.Equal(t, 200, resp.StatusCode) 3878 }