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  }