chore(go/plugin/sd): dont validate config in userconfig (#21842)
Ilya Mashchenko committed
Feb 27, 2026 at 23:54 UTC
2fc42064086225764555a03df29672f5a21c094d
4 files changed
+41
-10
src/go/plugin/agent/discovery/sd/dyncfg.go
+3
-3
@@ -78,7 +78,7 @@ func (cb *sdCallbacks) ExtractKey(fn dyncfg.Function) (key, name string, ok bool
78
79
func (cb *sdCallbacks) ParseAndValidate(fn dyncfg.Function, name string) (sdConfig, error) {
80
dt, _, _ := cb.sd.extractDiscovererAndName(fn.ID())
81
- if _, err := parseDyncfgPayload(fn.Payload(), dt, cb.sd.configDefaults, cb.sd.discovererRegistry()); err != nil {
81
+ if _, err := parseDyncfgPayload(fn.Payload(), dt, cb.sd.configDefaults, cb.sd.discovererRegistry(), true); err != nil {
82
return nil, err
83
}
84
pkey := pipelineKey(dt, name)
@@ -246,7 +246,7 @@ func (d *ServiceDiscovery) dyncfgCmdTest(fn dyncfg.Function) {
246
}
247
248
// Parse and validate the config without storing it
249
- _, err := parseDyncfgPayload(fn.Payload(), dt, d.configDefaults, d.discovererRegistry())
249
+ _, err := parseDyncfgPayload(fn.Payload(), dt, d.configDefaults, d.discovererRegistry(), true)
250
if err != nil {
251
d.Warningf("dyncfg: test: failed to parse config for '%s': %v", dt, err)
252
d.dyncfgApi.SendCodef(fn, 400, "Failed to parse config: %v", err)
@@ -279,7 +279,7 @@ func (d *ServiceDiscovery) dyncfgCmdUserconfig(fn dyncfg.Function) {
279
return
280
}
281
282
- if _, err := parseDyncfgPayload(fn.Payload(), dt, d.configDefaults, d.discovererRegistry()); err != nil {
282
+ if _, err := parseDyncfgPayload(fn.Payload(), dt, d.configDefaults, d.discovererRegistry(), false); err != nil {
283
d.Warningf("dyncfg: userconfig: failed to parse config for '%s': %v", id, err)
284
d.dyncfgApi.SendCodef(fn, 400, "Failed to parse config: %v", err)
285
return
src/go/plugin/agent/discovery/sd/dyncfg_parse.go
+6
-4
@@ -15,7 +15,7 @@ import (
15
// parseDyncfgPayload parses a dyncfg JSON payload into a pipeline.Config.
16
// Since pipeline.Config now has proper JSON tags matching the schema,
17
// we can unmarshal directly without type-specific parsing.
18
-func parseDyncfgPayload(payload []byte, discovererType string, configDefaults confgroup.Registry, reg Registry) (pipeline.Config, error) {
18
+func parseDyncfgPayload(payload []byte, discovererType string, configDefaults confgroup.Registry, reg Registry, validate bool) (pipeline.Config, error) {
19
if reg == nil {
20
return pipeline.Config{}, fmt.Errorf("discoverer registry is not configured")
21
}
@@ -42,9 +42,11 @@ func parseDyncfgPayload(payload []byte, discovererType string, configDefaults co
42
return pipeline.Config{}, fmt.Errorf("invalid %q discoverer config: %w", discovererType, err)
43
}
44
45
- // Perform full semantic validation (name, discoverer, services rules)
46
- if err := pipeline.ValidateConfig(cfg); err != nil {
47
- return pipeline.Config{}, err
45
+ if validate {
46
+ // Perform full semantic validation (name, discoverer, services rules)
47
+ if err := pipeline.ValidateConfig(cfg); err != nil {
48
+ return pipeline.Config{}, err
49
+ }
50
}
51
52
return cfg, nil
src/go/plugin/framework/vnodes/vnodes.go
+6
-2
@@ -128,9 +128,13 @@ func readConfDir(dir string) map[string]*VirtualNode {
128
129
v := v
130
131
- if v.Name == "" {
132
- v.Name = v.Hostname
131
+ if v.Name != "" && v.Name != v.Hostname {
132
+ log.Warningf(
133
+ "ignoring virtual node name '%s' for hostname '%s'; file-based vnode identity uses hostname",
134
+ v.Name, v.Hostname,
135
+ )
136
}
137
+ v.Name = v.Hostname
138
v.Source = fmt.Sprintf("file=%s", path)
139
if isStockConfig(path) {
140
v.SourceType = "stock"
src/go/plugin/framework/vnodes/vnodes_test.go
+26
-1
@@ -3,13 +3,21 @@
3
package vnodes
4
5
import (
6
+ "os"
7
+ "path/filepath"
8
"testing"
9
10
"github.com/stretchr/testify/assert"
11
+ "github.com/stretchr/testify/require"
12
)
13
14
func TestLoad(t *testing.T) {
12
- assert.NotNil(t, Load("testdata"))
15
+ nodes := Load("testdata")
16
+ assert.NotNil(t, nodes)
17
+ require.Contains(t, nodes, "first")
18
+ require.Contains(t, nodes, "second")
19
+ assert.Equal(t, "first", nodes["first"].Name)
20
+ assert.Equal(t, "second", nodes["second"].Name)
21
assert.NotNil(t, Load("not_exist"))
22
}
23
@@ -17,3 +25,20 @@ func TestIsStockConfig(t *testing.T) {
25
assert.True(t, isStockConfig("/usr/lib/netdata/conf.d/vnodes/test.conf"))
26
assert.False(t, isStockConfig("/etc/netdata/vnodes/test.conf"))
27
}
28
+
29
+func TestLoad_IgnoresCustomNameInFileAndUsesHostnameIdentity(t *testing.T) {
30
+ dir := t.TempDir()
31
+ cfgPath := filepath.Join(dir, "vnodes.yaml")
32
+ cfg := `
33
+- hostname: host-a
34
+ name: custom-name
35
+ guid: 11111111-2222-3333-4444-555555555555
36
+`
37
+ require.NoError(t, os.WriteFile(cfgPath, []byte(cfg), 0o644))
38
+
39
+ nodes := Load(dir)
40
+ require.Len(t, nodes, 1)
41
+ require.Contains(t, nodes, "host-a")
42
+ assert.Equal(t, "host-a", nodes["host-a"].Name)
43
+ assert.Equal(t, "host-a", nodes["host-a"].Hostname)
44
+}