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 }