master
go 147 lines 3.85 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package jobmgr
4
5 import (
6 "context"
7 "fmt"
8
9 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
10 "github.com/netdata/netdata/go/plugins/plugin/framework/dyncfg"
11 )
12
13 // scheduleRetryTask schedules a retry if the job supports auto-detection retry.
14 func (m *Manager) scheduleRetryTask(cfg confgroup.Config, job runtimeJob) {
15 if !job.RetryAutoDetection() {
16 return
17 }
18 m.Infof("%s[%s] job detection failed, will retry in %d seconds",
19 cfg.Module(), cfg.Name(), job.AutoDetectionEvery())
20
21 ctx, cancel := context.WithCancel(m.ctx)
22 m.retryingTasks.add(cfg, &retryTask{cancel: cancel})
23
24 go runRetryTask(ctx, m.addCh, cfg)
25 }
26
27 // --- collectorCallbacks implements dyncfg.Callbacks[confgroup.Config] ---
28
29 type collectorCallbacks struct {
30 mgr *Manager
31 }
32
33 func (cb *collectorCallbacks) ExtractKey(fn dyncfg.Function) (key, name string, ok bool) {
34 var mn, jn string
35
36 if fn.Command() == dyncfg.CommandAdd {
37 // For add: ID is module template, job name is in Args[2].
38 mn, ok = cb.mgr.extractModuleName(fn.ID())
39 if !ok {
40 return "", "", false
41 }
42 jn = fn.JobName()
43 if jn == "" {
44 return "", "", false
45 }
46 } else {
47 // For other commands: ID contains module:job.
48 mn, jn, ok = cb.mgr.extractModuleJobName(fn.ID())
49 if !ok {
50 return "", "", false
51 }
52 }
53
54 key = mn + "_" + jn
55 if mn == jn {
56 key = jn
57 }
58 return key, jn, true
59 }
60
61 func (cb *collectorCallbacks) ValidateJobName(name string) error {
62 return dyncfg.JobNameRuleStrict(name)
63 }
64
65 func (cb *collectorCallbacks) ParseAndValidate(fn dyncfg.Function, name string) (confgroup.Config, error) {
66 mn, ok := cb.mgr.extractModuleName(fn.ID())
67 if !ok {
68 return nil, fmt.Errorf("could not extract module name from ID: %s", fn.ID())
69 }
70
71 cfg, err := configFromPayload(fn)
72 if err != nil {
73 return nil, fmt.Errorf("invalid configuration format: failed to create configuration from payload: %v", err)
74 }
75
76 cb.mgr.dyncfgSetConfigMeta(cfg, mn, name, fn)
77
78 if err := cb.mgr.validateCollectorJob(cfg); err != nil {
79 return nil, fmt.Errorf("invalid configuration: failed to apply configuration: %v", err)
80 }
81
82 return cfg, nil
83 }
84
85 func (cb *collectorCallbacks) Start(cfg confgroup.Config) error {
86 cb.mgr.retryingTasks.remove(cfg)
87
88 job, err := cb.mgr.createCollectorJob(cfg)
89 if err != nil {
90 return &codedError{err: fmt.Errorf("invalid configuration: failed to apply configuration: %v", err), code: 400}
91 }
92
93 if err := job.AutoDetection(); err != nil {
94 job.Cleanup()
95 cb.mgr.scheduleRetryTask(cfg, job)
96 return fmt.Errorf("job enable failed: %v", err)
97 }
98
99 cb.mgr.startRunningJob(job)
100 return nil
101 }
102
103 func (cb *collectorCallbacks) Update(oldCfg, newCfg confgroup.Config) error {
104 cb.mgr.retryingTasks.remove(oldCfg)
105 cb.mgr.stopRunningJob(oldCfg.FullName())
106 cb.mgr.fileStatus.remove(oldCfg)
107
108 job, err := cb.mgr.createCollectorJob(newCfg)
109 if err != nil {
110 return fmt.Errorf("job update failed: %v", err)
111 }
112
113 if err := job.AutoDetection(); err != nil {
114 job.Cleanup()
115 cb.mgr.scheduleRetryTask(newCfg, job)
116 return fmt.Errorf("job update failed: %v", err)
117 }
118
119 cb.mgr.startRunningJob(job)
120 return nil
121 }
122
123 func (cb *collectorCallbacks) Stop(cfg confgroup.Config) {
124 cb.mgr.retryingTasks.remove(cfg)
125 cb.mgr.stopRunningJob(cfg.FullName())
126 cb.mgr.fileStatus.remove(cfg)
127 }
128
129 func (cb *collectorCallbacks) OnStatusChange(entry *dyncfg.Entry[confgroup.Config], _ dyncfg.Status, _ dyncfg.Function) {
130 if entry.Status == dyncfg.StatusRunning && isDyncfg(entry.Cfg) {
131 cb.mgr.fileStatus.add(entry.Cfg, entry.Status.String())
132 }
133 }
134
135 func (cb *collectorCallbacks) ConfigID(cfg confgroup.Config) string {
136 return cb.mgr.dyncfgJobID(cfg)
137 }
138
139 // codedError wraps an error with an HTTP status code for the handler.
140 type codedError struct {
141 err error
142 code int
143 }
144
145 func (e *codedError) Error() string { return e.err.Error() }
146 func (e *codedError) Unwrap() error { return e.err }
147 func (e *codedError) Code() int { return e.code }