github.com/lfch/etcd-io/tests/v3@v3.0.0-20221004140520-eac99acd3e9d/integration/clientv3/metrics_test.go (about) 1 // Copyright 2016 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package clientv3test 16 17 import ( 18 "bufio" 19 "context" 20 "io" 21 "net" 22 "net/http" 23 "strconv" 24 "strings" 25 "testing" 26 "time" 27 28 grpcprom "github.com/grpc-ecosystem/go-grpc-prometheus" 29 "github.com/lfch/etcd-io/client/pkg/v3/transport" 30 clientv3 "github.com/lfch/etcd-io/client/v3" 31 integration2 "github.com/lfch/etcd-io/tests/v3/framework/integration" 32 "github.com/prometheus/client_golang/prometheus/promhttp" 33 "google.golang.org/grpc" 34 ) 35 36 func TestV3ClientMetrics(t *testing.T) { 37 integration2.BeforeTest(t) 38 39 var ( 40 addr = "localhost:27989" 41 ln net.Listener 42 ) 43 44 srv := &http.Server{Handler: promhttp.Handler()} 45 srv.SetKeepAlivesEnabled(false) 46 47 ln, err := transport.NewUnixListener(addr) 48 if err != nil { 49 t.Errorf("Error: %v occurred while listening on addr: %v", err, addr) 50 } 51 52 donec := make(chan struct{}) 53 defer func() { 54 ln.Close() 55 <-donec 56 }() 57 58 // listen for all Prometheus metrics 59 60 go func() { 61 var err error 62 63 defer close(donec) 64 65 err = srv.Serve(ln) 66 if err != nil && !transport.IsClosedConnError(err) { 67 t.Errorf("Err serving http requests: %v", err) 68 } 69 }() 70 71 url := "unix://" + addr + "/metrics" 72 73 clus := integration2.NewCluster(t, &integration2.ClusterConfig{Size: 1}) 74 defer clus.Terminate(t) 75 76 cfg := clientv3.Config{ 77 Endpoints: []string{clus.Members[0].GRPCURL()}, 78 DialOptions: []grpc.DialOption{ 79 grpc.WithUnaryInterceptor(grpcprom.UnaryClientInterceptor), 80 grpc.WithStreamInterceptor(grpcprom.StreamClientInterceptor), 81 }, 82 } 83 cli, cerr := integration2.NewClient(t, cfg) 84 if cerr != nil { 85 t.Fatal(cerr) 86 } 87 defer cli.Close() 88 89 wc := cli.Watch(context.Background(), "foo") 90 91 wBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream") 92 93 pBefore := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary") 94 95 _, err = cli.Put(context.Background(), "foo", "bar") 96 if err != nil { 97 t.Errorf("Error putting value in key store") 98 } 99 100 pAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_started_total", "Put", "unary") 101 if pBefore+1 != pAfter { 102 t.Errorf("grpc_client_started_total expected %d, got %d", 1, pAfter-pBefore) 103 } 104 105 // consume watch response 106 select { 107 case <-wc: 108 case <-time.After(10 * time.Second): 109 t.Error("Timeout occurred for getting watch response") 110 } 111 112 wAfter := sumCountersForMetricAndLabels(t, url, "grpc_client_msg_received_total", "Watch", "bidi_stream") 113 if wBefore+1 != wAfter { 114 t.Errorf("grpc_client_msg_received_total expected %d, got %d", 1, wAfter-wBefore) 115 } 116 } 117 118 func sumCountersForMetricAndLabels(t *testing.T, url string, metricName string, matchingLabelValues ...string) int { 119 count := 0 120 for _, line := range getHTTPBodyAsLines(t, url) { 121 ok := true 122 if !strings.HasPrefix(line, metricName) { 123 continue 124 } 125 126 for _, labelValue := range matchingLabelValues { 127 if !strings.Contains(line, `"`+labelValue+`"`) { 128 ok = false 129 break 130 } 131 } 132 133 if !ok { 134 continue 135 } 136 137 valueString := line[strings.LastIndex(line, " ")+1 : len(line)-1] 138 valueFloat, err := strconv.ParseFloat(valueString, 32) 139 if err != nil { 140 t.Fatalf("failed parsing value for line: %v and matchingLabelValues: %v", line, matchingLabelValues) 141 } 142 count += int(valueFloat) 143 } 144 return count 145 } 146 147 func getHTTPBodyAsLines(t *testing.T, url string) []string { 148 cfgtls := transport.TLSInfo{} 149 tr, err := transport.NewTransport(cfgtls, time.Second) 150 if err != nil { 151 t.Fatalf("Error getting transport: %v", err) 152 } 153 154 tr.MaxIdleConns = -1 155 tr.DisableKeepAlives = true 156 157 cli := &http.Client{Transport: tr} 158 159 resp, err := cli.Get(url) 160 if err != nil { 161 t.Fatalf("Error fetching: %v", err) 162 } 163 164 reader := bufio.NewReader(resp.Body) 165 var lines []string 166 for { 167 line, err := reader.ReadString('\n') 168 if err != nil { 169 if err == io.EOF { 170 break 171 } else { 172 t.Fatalf("error reading: %v", err) 173 } 174 } 175 lines = append(lines, line) 176 } 177 resp.Body.Close() 178 return lines 179 }