github.com/kiali/kiali@v1.84.0/graph/telemetry/istio/util/util.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "regexp" 6 "strings" 7 8 "github.com/prometheus/common/model" 9 10 "github.com/kiali/kiali/config" 11 "github.com/kiali/kiali/graph" 12 "github.com/kiali/kiali/log" 13 "github.com/kiali/kiali/prometheus" 14 ) 15 16 // badServiceMatcher looks for a physical IP address with optional port (e.g. 10.11.12.13:80) 17 var badServiceMatcher = regexp.MustCompile(`^\d+\.\d+\.\d+\.\d+(:\d+)?$`) 18 var egressHost string 19 20 // HandleClusters just sets source an dest cluster to unknown if it is not supplied on the telemetry 21 // TODO: Starting in Istio 1.9 source_cluster and destination_cluster are always reported. So, this 22 // function can be removed when the Kiali version can assume Istio 1.9 or later. 23 func HandleClusters(lSourceCluster model.LabelValue, sourceClusterOk bool, lDestCluster model.LabelValue, destClusterOk bool) (sourceCluster, destCluster string) { 24 if sourceClusterOk { 25 sourceCluster = string(lSourceCluster) 26 } else { 27 sourceCluster = graph.Unknown 28 } 29 if destClusterOk { 30 destCluster = string(lDestCluster) 31 } else { 32 destCluster = graph.Unknown 33 } 34 return sourceCluster, destCluster 35 } 36 37 // HandleDestination modifies the destination information, when necessary, for various corner 38 // cases. It should be called after source validation and before destination processing. 39 // Returns destSvcNs, destSvcName, destWlNs, destWl, destApp, destVersion, isupdated 40 func HandleDestination(sourceCluster, sourceWlNs, sourceWl, destCluster, destSvcNs, destSvc, destSvcName, destWlNs, destWl, destApp, destVer string) (string, string, string, string, string, string, string, bool) { 41 // Handle egressgateway (kiali#2999) 42 if egressHost == "" { 43 egressHost = fmt.Sprintf("istio-egressgateway.%s.svc.cluster.local", config.Get().IstioNamespace) 44 } 45 46 if destSvc == egressHost && destSvc == destSvcName { 47 istioNs := config.Get().IstioNamespace 48 log.Debugf("HandleDestination: destCluster=%s, destSvcNs=%s", sourceCluster, istioNs) 49 return sourceCluster, istioNs, "istio-egressgateway", istioNs, "istio-egressgateway", "istio-egressgateway", "latest", true 50 } 51 52 // TODO: Below we are adding best-effort handling for https://github.com/istio/istio/issues/29373. That 53 // bug can cause destination_cluster to be incorrectly reported as "unknown". In that situation 54 // we are ASSUMING the destination_cluster is the same as the source_cluster. This is invalid 55 // if the request is for a different cluster AND the reporting is wrong, but leaving it as "unknown" 56 // causes strange graph behavior in the more typical, same-cluster, case. The bug is scheduled to 57 // be fixed in Istio 1.10.0. Remove handling when bug s resolved for all supported Istio versions. 58 if !graph.IsOK(destCluster) && graph.IsOK(sourceCluster) && graph.IsOK(destSvcNs) { 59 log.Debugf("Handling Istio#29373, resetting destination_cluster from [%s] to [%s]", destCluster, sourceCluster) 60 destCluster = sourceCluster 61 } 62 63 return destCluster, destSvcNs, destSvcName, destWlNs, destWl, destApp, destVer, false 64 } 65 66 // HandleResponseCode determines the proper response code based on how istio has set the response_code and 67 // grpc_response_status attributes. grpc_response_status was added upstream in Istio 1.5 and downstream 68 // in OSSM 1.1. We support it here in a backward compatible way. 69 // return "-" for requests that did not receive a response, regardless of protocol 70 // return HTTP response code when: 71 // - protocol is not GRPC 72 // - the version running does not supply the GRPC status 73 // - the protocol is GRPC but the HTTP transport fails (i.e. an HTTP error is reported, rare). 74 // 75 // return the GRPC status, otherwise. 76 func HandleResponseCode(protocol, responseCode string, grpcResponseStatusOk bool, grpcResponseStatus string) string { 77 // Istio sets response_code to 0 to indicate "no response" regardless of protocol. 78 if responseCode == "0" { 79 return "-" 80 } 81 82 // when not "0" responseCode holds the HTTP response status code for HTTP or GRPC requests 83 if protocol != graph.GRPC.Name || graph.IsHTTPErr(responseCode) || !grpcResponseStatusOk { 84 return responseCode 85 } 86 87 return grpcResponseStatus 88 } 89 90 // IsBadSourceTelemetry tests for known issues in generated telemetry given indicative label values. 91 // 1) source namespace is ok but neither workload nor app are set 92 // 2) source namespace is ok and source_cluster is provided but not ok. 93 // 3) no more conditions known 94 func IsBadSourceTelemetry(cluster string, clusterOK bool, ns, wl, app string) bool { 95 // case1 96 if graph.IsOK(ns) && !graph.IsOK(wl) && !graph.IsOK(app) { 97 log.Debugf("Skipping bad source telemetry [case 1] [%s] [%s] [%s]", ns, wl, app) 98 return true 99 } 100 // case2 101 if graph.IsOK(ns) && clusterOK && !graph.IsOK(cluster) { 102 log.Debugf("Skipping bad source telemetry [case 2] [%s] [%s] [%s] [%s]", ns, wl, app, cluster) 103 return true 104 } 105 106 return false 107 } 108 109 // IsBadDestTelemetry tests for known issues in generated telemetry given indicative label values. 110 // 1. During pod lifecycle changes incomplete telemetry may be generated that results in 111 // destSvc == destSvcName and no dest workload, where destSvc[Name] is in the form of an IP address. 112 // 2. destSvcNs is ok and destCluster is provided but not ok 113 // 3. no more conditions known 114 func IsBadDestTelemetry(cluster string, clusterOK bool, svcNs, svc, svcName, wl string) bool { 115 // case1 116 failsEqualsTest := (!graph.IsOK(wl) && graph.IsOK(svc) && graph.IsOK(svcName) && (svc == svcName)) 117 if failsEqualsTest && badServiceMatcher.MatchString(svcName) { 118 log.Debugf("Skipping bad dest telemetry [case 1] [%s] [%s] [%s]", svc, svcName, wl) 119 return true 120 } 121 // case2 122 if graph.IsOK(svcNs) && clusterOK && !graph.IsOK(cluster) { 123 log.Debugf("Skipping bad dest telemetry [case 2] [%s] [%s]", svcNs, cluster) 124 return true 125 } 126 return false 127 } 128 129 // AddQueryScope returns the prom query unchanged if there is no configured queryScope, otherwise 130 // it returns the query with the queryScope injected after each occurrence of a leading '{'. 131 func AddQueryScope(query string) string { 132 queryScope := config.Get().ExternalServices.Prometheus.QueryScope 133 if len(queryScope) == 0 { 134 return query 135 } 136 137 scope := "{" 138 for labelName, labelValue := range queryScope { 139 scope = fmt.Sprintf("%s%s=\"%s\",", scope, prometheus.SanitizeLabelName(labelName), labelValue) 140 } 141 142 return strings.ReplaceAll(query, "{", scope) 143 }