diff --git a/console/data/docs/99-changelog.md b/console/data/docs/99-changelog.md index fb065790..e62cd74b 100644 --- a/console/data/docs/99-changelog.md +++ b/console/data/docs/99-changelog.md @@ -17,6 +17,7 @@ identified with a specific icon: false by default) - 🩹 *outlet*: provide additional gracetime for a worker to send to ClickHouse - 🩹 *outlet*: enhance scaling up and down workers to avoid hysteresis +- 🩹 *outlet*: accept flows where interface names or descriptions are missing - 🩹 *docker*: update Traefik to 3.6.1 (for compatibility with Docker Engine 29) - 🌱 *common*: enable block and mutex profiling - 🌱 *config*: rename `verify` to `skip-verify` in TLS configurations for diff --git a/outlet/core/enricher.go b/outlet/core/enricher.go index e7676861..5bc3a8f0 100644 --- a/outlet/core/enricher.go +++ b/outlet/core/enricher.go @@ -38,10 +38,7 @@ func (w *worker) enrichFlow(exporterIP netip.Addr, exporterStr string) bool { if flow.InIf != 0 { answer := c.d.Metadata.Lookup(t, exporterIP, uint(flow.InIf)) - if !answer.Found { - c.metrics.flowsErrors.WithLabelValues(exporterStr, "metadata cache miss").Inc() - skip = true - } else { + if answer.Found { flowExporterName = answer.Exporter.Name expClassification.Region = answer.Exporter.Region expClassification.Role = answer.Exporter.Role @@ -61,14 +58,7 @@ func (w *worker) enrichFlow(exporterIP netip.Addr, exporterStr string) bool { if flow.OutIf != 0 { answer := c.d.Metadata.Lookup(t, exporterIP, uint(flow.OutIf)) - if !answer.Found { - // Only register a cache miss if we don't have one. - // TODO: maybe we could do one SNMP query for both interfaces. - if !skip { - c.metrics.flowsErrors.WithLabelValues(exporterStr, "metadata cache miss").Inc() - skip = true - } - } else { + if answer.Found { flowExporterName = answer.Exporter.Name expClassification.Region = answer.Exporter.Region expClassification.Role = answer.Exporter.Role @@ -90,6 +80,9 @@ func (w *worker) enrichFlow(exporterIP netip.Addr, exporterStr string) bool { if flow.OutIf == 0 && flow.InIf == 0 { c.metrics.flowsErrors.WithLabelValues(exporterStr, "input and output interfaces missing").Inc() skip = true + } else if flowExporterName == "" { + c.metrics.flowsErrors.WithLabelValues(exporterStr, "metadata cache miss").Inc() + skip = true } if samplingRate, ok := c.config.OverrideSamplingRate.Lookup(exporterIP); ok && samplingRate > 0 { diff --git a/outlet/core/enricher_test.go b/outlet/core/enricher_test.go index 766b726e..bd4bfee8 100644 --- a/outlet/core/enricher_test.go +++ b/outlet/core/enricher_test.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/gob" "fmt" + "maps" "net/netip" "sync" "testing" @@ -31,10 +32,11 @@ import ( func TestEnrich(t *testing.T) { cases := []struct { - Name string - Configuration gin.H - InputFlow func() *schema.FlowMessage - OutputFlow *schema.FlowMessage + Name string + Configuration gin.H + InputFlow func() *schema.FlowMessage + OutputFlow *schema.FlowMessage + ExpectedMetrics map[string]string }{ { Name: "no rule", @@ -583,6 +585,38 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`, }, }, }, + { + Name: "flow with missing interfaces", + Configuration: gin.H{}, + InputFlow: func() *schema.FlowMessage { + return &schema.FlowMessage{ + SamplingRate: 1000, + ExporterAddress: netip.MustParseAddr("::ffff:192.0.2.142"), + InIf: 0, + OutIf: 0, + } + }, + OutputFlow: nil, + ExpectedMetrics: map[string]string{ + `flows_errors_total{error="input and output interfaces missing",exporter="192.0.2.142"}`: "1", + }, + }, + { + Name: "flow with metadata cache miss", + Configuration: gin.H{}, + InputFlow: func() *schema.FlowMessage { + return &schema.FlowMessage{ + SamplingRate: 1000, + ExporterAddress: netip.MustParseAddr("::ffff:192.0.2.142"), + InIf: 999, + OutIf: 0, + } + }, + OutputFlow: nil, + ExpectedMetrics: map[string]string{ + `flows_errors_total{error="metadata cache miss",exporter="192.0.2.142"}`: "1", + }, + }, } for _, tc := range cases { t.Run(tc.Name, func(t *testing.T) { @@ -684,6 +718,7 @@ ClassifyProviderRegex(Interface.Description, "^Transit: ([^ ]+)", "$1")`, if tc.OutputFlow != nil { expectedMetrics[`forwarded_flows_total{exporter="192.0.2.142"}`] = "1" } + maps.Copy(expectedMetrics, tc.ExpectedMetrics) if diff := helpers.Diff(gotMetrics, expectedMetrics); diff != "" { t.Fatalf("Metrics (-got, +want):\n%s", diff) }