From 71c4e69a083d50d39d78f4695e4ed80aeae04c7e Mon Sep 17 00:00:00 2001 From: Arve Knudsen Date: Wed, 24 Dec 2025 13:16:18 +0100 Subject: [PATCH] fix(config): check all fields in GlobalConfig.isZero() The isZero() method was missing checks for 9 fields that exist in the GlobalConfig struct. This caused the method to incorrectly return true when only these fields had non-zero values, resulting in user configurations being silently overwritten with defaults during YAML unmarshaling. Added checks for: BodySizeLimit, SampleLimit, TargetLimit, LabelLimit, LabelNameLengthLimit, LabelValueLengthLimit, KeepDroppedTargets, MetricNameValidationScheme, and MetricNameEscapingScheme. Consolidated TestEmptyGlobalBlock and new isZero tests under TestGlobalConfig. Signed-off-by: Arve Knudsen --- config/config.go | 11 +++++- config/config_test.go | 87 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 91 insertions(+), 7 deletions(-) diff --git a/config/config.go b/config/config.go index 113942b61a..51a8cefe3b 100644 --- a/config/config.go +++ b/config/config.go @@ -687,7 +687,16 @@ func (c *GlobalConfig) isZero() bool { c.ScrapeProtocols == nil && c.ScrapeNativeHistograms == nil && !c.ConvertClassicHistogramsToNHCB && - !c.AlwaysScrapeClassicHistograms + !c.AlwaysScrapeClassicHistograms && + c.BodySizeLimit == 0 && + c.SampleLimit == 0 && + c.TargetLimit == 0 && + c.LabelLimit == 0 && + c.LabelNameLengthLimit == 0 && + c.LabelValueLengthLimit == 0 && + c.KeepDroppedTargets == 0 && + c.MetricNameValidationScheme == model.UnsetValidation && + c.MetricNameEscapingScheme == "" } const DefaultGoGCPercentage = 75 diff --git a/config/config_test.go b/config/config_test.go index 28c8f2196d..1804f4925e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -2663,12 +2663,87 @@ func TestAgentMode(t *testing.T) { ) } -func TestEmptyGlobalBlock(t *testing.T) { - c, err := Load("global:\n", promslog.NewNopLogger()) - require.NoError(t, err) - exp := DefaultConfig - exp.loaded = true - require.Equal(t, exp, *c) +func TestGlobalConfig(t *testing.T) { + t.Run("empty block restores defaults", func(t *testing.T) { + c, err := Load("global:\n", promslog.NewNopLogger()) + require.NoError(t, err) + exp := DefaultConfig + exp.loaded = true + require.Equal(t, exp, *c) + }) + + // Verify that isZero() correctly identifies non-zero configurations for all + // fields in GlobalConfig. This is important because isZero() is used during + // YAML unmarshaling to detect empty global blocks that should be replaced + // with defaults. + t.Run("isZero", func(t *testing.T) { + for _, tc := range []struct { + name string + config GlobalConfig + expectZero bool + }{ + { + name: "empty GlobalConfig", + config: GlobalConfig{}, + expectZero: true, + }, + { + name: "ScrapeInterval set", + config: GlobalConfig{ScrapeInterval: model.Duration(30 * time.Second)}, + expectZero: false, + }, + { + name: "BodySizeLimit set", + config: GlobalConfig{BodySizeLimit: 1 * units.MiB}, + expectZero: false, + }, + { + name: "SampleLimit set", + config: GlobalConfig{SampleLimit: 1000}, + expectZero: false, + }, + { + name: "TargetLimit set", + config: GlobalConfig{TargetLimit: 500}, + expectZero: false, + }, + { + name: "LabelLimit set", + config: GlobalConfig{LabelLimit: 100}, + expectZero: false, + }, + { + name: "LabelNameLengthLimit set", + config: GlobalConfig{LabelNameLengthLimit: 50}, + expectZero: false, + }, + { + name: "LabelValueLengthLimit set", + config: GlobalConfig{LabelValueLengthLimit: 200}, + expectZero: false, + }, + { + name: "KeepDroppedTargets set", + config: GlobalConfig{KeepDroppedTargets: 10}, + expectZero: false, + }, + { + name: "MetricNameValidationScheme set", + config: GlobalConfig{MetricNameValidationScheme: model.LegacyValidation}, + expectZero: false, + }, + { + name: "MetricNameEscapingScheme set", + config: GlobalConfig{MetricNameEscapingScheme: model.EscapeUnderscores}, + expectZero: false, + }, + } { + t.Run(tc.name, func(t *testing.T) { + result := tc.config.isZero() + require.Equal(t, tc.expectZero, result) + }) + } + }) } // ScrapeConfigOptions contains options for creating a scrape config.