istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/telemetry/api/customize_metrics_test.go (about)

     1  //go:build integ
     2  // +build integ
     3  
     4  // Copyright Istio Authors. All Rights Reserved.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //     http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package api
    19  
    20  import (
    21  	_ "embed"
    22  	"fmt"
    23  	"strings"
    24  	"testing"
    25  	"time"
    26  
    27  	"istio.io/istio/pkg/test/framework"
    28  	"istio.io/istio/pkg/test/framework/components/echo"
    29  	"istio.io/istio/pkg/test/framework/components/prometheus"
    30  	"istio.io/istio/pkg/test/framework/label"
    31  	"istio.io/istio/pkg/test/util/retry"
    32  	util "istio.io/istio/tests/integration/telemetry"
    33  )
    34  
    35  const (
    36  	removedTag   = "source_principal"
    37  	httpProtocol = "http"
    38  	grpcProtocol = "grpc"
    39  )
    40  
    41  func TestCustomizeMetrics(t *testing.T) {
    42  	framework.NewTest(t).
    43  		Label(label.IPv4). // https://github.com/istio/istio/issues/35835
    44  		Run(func(t framework.TestContext) {
    45  			setupWasmExtension(t)
    46  			t.ConfigIstio().YAML(apps.Namespace.Name(), `
    47  apiVersion: telemetry.istio.io/v1alpha1
    48  kind: Telemetry
    49  metadata:
    50    name: ns-default
    51  spec:
    52    metrics:
    53    - providers:
    54      - name: prometheus
    55      overrides:
    56      - match:
    57          metric: REQUEST_COUNT
    58        tagOverrides:
    59          response_code:
    60            value: "istio_responseClass"
    61          request_operation: 
    62            value: istio_operationId
    63          grpc_response_status: 
    64            value: istio_grpcResponseStatus
    65          custom_dimension: 
    66            value: "'test'"
    67          source_principal:
    68            operation: REMOVE
    69  
    70  `).ApplyOrFail(t)
    71  			t.Cleanup(func() {
    72  				if t.Failed() {
    73  					util.PromDump(t.Clusters().Default(), promInst, prometheus.Query{Metric: "istio_requests_total"})
    74  				}
    75  			})
    76  			httpDestinationQuery := buildCustomMetricsQuery(httpProtocol)
    77  			grpcDestinationQuery := buildCustomMetricsQuery(grpcProtocol)
    78  			var httpMetricVal string
    79  			cluster := t.Clusters().Default()
    80  			httpChecked := false
    81  			retry.UntilSuccessOrFail(t, func() error {
    82  				if err := sendCustomizeMetricsTraffic(); err != nil {
    83  					t.Log("failed to send traffic")
    84  					return err
    85  				}
    86  				var err error
    87  				if !httpChecked {
    88  					httpMetricVal, err = util.QueryPrometheus(t, cluster, httpDestinationQuery, promInst)
    89  					if err != nil {
    90  						util.PromDiff(t, promInst, cluster, httpDestinationQuery)
    91  						return err
    92  					}
    93  					httpChecked = true
    94  				}
    95  				_, err = util.QueryPrometheus(t, cluster, grpcDestinationQuery, promInst)
    96  				if err != nil {
    97  					util.PromDiff(t, promInst, cluster, grpcDestinationQuery)
    98  					return err
    99  				}
   100  				return nil
   101  			}, retry.Delay(1*time.Second), retry.Timeout(300*time.Second))
   102  			// check tag removed
   103  			if strings.Contains(httpMetricVal, removedTag) {
   104  				t.Errorf("failed to remove tag: %v", removedTag)
   105  			}
   106  			util.ValidateMetric(t, cluster, promInst, httpDestinationQuery, 1)
   107  			util.ValidateMetric(t, cluster, promInst, grpcDestinationQuery, 1)
   108  			// By default, envoy histogram has 20 buckets, annotation changes it to 10
   109  			if err := ValidateBucket(cluster, promInst, "a", "destination", 10); err != nil {
   110  				t.Errorf("failed to validate bucket: %v", err)
   111  			}
   112  		})
   113  }
   114  
   115  func setupWasmExtension(t framework.TestContext) {
   116  	proxySHA := "359dcd3a19f109c50e97517fe6b1e2676e870c4d"
   117  	attrGenImageURL := fmt.Sprintf("oci://%v/istio-testing/wasm/attributegen:%v", registry.Address(), proxySHA)
   118  	args := map[string]any{
   119  		"AttributeGenURL": attrGenImageURL,
   120  	}
   121  	t.ConfigIstio().
   122  		EvalFile(apps.Namespace.Name(), args, "testdata/attributegen.yaml").
   123  		ApplyOrFail(t)
   124  }
   125  
   126  func sendCustomizeMetricsTraffic() error {
   127  	for _, cltInstance := range GetClientInstances() {
   128  		httpOpts := echo.CallOptions{
   129  			To: GetTarget(),
   130  			Port: echo.Port{
   131  				Name: "http",
   132  			},
   133  			HTTP: echo.HTTP{
   134  				Path:   "/path",
   135  				Method: "GET",
   136  			},
   137  			Retry: echo.Retry{
   138  				NoRetry: true,
   139  			},
   140  		}
   141  
   142  		if _, err := cltInstance.Call(httpOpts); err != nil {
   143  			return err
   144  		}
   145  
   146  		httpOpts.HTTP.Method = "POST"
   147  		if _, err := cltInstance.Call(httpOpts); err != nil {
   148  			return err
   149  		}
   150  
   151  		grpcOpts := echo.CallOptions{
   152  			To: GetTarget(),
   153  			Port: echo.Port{
   154  				Name: "grpc",
   155  			},
   156  		}
   157  		if _, err := cltInstance.Call(grpcOpts); err != nil {
   158  			return err
   159  		}
   160  	}
   161  
   162  	return nil
   163  }
   164  
   165  func buildCustomMetricsQuery(protocol string) (destinationQuery prometheus.Query) {
   166  	labels := map[string]string{
   167  		"request_protocol":               "http",
   168  		"response_code":                  "2xx",
   169  		"destination_app":                "b",
   170  		"destination_version":            "v1",
   171  		"destination_service":            "b." + apps.Namespace.Name() + ".svc.cluster.local",
   172  		"destination_service_name":       "b",
   173  		"destination_workload_namespace": apps.Namespace.Name(),
   174  		"destination_service_namespace":  apps.Namespace.Name(),
   175  		"source_app":                     "a",
   176  		"source_version":                 "v1",
   177  		"source_workload":                "a-v1",
   178  		"source_workload_namespace":      apps.Namespace.Name(),
   179  		"custom_dimension":               "test",
   180  	}
   181  	if protocol == httpProtocol {
   182  		labels["request_operation"] = "getoperation"
   183  	}
   184  	if protocol == grpcProtocol {
   185  		labels["grpc_response_status"] = "OK"
   186  		labels["request_protocol"] = "grpc"
   187  	}
   188  
   189  	_, destinationQuery, _ = BuildQueryCommon(labels, apps.Namespace.Name())
   190  	return destinationQuery
   191  }