Skip to content

Commit ec1e783

Browse files
committed
Pull request #402: Fix missing custom metrics when using Docker label "glouton.allow_metric"
Merge in PRODUCT/glouton from bugfix/PRODUCT-2833-glouton-prometheus-exporter-no-longer-working to main * commit '51425ce804158e4c6f823663f54f0c2f20f0c865': Fix issue with update of metrics filter
2 parents 48273b5 + 51425ce commit ec1e783

File tree

3 files changed

+95
-4
lines changed

3 files changed

+95
-4
lines changed

agent/agent.go

+26-3
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ type agent struct {
145145
lastContainerEventTime time.Time
146146
watchdogRunAt []time.Time
147147
metricFilter *metricFilter
148+
promFilter *metricFilter
149+
mergeMetricFilter *metricFilter
148150
monitorManager *blackbox.RegisterManager
149151
rulesManager *rules.Manager
150152
reloadState ReloadState
@@ -648,7 +650,7 @@ func (a *agent) updateThresholds(ctx context.Context, thresholds map[string]thre
648650
if err != nil {
649651
logger.V(2).Printf("An error occurred while running discoveries for updateThresholds: %v", err)
650652
} else {
651-
err = a.metricFilter.RebuildDynamicLists(a.dynamicScrapper, services, a.threshold.GetThresholdMetricNames(), a.rulesManager.MetricNames())
653+
err = a.rebuildDynamicMetricAllowDenyList(services)
652654
if err != nil {
653655
logger.V(2).Printf("An error occurred while rebuilding dynamic list for updateThresholds: %v", err)
654656
}
@@ -671,6 +673,24 @@ func (a *agent) updateThresholds(ctx context.Context, thresholds map[string]thre
671673
}
672674
}
673675

676+
func (a *agent) rebuildDynamicMetricAllowDenyList(services []discovery.Service) error {
677+
var errs []error
678+
679+
errs = append(
680+
errs,
681+
a.metricFilter.RebuildDynamicLists(a.dynamicScrapper, services, a.threshold.GetThresholdMetricNames(), a.rulesManager.MetricNames()),
682+
)
683+
684+
errs = append(
685+
errs,
686+
a.promFilter.RebuildDynamicLists(a.dynamicScrapper, services, a.threshold.GetThresholdMetricNames(), a.rulesManager.MetricNames()),
687+
)
688+
689+
a.mergeMetricFilter.mergeInPlace(a.metricFilter, a.promFilter)
690+
691+
return errors.Join(errs...)
692+
}
693+
674694
// Run will start the agent. It will terminate when sigquit/sigterm/sigint is received.
675695
func (a *agent) run(ctx context.Context, sighupChan chan os.Signal) { //nolint:maintidx
676696
ctx, cancel := context.WithCancel(ctx)
@@ -800,6 +820,9 @@ func (a *agent) run(ctx context.Context, sighupChan chan os.Signal) { //nolint:m
800820
logger.Printf("An error occurred while building the Prometheus metric filter, allow/deny list may be partial: %v", err)
801821
}
802822

823+
a.promFilter = promFilter
824+
a.mergeMetricFilter = mergeMetricFilters(a.promFilter, bleemeoFilter)
825+
803826
secretInputsGate := gate.New(inputs.MaxParallelSecrets())
804827

805828
a.gathererRegistry, err = registry.New(
@@ -809,7 +832,7 @@ func (a *agent) run(ctx context.Context, sighupChan chan os.Signal) { //nolint:m
809832
FQDN: fqdn,
810833
GloutonPort: strconv.Itoa(a.config.Web.Listener.Port),
811834
BlackboxSendScraperID: a.config.Blackbox.ScraperSendUUID,
812-
Filter: mergeMetricFilters(promFilter, bleemeoFilter),
835+
Filter: a.mergeMetricFilter,
813836
Queryable: a.store,
814837
SecretInputsGate: secretInputsGate,
815838
ShutdownDeadline: 15 * time.Second,
@@ -1943,7 +1966,7 @@ func (a *agent) handleTrigger(ctx context.Context) {
19431966
}
19441967
}
19451968

1946-
err := a.metricFilter.RebuildDynamicLists(a.dynamicScrapper, services, a.threshold.GetThresholdMetricNames(), a.rulesManager.MetricNames())
1969+
err := a.rebuildDynamicMetricAllowDenyList(services)
19471970
if err != nil {
19481971
logger.V(2).Printf("Error during dynamic Filter rebuild: %v", err)
19491972
}

agent/metric.go

+13-1
Original file line numberDiff line numberDiff line change
@@ -974,6 +974,18 @@ func newMetricFilter(metricCfg config.Metric, hasSNMP, hasSwap, forBleemeo bool)
974974
// that represents the union of both the given filters.
975975
// This only works if the deny-list uses equal comparison (no regexp), or is identical.
976976
func mergeMetricFilters(f1, f2 *metricFilter) *metricFilter {
977+
result := &metricFilter{}
978+
result.mergeInPlace(f1, f2)
979+
980+
return result
981+
}
982+
983+
// mergeInPlace merge the two input filter (f1 & f2) and update the filter m.
984+
// All previous content of m is overwrite, the result only depend on f1 & f2.
985+
// The merge result is the union of both result the given filters (metrics that are allowed by
986+
// one of the filter).
987+
// This only works if the deny-list uses equal comparison (no regexp), or is identical.
988+
func (m *metricFilter) mergeInPlace(f1, f2 *metricFilter) {
977989
// Merging logic:
978990
// - to be allowed, a metric only needs to be present in the allowlist of one of the filters
979991
// - to be denied, a metric needs to be present in the deny-list of both the filters
@@ -997,7 +1009,7 @@ func mergeMetricFilters(f1, f2 *metricFilter) *metricFilter {
9971009
}
9981010
}
9991011

1000-
return &metricFilter{
1012+
*m = metricFilter{
10011013
includeDefaultMetrics: f1.includeDefaultMetrics || f2.includeDefaultMetrics,
10021014
staticAllowList: slices.Concat(f1.staticAllowList, f2.staticAllowList),
10031015
staticDenyList: staticDenyList,

agent/metric_test.go

+56
Original file line numberDiff line numberDiff line change
@@ -1562,6 +1562,62 @@ func Test_MergeMetricFilters(t *testing.T) {
15621562
}
15631563
}
15641564

1565+
// Test_MergeMetricFiltersMutation ensure that it's possible to modify in place
1566+
// the merge filter. This is required because filter get updated by RebuildDynamicLists.
1567+
func Test_MergeMetricFiltersMutation(t *testing.T) {
1568+
t.Parallel()
1569+
1570+
mustMatchers := func(s string) matcher.Matchers {
1571+
matchers, err := matcher.NormalizeMetric(s)
1572+
if err != nil {
1573+
t.Fatalf("Can't normalize metric %q: %v", s, err)
1574+
}
1575+
1576+
return matchers
1577+
}
1578+
1579+
metricCfg := config.Metric{
1580+
IncludeDefaultMetrics: false,
1581+
AllowMetrics: []string{"m1", "m2"},
1582+
DenyMetrics: []string{"no1"},
1583+
}
1584+
mf1, _ := newMetricFilter(metricCfg, false, false, false)
1585+
1586+
metricCfg.AllowMetrics = []string{"m3", "m4"}
1587+
metricCfg.DenyMetrics = []string{"no1", "no2"}
1588+
mf2, _ := newMetricFilter(metricCfg, false, false, false)
1589+
1590+
mergedFilter := mergeMetricFilters(mf1, mf2)
1591+
shallowCopy := mergedFilter
1592+
1593+
metricCfg2 := config.Metric{
1594+
IncludeDefaultMetrics: false,
1595+
AllowMetrics: []string{"m1", "m2"},
1596+
DenyMetrics: []string{"m4"},
1597+
}
1598+
mf1b, _ := newMetricFilter(metricCfg2, false, false, false)
1599+
1600+
mergedFilter.mergeInPlace(mf1b, mf2)
1601+
1602+
expectedFilter := &metricFilter{
1603+
includeDefaultMetrics: false,
1604+
staticAllowList: []matcher.Matchers{mustMatchers("m1"), mustMatchers("m2"), mustMatchers("m3"), mustMatchers("m4")},
1605+
staticDenyList: nil,
1606+
allowList: map[labels.Matcher][]matcher.Matchers{
1607+
*labels.MustNewMatcher(labels.MatchEqual, types.LabelName, "m1"): {mustMatchers("m1")},
1608+
*labels.MustNewMatcher(labels.MatchEqual, types.LabelName, "m2"): {mustMatchers("m2")},
1609+
*labels.MustNewMatcher(labels.MatchEqual, types.LabelName, "m3"): {mustMatchers("m3")},
1610+
*labels.MustNewMatcher(labels.MatchEqual, types.LabelName, "m4"): {mustMatchers("m4")},
1611+
},
1612+
denyList: map[labels.Matcher][]matcher.Matchers{},
1613+
}
1614+
1615+
cmpOpts := cmp.Options{cmp.AllowUnexported(metricFilter{}), cmpopts.IgnoreTypes(sync.Mutex{}), cmpopts.IgnoreFields(labels.Matcher{}, "re")}
1616+
if diff := cmp.Diff(expectedFilter, shallowCopy, cmpOpts); diff != "" {
1617+
t.Fatalf("Unexpected filter merge result: (-want +got):\n%s", diff)
1618+
}
1619+
}
1620+
15651621
func Benchmark_MultipleFilters(b *testing.B) {
15661622
rawData, err := os.ReadFile("../prometheus/scrapper/testdata/large.txt")
15671623
if err != nil {

0 commit comments

Comments
 (0)