@cryptotaxi247 / netdata-1 / commits / 5c57cff6d

fix(plugin/agent): log applyConfig failures in job manager (#22124)

Ilya Mashchenko committed Apr 3, 2026 at 10:55 UTC 5c57cff6dd904db4defb8766f0e94d2c5ea64b35
3 files changed +65 -15
src/go/plugin/agent/jobmgr/dyncfg_collector_cmds.go
+2 -7
@@ -9,7 +9,6 @@ import (
9 "time"
10
11 "github.com/netdata/netdata/go/plugins/logger"
12 - "github.com/netdata/netdata/go/plugins/plugin/agent/secrets/secretstore"
12 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
13 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
14 "github.com/netdata/netdata/go/plugins/plugin/framework/dyncfg"
@@ -185,14 +184,10 @@ func (m *Manager) runDyncfgCmdTest(task dyncfgCmdTestTask) {
184 defer cancel()
185
186 secretStoreSvc := m.secretsCtl.Service()
188 -
189 - storeSnapshot := (*secretstore.Snapshot)(nil)
190 - if secretStoreSvc != nil {
191 - storeSnapshot = secretStoreSvc.Capture()
192 - }
187 + storeSnapshot := secretStoreSvc.Capture()
188 resolveCtx := collectorSecretResolveContext(ctx, m.Logger, task.cfg)
189 if err := applyConfig(resolveCtx, task.cfg, job, m.secretResolver, secretStoreSvc, storeSnapshot); err != nil {
195 - m.Warningf("dyncfg: test: module %s: failed to apply config: %v", task.moduleName, err)
190 + m.Warningf("dyncfg: test: module %s job %s: failed to apply config: %v", task.moduleName, task.cfg.Name(), err)
191 m.dyncfgResponder.SendCodef(task.fn, 400, "Invalid configuration. Failed to apply configuration: %v.", err)
192 return
193 }
src/go/plugin/agent/jobmgr/dyncfg_collector_test.go
+52
@@ -12,6 +12,7 @@ import (
12 "testing"
13 "time"
14
15 + "github.com/netdata/netdata/go/plugins/logger"
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18
@@ -364,6 +365,57 @@ func TestCollectorCallbacks_ParseAndValidate_SuppressesAuditSideEffects(t *testi
365 assert.Empty(t, analyzer.registered)
366 }
367
368 +func TestCollectorCallbacks_ApplyConfigLoggingHonorsValidationMode(t *testing.T) {
369 + tests := map[string]struct {
370 + run func(t *testing.T, mgr *Manager, logBuf *bytes.Buffer)
371 + wantLogMessage bool
372 + }{
373 + "validation suppresses expected applyConfig error logs": {
374 + run: func(t *testing.T, mgr *Manager, _ *bytes.Buffer) {
375 + cb := &collectorCallbacks{mgr: mgr}
376 + cfg := prepareDyncfgCfg("success", "payload-name").Set("option_str", "one").Set("option_int", "bad")
377 + fn := dyncfg.NewFunction(functions.Function{
378 + UID: "validation-no-log",
379 + ContentType: "application/json",
380 + Payload: mustMarshalCollectorConfigPayload(t, cfg),
381 + Args: collectorTestArgs(mgr, "success", string(dyncfg.CommandAdd), "validated"),
382 + })
383 +
384 + _, err := cb.ParseAndValidate(fn, "validated")
385 + require.Error(t, err)
386 + assert.Contains(t, err.Error(), "failed to apply configuration")
387 + },
388 + },
389 + "runtime creation still logs applyConfig errors": {
390 + run: func(t *testing.T, mgr *Manager, _ *bytes.Buffer) {
391 + cfg := prepareDyncfgCfg("success", "runtime-job").Set("option_str", "one").Set("option_int", "bad")
392 +
393 + _, err := mgr.createCollectorJob(cfg)
394 + require.Error(t, err)
395 + assert.Contains(t, err.Error(), "cannot unmarshal")
396 + },
397 + wantLogMessage: true,
398 + },
399 + }
400 +
401 + for name, tc := range tests {
402 + t.Run(name, func(t *testing.T) {
403 + var logBuf bytes.Buffer
404 + mgr := newCollectorTestManager()
405 + mgr.Logger = logger.NewWithWriter(&logBuf)
406 +
407 + tc.run(t, mgr, &logBuf)
408 +
409 + const msg = "failed to apply config for"
410 + if tc.wantLogMessage {
411 + assert.Contains(t, logBuf.String(), msg)
412 + } else {
413 + assert.NotContains(t, logBuf.String(), msg)
414 + }
415 + })
416 + }
417 +}
418 +
419 func TestCollectorCallbacks_Start(t *testing.T) {
420 tests := map[string]struct {
421 cfg confgroup.Config
src/go/plugin/agent/jobmgr/job_factory.go
+11 -8
@@ -101,17 +101,22 @@ func (f *jobFactory) create(cfg confgroup.Config) (runtimeJob, error) {
101 return f.createV1(cfg, creator, functionOnly, vnode)
102 }
103
104 +func (f *jobFactory) logApplyConfigError(cfg confgroup.Config, err error) {
105 + if f.validationOnly {
106 + return
107 + }
108 + f.logger.Errorf("failed to apply config for %s[%s] job: %v", cfg.Module(), cfg.Name(), err)
109 +}
110 +
111 func (f *jobFactory) createV2(cfg confgroup.Config, creator collectorapi.Creator, functionOnly bool, vnode *vnodes.VirtualNode) (runtimeJob, error) {
112 mod := creator.CreateV2()
113 if mod == nil {
114 return nil, fmt.Errorf("module %s CreateV2 returned nil", cfg.Module())
115 }
109 - storeSnapshot := (*secretstore.Snapshot)(nil)
110 - if f.secretStoreSvc != nil {
111 - storeSnapshot = f.secretStoreSvc.Capture()
112 - }
116 + storeSnapshot := f.secretStoreSvc.Capture()
117 resolveCtx := collectorSecretResolveContext(f.ctx, f.logger, cfg)
118 if err := applyConfig(resolveCtx, cfg, mod, f.secretResolver, f.secretStoreSvc, storeSnapshot); err != nil {
119 + f.logApplyConfigError(cfg, err)
120 return nil, err
121 }
122
@@ -146,12 +151,10 @@ func (f *jobFactory) createV1(cfg confgroup.Config, creator collectorapi.Creator
151 }
152
153 mod := creator.Create()
149 - storeSnapshot := (*secretstore.Snapshot)(nil)
150 - if f.secretStoreSvc != nil {
151 - storeSnapshot = f.secretStoreSvc.Capture()
152 - }
154 + storeSnapshot := f.secretStoreSvc.Capture()
155 resolveCtx := collectorSecretResolveContext(f.ctx, f.logger, cfg)
156 if err := applyConfig(resolveCtx, cfg, mod, f.secretResolver, f.secretStoreSvc, storeSnapshot); err != nil {
157 + f.logApplyConfigError(cfg, err)
158 return nil, err
159 }
160