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  }