@cryptotaxi247 / netdata-1 / commits / 2fc420640

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 +}