github.com/cilium/cilium@v1.16.2/pkg/hubble/metrics/api/registry_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Hubble 3 4 package api 5 6 import ( 7 "context" 8 "io" 9 "testing" 10 11 "github.com/prometheus/client_golang/prometheus" 12 "github.com/sirupsen/logrus" 13 "github.com/stretchr/testify/assert" 14 "github.com/stretchr/testify/require" 15 16 pb "github.com/cilium/cilium/api/v1/flow" 17 slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 18 "github.com/cilium/cilium/pkg/k8s/types" 19 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 20 ) 21 22 type testPlugin struct { 23 handler Handler 24 } 25 26 func (t *testPlugin) NewHandler() Handler { 27 return t.handler 28 } 29 30 func (t *testPlugin) HelpText() string { 31 return "" 32 } 33 34 type testHandler struct { 35 ContextOptions *ContextOptions 36 counter *prometheus.CounterVec 37 ProcessCalled int 38 InitCalled int 39 ListMetricCalled int 40 } 41 42 func (t *testHandler) Init(registry *prometheus.Registry, options Options) error { 43 t.InitCalled++ 44 return nil 45 } 46 47 func (t *testHandler) Status() string { 48 return "" 49 } 50 51 func (t *testHandler) Context() *ContextOptions { 52 return t.ContextOptions 53 } 54 55 func (t *testHandler) ListMetricVec() []*prometheus.MetricVec { 56 t.ListMetricCalled++ 57 return []*prometheus.MetricVec{t.counter.MetricVec} 58 } 59 60 func (t *testHandler) ProcessFlow(ctx context.Context, p *pb.Flow) error { 61 labels, _ := t.ContextOptions.GetLabelValues(p) 62 t.counter.WithLabelValues(labels...).Inc() 63 t.ProcessCalled++ 64 return nil 65 } 66 67 func TestRegister(t *testing.T) { 68 69 flow1 := &pb.Flow{ 70 EventType: &pb.CiliumEventType{Type: monitorAPI.MessageTypeAccessLog}, 71 L7: &pb.Layer7{ 72 Record: &pb.Layer7_Http{Http: &pb.HTTP{}}, 73 }, 74 Source: &pb.Endpoint{Namespace: "foo", PodName: "foo-123", Workloads: []*pb.Workload{{Name: "worker"}}}, 75 Destination: &pb.Endpoint{Namespace: "bar", PodName: "bar-123", Workloads: []*pb.Workload{{Name: "api"}}}, 76 Verdict: pb.Verdict_FORWARDED, 77 } 78 79 flow2 := &pb.Flow{ 80 EventType: &pb.CiliumEventType{Type: monitorAPI.MessageTypeAccessLog}, 81 L7: &pb.Layer7{ 82 Record: &pb.Layer7_Http{Http: &pb.HTTP{}}, 83 }, 84 Source: &pb.Endpoint{Namespace: "abc", PodName: "abc-456", Workloads: []*pb.Workload{{Name: "worker"}}}, 85 Destination: &pb.Endpoint{Namespace: "bar", PodName: "bar-123", Workloads: []*pb.Workload{{Name: "api"}}}, 86 Verdict: pb.Verdict_FORWARDED, 87 } 88 log := logrus.New() 89 log.SetOutput(io.Discard) 90 91 t.Run("Should not register handler", func(t *testing.T) { 92 93 r := NewRegistry(log) 94 95 handler := &testHandler{} 96 97 r.Register("test", &testPlugin{handler: handler}) 98 99 handlers, err := r.ConfigureHandlers(nil, Map{}) 100 assert.EqualValues(t, err, nil) 101 assert.EqualValues(t, len(handlers.handlers), 0) 102 }) 103 104 t.Run("Should register handler", func(t *testing.T) { 105 106 promRegistry := prometheus.NewRegistry() 107 opts, _ := ParseContextOptions(Options{"sourceContext": "pod", "destinationContext": "pod"}) 108 initHandlers(t, opts, promRegistry, log) 109 }) 110 111 t.Run("Should remove metrics series with ContextPod", func(t *testing.T) { 112 113 promRegistry := prometheus.NewRegistry() 114 opts, _ := ParseContextOptions(Options{"sourceContext": "pod", "destinationContext": "pod"}) 115 handlers := initHandlers(t, opts, promRegistry, log) 116 117 handlers.ProcessFlow(context.TODO(), flow1) 118 handlers.ProcessFlow(context.TODO(), flow2) 119 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ProcessCalled, 2) 120 121 verifyMetricSeriesExists(t, promRegistry, 2) 122 123 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 124 ObjectMeta: slim_metav1.ObjectMeta{ 125 Name: "foo-123", 126 Namespace: "foo", 127 }, 128 }) 129 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 1) 130 131 verifyMetricSeriesExists(t, promRegistry, 1) 132 133 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 134 ObjectMeta: slim_metav1.ObjectMeta{ 135 Name: "bar-123", 136 Namespace: "bar", 137 }, 138 }) 139 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 2) 140 141 verifyMetricSeriesNotExists(t, promRegistry) 142 }) 143 144 t.Run("Should not remove metrics series with ContextWorkloadName", func(t *testing.T) { 145 146 promRegistry := prometheus.NewRegistry() 147 opts, _ := ParseContextOptions(Options{"sourceContext": "workload-name", "destinationContext": "workload-name"}) 148 handlers := initHandlers(t, opts, promRegistry, log) 149 150 handlers.ProcessFlow(context.TODO(), flow1) 151 handlers.ProcessFlow(context.TODO(), flow2) 152 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ProcessCalled, 2) 153 154 verifyMetricSeriesExists(t, promRegistry, 1) 155 156 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 157 ObjectMeta: slim_metav1.ObjectMeta{ 158 Name: "foo-123", 159 Namespace: "foo", 160 }, 161 }) 162 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 1) 163 164 verifyMetricSeriesExists(t, promRegistry, 1) 165 166 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 167 ObjectMeta: slim_metav1.ObjectMeta{ 168 Name: "bar-123", 169 Namespace: "bar", 170 }, 171 }) 172 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 2) 173 174 verifyMetricSeriesExists(t, promRegistry, 1) 175 }) 176 177 t.Run("Should remove metrics series with LabelsContext", func(t *testing.T) { 178 179 promRegistry := prometheus.NewRegistry() 180 opts, _ := ParseContextOptions(Options{"labelsContext": "source_pod,source_namespace,destination_pod,destination_namespace"}) 181 handlers := initHandlers(t, opts, promRegistry, log) 182 183 handlers.ProcessFlow(context.TODO(), flow1) 184 handlers.ProcessFlow(context.TODO(), flow2) 185 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ProcessCalled, 2) 186 187 verifyMetricSeriesExists(t, promRegistry, 2) 188 189 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 190 ObjectMeta: slim_metav1.ObjectMeta{ 191 Name: "foo-123", 192 Namespace: "foo", 193 }, 194 }) 195 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 1) 196 197 verifyMetricSeriesExists(t, promRegistry, 1) 198 199 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 200 ObjectMeta: slim_metav1.ObjectMeta{ 201 Name: "bar-123", 202 Namespace: "bar", 203 }, 204 }) 205 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 2) 206 207 verifyMetricSeriesNotExists(t, promRegistry) 208 }) 209 210 t.Run("Should not remove metrics series with LabelsContext without namespace", func(t *testing.T) { 211 212 promRegistry := prometheus.NewRegistry() 213 opts, _ := ParseContextOptions(Options{"labelsContext": "source_pod,destination_pod"}) 214 handlers := initHandlers(t, opts, promRegistry, log) 215 216 handlers.ProcessFlow(context.TODO(), flow1) 217 handlers.ProcessFlow(context.TODO(), flow2) 218 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ProcessCalled, 2) 219 220 verifyMetricSeriesExists(t, promRegistry, 2) 221 222 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 223 ObjectMeta: slim_metav1.ObjectMeta{ 224 Name: "foo-123", 225 Namespace: "foo", 226 }, 227 }) 228 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 1) 229 230 verifyMetricSeriesExists(t, promRegistry, 2) 231 232 handlers.ProcessCiliumEndpointDeletion(&types.CiliumEndpoint{ 233 ObjectMeta: slim_metav1.ObjectMeta{ 234 Name: "bar-123", 235 Namespace: "bar", 236 }, 237 }) 238 assert.EqualValues(t, handlers.handlers[0].(*testHandler).ListMetricCalled, 2) 239 240 verifyMetricSeriesExists(t, promRegistry, 2) 241 }) 242 243 } 244 245 func initHandlers(t *testing.T, opts *ContextOptions, promRegistry *prometheus.Registry, log *logrus.Logger) *Handlers { 246 counter := prometheus.NewCounterVec(prometheus.CounterOpts{ 247 Namespace: "test", 248 Name: "events", 249 }, opts.GetLabelNames()) 250 promRegistry.MustRegister(counter) 251 252 r := NewRegistry(log) 253 254 handler := &testHandler{} 255 handler.ContextOptions = opts 256 handler.counter = counter 257 258 r.Register("test", &testPlugin{handler: handler}) 259 260 handlers, err := r.ConfigureHandlers(nil, Map{"test": Options{}}) 261 assert.EqualValues(t, err, nil) 262 assert.EqualValues(t, len(handlers.handlers), 1) 263 assert.EqualValues(t, handlers.handlers[0].(*testHandler).InitCalled, 1) 264 return handlers 265 } 266 267 func verifyMetricSeriesExists(t *testing.T, promRegistry *prometheus.Registry, expectedCount int) { 268 metricFamilies, err := promRegistry.Gather() 269 require.NoError(t, err) 270 require.Len(t, metricFamilies, 1) 271 assert.Equal(t, "test_events", *metricFamilies[0].Name) 272 require.Len(t, metricFamilies[0].Metric, expectedCount) 273 } 274 275 func verifyMetricSeriesNotExists(t *testing.T, promRegistry *prometheus.Registry) { 276 metricFamilies, err := promRegistry.Gather() 277 require.NoError(t, err) 278 require.Len(t, metricFamilies, 0) 279 }