github.com/cilium/cilium@v1.16.2/pkg/hubble/metrics/http/handler_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package http 5 6 import ( 7 "context" 8 "strings" 9 "testing" 10 11 "github.com/prometheus/client_golang/prometheus" 12 "github.com/prometheus/client_golang/prometheus/testutil" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 16 pb "github.com/cilium/cilium/api/v1/flow" 17 "github.com/cilium/cilium/pkg/hubble/metrics/api" 18 ) 19 20 func Test_httpHandler_Status(t *testing.T) { 21 plugin := httpPlugin{} 22 handler := plugin.NewHandler() 23 assert.Equal(t, handler.Status(), "") 24 options := map[string]string{"sourceContext": "namespace", "destinationContext": "identity"} 25 require.NoError(t, handler.Init(prometheus.NewRegistry(), options)) 26 assert.Equal(t, handler.Status(), "destination=identity,source=namespace,exemplars=false") 27 } 28 29 func Test_httpHandler_ProcessFlow(t *testing.T) { 30 ctx := context.TODO() 31 plugin := httpPlugin{} 32 handler := plugin.NewHandler() 33 require.Error(t, handler.Init(prometheus.NewRegistry(), map[string]string{"destinationContext": "invalid"})) 34 require.NoError(t, handler.Init(prometheus.NewRegistry(), nil)) 35 fp, ok := handler.(api.FlowProcessor) 36 require.True(t, ok) 37 // shouldn't count 38 fp.ProcessFlow(ctx, &pb.Flow{}) 39 // shouldn't count 40 fp.ProcessFlow(ctx, &pb.Flow{L7: &pb.Layer7{ 41 Type: pb.L7FlowType_RESPONSE, 42 Record: &pb.Layer7_Dns{}, 43 }}) 44 // should count for request 45 fp.ProcessFlow(ctx, &pb.Flow{ 46 TrafficDirection: pb.TrafficDirection_INGRESS, 47 L7: &pb.Layer7{ 48 Type: pb.L7FlowType_REQUEST, 49 Record: &pb.Layer7_Http{Http: &pb.HTTP{ 50 Method: "GET", 51 }}, 52 }, 53 }) 54 // should count for response 55 fp.ProcessFlow(ctx, &pb.Flow{ 56 TrafficDirection: pb.TrafficDirection_INGRESS, 57 L7: &pb.Layer7{ 58 Type: pb.L7FlowType_RESPONSE, 59 LatencyNs: 12345678, 60 Record: &pb.Layer7_Http{Http: &pb.HTTP{ 61 Code: 200, 62 Method: "GET", 63 }}, 64 }, 65 }) 66 requestsExpected := ` 67 # HELP hubble_http_requests_total Count of HTTP requests 68 # TYPE hubble_http_requests_total counter 69 hubble_http_requests_total{method="GET",protocol="",reporter="server"} 1 70 ` 71 require.NoError(t, testutil.CollectAndCompare(handler.(*httpHandler).requests, strings.NewReader(requestsExpected))) 72 responsesExpected := ` 73 # HELP hubble_http_responses_total Count of HTTP responses 74 # TYPE hubble_http_responses_total counter 75 hubble_http_responses_total{method="GET",protocol="",reporter="server",status="200"} 1 76 ` 77 require.NoError(t, testutil.CollectAndCompare(handler.(*httpHandler).responses, strings.NewReader(responsesExpected))) 78 79 durationExpected := ` 80 # HELP hubble_http_request_duration_seconds Quantiles of HTTP request duration in seconds 81 # TYPE hubble_http_request_duration_seconds histogram 82 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.005"} 0 83 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.01"} 0 84 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.025"} 1 85 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.05"} 1 86 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.1"} 1 87 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.25"} 1 88 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="0.5"} 1 89 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="1"} 1 90 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="2.5"} 1 91 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="5"} 1 92 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="10"} 1 93 hubble_http_request_duration_seconds_bucket{method="GET",reporter="server",le="+Inf"} 1 94 hubble_http_request_duration_seconds_sum{method="GET",reporter="server"} 0.012345678 95 hubble_http_request_duration_seconds_count{method="GET",reporter="server"} 1 96 ` 97 require.NoError(t, testutil.CollectAndCompare(handler.(*httpHandler).duration, strings.NewReader(durationExpected))) 98 } 99 100 func Test_httpHandlerV2_ProcessFlow(t *testing.T) { 101 ctx := context.TODO() 102 plugin := httpV2Plugin{} 103 handler := plugin.NewHandler() 104 require.Error(t, handler.Init(prometheus.NewRegistry(), map[string]string{"destinationContext": "invalid"})) 105 require.NoError(t, handler.Init(prometheus.NewRegistry(), map[string]string{ 106 "sourceContext": "pod", 107 "destinationContext": "pod", 108 "labelsContext": "source_pod,destination_pod", 109 })) 110 111 fp, ok := handler.(api.FlowProcessor) 112 require.True(t, ok) 113 114 // shouldn't count 115 fp.ProcessFlow(ctx, &pb.Flow{}) 116 // shouldn't count 117 fp.ProcessFlow(ctx, &pb.Flow{ 118 TrafficDirection: pb.TrafficDirection_INGRESS, 119 L7: &pb.Layer7{ 120 Type: pb.L7FlowType_RESPONSE, 121 Record: &pb.Layer7_Dns{}, 122 }}) 123 // shouldn't count for request, we use responses in v2 124 fp.ProcessFlow(ctx, &pb.Flow{ 125 TrafficDirection: pb.TrafficDirection_INGRESS, 126 L7: &pb.Layer7{ 127 Type: pb.L7FlowType_REQUEST, 128 Record: &pb.Layer7_Http{Http: &pb.HTTP{ 129 Method: "GET", 130 }}, 131 }, 132 }) 133 134 sourceEndpoint := &pb.Endpoint{ 135 Namespace: "source-ns", 136 PodName: "source-deploy-pod", 137 Workloads: []*pb.Workload{{ 138 Name: "source-deploy", 139 Kind: "Deployment", 140 }}, 141 Labels: []string{ 142 "k8s:app=sourceapp", 143 }, 144 } 145 destinationEndpoint := &pb.Endpoint{ 146 Namespace: "destination-ns", 147 PodName: "destination-deploy-pod", 148 Workloads: []*pb.Workload{{ 149 Name: "destination-deploy", 150 Kind: "Deployment", 151 }}, 152 Labels: []string{ 153 "k8s:app=destinationapp", 154 }, 155 } 156 // should count for request 157 fp.ProcessFlow(ctx, &pb.Flow{ 158 TrafficDirection: pb.TrafficDirection_INGRESS, 159 // Responses have the source and destination inverted, because it's the 160 // other side of the flow. Our tests are asserting that the HTTPv2 handler 161 // correctly inverts them so the source and destination are from the 162 // perspective of the request. 163 Source: destinationEndpoint, 164 Destination: sourceEndpoint, 165 L7: &pb.Layer7{ 166 Type: pb.L7FlowType_RESPONSE, 167 LatencyNs: 12345678, 168 Record: &pb.Layer7_Http{Http: &pb.HTTP{ 169 Protocol: "HTTP/1.1", 170 Code: 200, 171 Method: "GET", 172 }}, 173 }, 174 }) 175 requestsExpected := ` 176 # HELP hubble_http_requests_total Count of HTTP requests 177 # TYPE hubble_http_requests_total counter 178 hubble_http_requests_total{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",protocol="HTTP/1.1",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",status="200"} 1 179 ` 180 assert.NoError(t, testutil.CollectAndCompare(handler.(*httpHandler).requests, strings.NewReader(requestsExpected))) 181 durationExpected := ` 182 # HELP hubble_http_request_duration_seconds Quantiles of HTTP request duration in seconds 183 # TYPE hubble_http_request_duration_seconds histogram 184 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.005"} 0 185 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.01"} 0 186 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.025"} 1 187 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.05"} 1 188 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.1"} 1 189 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.25"} 1 190 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="0.5"} 1 191 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="1"} 1 192 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="2.5"} 1 193 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="5"} 1 194 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="10"} 1 195 hubble_http_request_duration_seconds_bucket{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod",le="+Inf"} 1 196 hubble_http_request_duration_seconds_sum{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod"} 0.012345678 197 hubble_http_request_duration_seconds_count{destination="destination-ns/destination-deploy-pod",destination_pod="destination-deploy-pod",method="GET",reporter="server",source="source-ns/source-deploy-pod",source_pod="source-deploy-pod"} 1 198 ` 199 require.NoError(t, testutil.CollectAndCompare(handler.(*httpHandler).duration, strings.NewReader(durationExpected))) 200 } 201 202 func Test_httpHandler_ListMetricVec(t *testing.T) { 203 plugin := httpPlugin{} 204 handler := plugin.NewHandler() 205 require.NoError(t, handler.Init(prometheus.NewRegistry(), nil)) 206 assert.Len(t, handler.ListMetricVec(), 3, "expecting 3 metrics, requests, responses and duration") 207 for _, vec := range handler.ListMetricVec() { 208 require.NotNil(t, vec, "ListMetricVec should not nil metrics vectors") 209 } 210 } 211 212 func Test_httpV2Handler_ListMetricVec(t *testing.T) { 213 plugin := httpV2Plugin{} 214 handler := plugin.NewHandler() 215 require.NoError(t, handler.Init(prometheus.NewRegistry(), nil)) 216 assert.Len(t, handler.ListMetricVec(), 2, "expecting 2 metrics, requests and duration") 217 for _, vec := range handler.ListMetricVec() { 218 require.NotNil(t, vec, "ListMetricVec should not nil metrics vectors") 219 } 220 }