diff options
Diffstat (limited to 'src/go/plugin/go.d/agent')
31 files changed, 183 insertions, 756 deletions
diff --git a/src/go/plugin/go.d/agent/agent.go b/src/go/plugin/go.d/agent/agent.go index 2423e84e0..014f544b5 100644 --- a/src/go/plugin/go.d/agent/agent.go +++ b/src/go/plugin/go.d/agent/agent.go @@ -13,6 +13,9 @@ import ( "time" "github.com/netdata/netdata/go/plugins/logger" + "github.com/netdata/netdata/go/plugins/pkg/multipath" + "github.com/netdata/netdata/go/plugins/pkg/netdataapi" + "github.com/netdata/netdata/go/plugins/pkg/safewriter" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/confgroup" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/discovery" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/filelock" @@ -20,10 +23,6 @@ import ( "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/functions" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/jobmgr" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/netdataapi" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/safewriter" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes" - "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/multipath" "github.com/mattn/go-isatty" ) @@ -187,9 +186,7 @@ func (a *Agent) run(ctx context.Context) { jobMgr.ConfigDefaults = discCfg.Registry jobMgr.FnReg = fnMgr - if reg := a.setupVnodeRegistry(); reg == nil || reg.Len() == 0 { - vnodes.Disabled = true - } else { + if reg := a.setupVnodeRegistry(); reg != nil && reg.Len() > 0 { jobMgr.Vnodes = reg } diff --git a/src/go/plugin/go.d/agent/agent_test.go b/src/go/plugin/go.d/agent/agent_test.go index 9096b9015..39e12f751 100644 --- a/src/go/plugin/go.d/agent/agent_test.go +++ b/src/go/plugin/go.d/agent/agent_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" + "github.com/netdata/netdata/go/plugins/pkg/safewriter" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/safewriter" "github.com/stretchr/testify/assert" ) diff --git a/src/go/plugin/go.d/agent/confgroup/config_test.go b/src/go/plugin/go.d/agent/confgroup/config_test.go index 98c6c3e78..d8e865b44 100644 --- a/src/go/plugin/go.d/agent/confgroup/config_test.go +++ b/src/go/plugin/go.d/agent/confgroup/config_test.go @@ -13,7 +13,7 @@ import ( func TestConfig_Name(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "string": {cfg: Config{"name": "name"}, expected: "name"}, "empty string": {cfg: Config{"name": ""}, expected: ""}, @@ -32,7 +32,7 @@ func TestConfig_Name(t *testing.T) { func TestConfig_Module(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "string": {cfg: Config{"module": "module"}, expected: "module"}, "empty string": {cfg: Config{"module": ""}, expected: ""}, @@ -51,7 +51,7 @@ func TestConfig_Module(t *testing.T) { func TestConfig_FullName(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "name == module": {cfg: Config{"name": "name", "module": "name"}, expected: "name"}, "name != module": {cfg: Config{"name": "name", "module": "module"}, expected: "module_name"}, @@ -68,7 +68,7 @@ func TestConfig_FullName(t *testing.T) { func TestConfig_UpdateEvery(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "int": {cfg: Config{"update_every": 1}, expected: 1}, "not int": {cfg: Config{"update_every": "1"}, expected: 0}, @@ -86,7 +86,7 @@ func TestConfig_UpdateEvery(t *testing.T) { func TestConfig_AutoDetectionRetry(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "int": {cfg: Config{"autodetection_retry": 1}, expected: 1}, "not int": {cfg: Config{"autodetection_retry": "1"}, expected: 0}, @@ -104,7 +104,7 @@ func TestConfig_AutoDetectionRetry(t *testing.T) { func TestConfig_Priority(t *testing.T) { tests := map[string]struct { cfg Config - expected interface{} + expected any }{ "int": {cfg: Config{"priority": 1}, expected: 1}, "not int": {cfg: Config{"priority": "1"}, expected: 0}, diff --git a/src/go/plugin/go.d/agent/config.go b/src/go/plugin/go.d/agent/config.go index fef68c7e0..e0c3f4605 100644 --- a/src/go/plugin/go.d/agent/config.go +++ b/src/go/plugin/go.d/agent/config.go @@ -47,13 +47,13 @@ func (c *config) isEnabled(moduleName string, explicit bool) bool { return c.DefaultRun } -func (c *config) UnmarshalYAML(unmarshal func(interface{}) error) error { +func (c *config) UnmarshalYAML(unmarshal func(any) error) error { type plain config if err := unmarshal((*plain)(c)); err != nil { return err } - var m map[string]interface{} + var m map[string]any if err := unmarshal(&m); err != nil { return err } diff --git a/src/go/plugin/go.d/agent/discovery/file/parse.go b/src/go/plugin/go.d/agent/discovery/file/parse.go index 5fd31f32a..ae48fcb8f 100644 --- a/src/go/plugin/go.d/agent/discovery/file/parse.go +++ b/src/go/plugin/go.d/agent/discovery/file/parse.go @@ -97,7 +97,7 @@ func parseSDFormat(reg confgroup.Registry, path string, bs []byte) (*confgroup.G } func cfgFormat(bs []byte) format { - var data interface{} + var data any if err := yaml.Unmarshal(bs, &data); err != nil { return unknownFormat } diff --git a/src/go/plugin/go.d/agent/discovery/file/sim_test.go b/src/go/plugin/go.d/agent/discovery/file/sim_test.go index 3219c6892..a0a8c4425 100644 --- a/src/go/plugin/go.d/agent/discovery/file/sim_test.go +++ b/src/go/plugin/go.d/agent/discovery/file/sim_test.go @@ -110,7 +110,7 @@ func (d *tmpDir) renameFile(origFilename, newFilename string) { require.NoError(d.t, err) } -func (d *tmpDir) writeYAML(filename string, in interface{}) { +func (d *tmpDir) writeYAML(filename string, in any) { bs, err := yaml.Marshal(in) require.NoError(d.t, err) err = os.WriteFile(filename, bs, 0644) diff --git a/src/go/plugin/go.d/agent/discovery/sd/conffile.go b/src/go/plugin/go.d/agent/discovery/sd/conffile.go index e08a4021b..60a7208d2 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/conffile.go +++ b/src/go/plugin/go.d/agent/discovery/sd/conffile.go @@ -7,7 +7,7 @@ import ( "os" "github.com/netdata/netdata/go/plugins/logger" - "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/multipath" + "github.com/netdata/netdata/go/plugins/pkg/multipath" ) type confFile struct { diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/dockerd/docker.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/dockerd/docker.go index 1cea014a9..e79d8b562 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/dockerd/docker.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/dockerd/docker.go @@ -13,8 +13,8 @@ import ( "github.com/netdata/netdata/go/plugins/logger" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/discovery/sd/model" + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/confopt" "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/dockerhost" - "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/web" "github.com/docker/docker/api/types" typesContainer "github.com/docker/docker/api/types/container" @@ -64,9 +64,9 @@ func NewDiscoverer(cfg Config) (*Discoverer, error) { type Config struct { Source string - Tags string `yaml:"tags"` - Address string `yaml:"address"` - Timeout web.Duration `yaml:"timeout"` + Tags string `yaml:"tags"` + Address string `yaml:"address"` + Timeout confopt.Duration `yaml:"timeout"` } type ( diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes.go index 439e2b695..26f8a1bd2 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes.go @@ -234,7 +234,7 @@ func (d *KubeDiscoverer) setupServiceDiscoverer(ctx context.Context, namespace s return td } -func enqueue(queue *workqueue.Type, obj any) { +func enqueue(queue *workqueue.Typed[any], obj any) { key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj) if err != nil { return diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes_test.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes_test.go index ba60a47b4..908a2a192 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes_test.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/kubernetes_test.go @@ -134,7 +134,7 @@ func TestKubeDiscoverer_Discover(t *testing.T) { } func prepareDiscoverer(role role, namespaces []string, objects ...runtime.Object) (*KubeDiscoverer, kubernetes.Interface) { - client := fake.NewSimpleClientset(objects...) + client := fake.NewClientset(objects...) tags, _ := model.ParseTags("k8s") disc := &KubeDiscoverer{ tags: tags, diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/pod.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/pod.go index 617081742..f5918ca35 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/pod.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/pod.go @@ -58,7 +58,7 @@ func newPodDiscoverer(pod, cmap, secret cache.SharedInformer) *podDiscoverer { panic("nil pod or cmap or secret informer") } - queue := workqueue.NewWithConfig(workqueue.QueueConfig{Name: "pod"}) + queue := workqueue.NewTypedWithConfig(workqueue.TypedQueueConfig[any]{Name: "pod"}) _, _ = pod.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj any) { enqueue(queue, obj) }, @@ -82,7 +82,7 @@ type podDiscoverer struct { podInformer cache.SharedInformer cmapInformer cache.SharedInformer secretInformer cache.SharedInformer - queue *workqueue.Type + queue *workqueue.Typed[any] } func (p *podDiscoverer) String() string { diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/service.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/service.go index 1d5ae7cd5..ebcfe31cc 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/service.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/kubernetes/service.go @@ -53,7 +53,7 @@ type serviceDiscoverer struct { model.Base informer cache.SharedInformer - queue *workqueue.Type + queue *workqueue.Typed[any] } func newServiceDiscoverer(inf cache.SharedInformer) *serviceDiscoverer { @@ -61,7 +61,8 @@ func newServiceDiscoverer(inf cache.SharedInformer) *serviceDiscoverer { panic("nil service informer") } - queue := workqueue.NewWithConfig(workqueue.QueueConfig{Name: "service"}) + queue := workqueue.NewTypedWithConfig(workqueue.TypedQueueConfig[any]{Name: "service"}) + _, _ = inf.AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj any) { enqueue(queue, obj) }, UpdateFunc: func(_, obj any) { enqueue(queue, obj) }, diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/ll.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/ll.go new file mode 100644 index 000000000..fdb70f5a3 --- /dev/null +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/ll.go @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-3.0-or-later + +package netlisteners + +import ( + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + "github.com/netdata/netdata/go/plugins/pkg/executable" +) + +type localListeners interface { + discover(ctx context.Context) ([]byte, error) +} + +func newLocalListeners(timeout time.Duration) localListeners { + dir := os.Getenv("NETDATA_PLUGINS_DIR") + if dir == "" { + dir = executable.Directory + } + if dir == "" { + dir, _ = os.Getwd() + } + + return &localListenersExec{ + binPath: filepath.Join(dir, "local-listeners"), + timeout: timeout, + } +} + +type localListenersExec struct { + binPath string + timeout time.Duration +} + +func (e *localListenersExec) discover(ctx context.Context) ([]byte, error) { + execCtx, cancel := context.WithTimeout(ctx, e.timeout) + defer cancel() + + // TCPv4/6 and UPDv4 sockets in LISTEN state + // https://github.com/netdata/netdata/blob/master/src/collectors/utils/local_listeners.c + args := []string{ + "no-udp6", + "no-local", + "no-inbound", + "no-outbound", + "no-namespaces", + } + + cmd := exec.CommandContext(execCtx, e.binPath, args...) + + bs, err := cmd.Output() + if err != nil { + return nil, fmt.Errorf("error on executing '%s': %v", cmd, err) + } + + return bs, nil +} diff --git a/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/netlisteners.go b/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/netlisteners.go index 60dd92cb4..b38d0506f 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/netlisteners.go +++ b/src/go/plugin/go.d/agent/discovery/sd/discoverer/netlisteners/netlisteners.go @@ -10,8 +10,6 @@ import ( "fmt" "log/slog" "net" - "os" - "os/exec" "path/filepath" "sort" "strconv" @@ -19,8 +17,8 @@ import ( "time" "github.com/netdata/netdata/go/plugins/logger" - "github.com/netdata/netdata/go/plugins/pkg/executable" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/discovery/sd/model" + "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/confopt" "github.com/ilyam8/hashstructure" ) @@ -30,18 +28,27 @@ var ( fullName = fmt.Sprintf("sd:%s", shortName) ) +type Config struct { + Source string `yaml:"-"` + Tags string `yaml:"tags"` + + Interval *confopt.Duration `yaml:"interval"` + Timeout confopt.Duration `yaml:"timeout"` +} + func NewDiscoverer(cfg Config) (*Discoverer, error) { tags, err := model.ParseTags(cfg.Tags) if err != nil { return nil, fmt.Errorf("parse tags: %v", err) } - dir := os.Getenv("NETDATA_PLUGINS_DIR") - if dir == "" { - dir = executable.Directory + interval := time.Minute * 2 + if cfg.Interval != nil { + interval = cfg.Interval.Duration() } - if dir == "" { - dir, _ = os.Getwd() + timeout := time.Second * 5 + if cfg.Timeout.Duration() != 0 { + timeout = cfg.Timeout.Duration() } d := &Discoverer{ @@ -49,12 +56,10 @@ func NewDiscoverer(cfg Config) (*Discoverer, error) { slog.String("component", "service discovery"), slog.String("discoverer", shortName), ), - cfgSource: cfg.Source, - ll: &localListenersExec{ - binPath: filepath.Join(dir, "local-listeners"), - timeout: time.Second * 5, - }, - interval: time.Minute * 2, + cfgSource: cfg.Source, + ll: newLocalListeners(timeout), + interval: interval, + timeout: timeout, expiryTime: time.Minute * 10, cache: make(map[uint64]*cacheItem), started: make(chan struct{}), @@ -65,11 +70,6 @@ func NewDiscoverer(cfg Config) (*Discoverer, error) { return d, nil } -type Config struct { - Source string `yaml:"-"` - Tags string `yaml:"tags"` -} - type ( Discoverer struct { *logger.Logger @@ -78,6 +78,7 @@ type ( cfgSource string interval time.Duration + timeout time.Duration ll localListeners expiryTime time.Duration @@ -92,9 +93,6 @@ type ( lastSeenTime time.Time tgt model.Target } - localListeners interface { - discover(ctx context.Context) ([]byte, error) - } ) func (d *Discoverer) String() string { @@ -103,6 +101,7 @@ func (d *Discoverer) String() string { func (d *Discoverer) Discover(ctx context.Context, in chan<- []model.TargetGroup) { d.Info("instance is started") + d.Debugf("used config: interval: %s, timeout: %s, cache expiration time: %s", d.interval, d.timeout, d.expiryTime) defer func() { d.Info("instance is stopped") }() close(d.started) @@ -112,6 +111,10 @@ func (d *Discoverer) Discover(ctx context.Context, in chan<- []model.TargetGroup return } + if d.interval == 0 { + return + } + tk := time.NewTicker(d.interval) defer tk.Stop() @@ -295,35 +298,6 @@ func (d *Discoverer) parseLocalListeners(bs []byte) ([]model.Target, error) { return tgts[:n], nil } -type localListenersExec struct { - binPath string - timeout time.Duration -} - -func (e *localListenersExec) discover(ctx context.Context) ([]byte, error) { - execCtx, cancel := context.WithTimeout(ctx, e.timeout) - defer cancel() - - // TCPv4/6 and UPDv4 sockets in LISTEN state - // https://github.com/netdata/netdata/blob/master/src/collectors/plugins.d/local_listeners.c - args := []string{ - "no-udp6", - "no-local", - "no-inbound", - "no-outbound", - "no-namespaces", - } - - cmd := exec.CommandContext(execCtx, e.binPath, args...) - - bs, err := cmd.Output() - if err != nil { - return nil, fmt.Errorf("error on executing '%s': %v", cmd, err) - } - - return bs, nil -} - func extractComm(cmdLine string) string { if i := strings.IndexByte(cmdLine, ' '); i != -1 { cmdLine = cmdLine[:i] diff --git a/src/go/plugin/go.d/agent/discovery/sd/pipeline/funcmap.go b/src/go/plugin/go.d/agent/discovery/sd/pipeline/funcmap.go index 5ed188a54..378e03c25 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/pipeline/funcmap.go +++ b/src/go/plugin/go.d/agent/discovery/sd/pipeline/funcmap.go @@ -7,14 +7,16 @@ import ( "strconv" "text/template" - "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/matcher" + "github.com/netdata/netdata/go/plugins/pkg/matcher" "github.com/Masterminds/sprig/v3" "github.com/bmatcuk/doublestar/v4" ) func newFuncMap() template.FuncMap { - custom := map[string]interface{}{ + fm := sprig.TxtFuncMap() + + extra := map[string]any{ "match": funcMatchAny, "glob": func(value, pattern string, patterns ...string) bool { return funcMatchAny("glob", value, pattern, patterns...) @@ -25,9 +27,7 @@ func newFuncMap() template.FuncMap { }, } - fm := sprig.HermeticTxtFuncMap() - - for name, fn := range custom { + for name, fn := range extra { fm[name] = fn } diff --git a/src/go/plugin/go.d/agent/discovery/sd/pipeline/pipeline_test.go b/src/go/plugin/go.d/agent/discovery/sd/pipeline/pipeline_test.go index e67b6d7ce..4f2e11199 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/pipeline/pipeline_test.go +++ b/src/go/plugin/go.d/agent/discovery/sd/pipeline/pipeline_test.go @@ -35,13 +35,13 @@ func Test_defaultConfigs(t *testing.T) { require.NoError(t, err, "abs path") bs, err := os.ReadFile(file) - require.NoError(t, err, "read config file") + require.NoErrorf(t, err, "read config file '%s'", file) var cfg Config - require.NoError(t, yaml.Unmarshal(bs, &cfg), "unmarshal") + require.NoErrorf(t, yaml.Unmarshal(bs, &cfg), "unmarshal '%s'", e.Name()) _, err = New(cfg) - require.NoError(t, err, "create pipeline") + require.NoErrorf(t, err, "create pipeline '%s'", e.Name()) } } diff --git a/src/go/plugin/go.d/agent/discovery/sd/pipeline/promport.go b/src/go/plugin/go.d/agent/discovery/sd/pipeline/promport.go index 646e1abb1..7edf227c7 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/pipeline/promport.go +++ b/src/go/plugin/go.d/agent/discovery/sd/pipeline/promport.go @@ -46,7 +46,6 @@ var prometheusPortAllocations = map[int]string{ 9125: "statsd_exporter", 9126: "new_relic_exporter", 9127: "pgbouncer_exporter", - 9128: "ceph_exporter", 9129: "haproxy_log_exporter", 9130: "unifi_poller", 9131: "varnish_exporter", @@ -193,7 +192,6 @@ var prometheusPortAllocations = map[int]string{ 9280: "citrix_netscaler_exporter", 9281: "fastd_exporter", 9282: "freeswitch_exporter", - 9283: "ceph_ceph-mgr_prometheus_plugin", 9284: "gobetween", 9285: "database_exporter", 9286: "vdo_compression_and_deduplication_exporter", diff --git a/src/go/plugin/go.d/agent/discovery/sd/sd.go b/src/go/plugin/go.d/agent/discovery/sd/sd.go index 687ebfba8..90207219d 100644 --- a/src/go/plugin/go.d/agent/discovery/sd/sd.go +++ b/src/go/plugin/go.d/agent/discovery/sd/sd.go @@ -9,9 +9,9 @@ import ( "sync" "github.com/netdata/netdata/go/plugins/logger" + "github.com/netdata/netdata/go/plugins/pkg/multipath" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/confgroup" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/discovery/sd/pipeline" - "github.com/netdata/netdata/go/plugins/plugin/go.d/pkg/multipath" "gopkg.in/yaml.v2" ) diff --git a/src/go/plugin/go.d/agent/discovery/sim_test.go b/src/go/plugin/go.d/agent/discovery/sim_test.go index b20344c3c..134ec29f9 100644 --- a/src/go/plugin/go.d/agent/discovery/sim_test.go +++ b/src/go/plugin/go.d/agent/discovery/sim_test.go @@ -9,6 +9,7 @@ import ( "time" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/confgroup" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/src/go/plugin/go.d/agent/functions/manager.go b/src/go/plugin/go.d/agent/functions/manager.go index b7cdecd6a..fe4228a75 100644 --- a/src/go/plugin/go.d/agent/functions/manager.go +++ b/src/go/plugin/go.d/agent/functions/manager.go @@ -13,8 +13,8 @@ import ( "time" "github.com/netdata/netdata/go/plugins/logger" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/netdataapi" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/safewriter" + "github.com/netdata/netdata/go/plugins/pkg/netdataapi" + "github.com/netdata/netdata/go/plugins/pkg/safewriter" ) func NewManager() *Manager { diff --git a/src/go/plugin/go.d/agent/jobmgr/manager.go b/src/go/plugin/go.d/agent/jobmgr/manager.go index 59947be77..b2ba7a2c7 100644 --- a/src/go/plugin/go.d/agent/jobmgr/manager.go +++ b/src/go/plugin/go.d/agent/jobmgr/manager.go @@ -12,12 +12,12 @@ import ( "time" "github.com/netdata/netdata/go/plugins/logger" + "github.com/netdata/netdata/go/plugins/pkg/netdataapi" + "github.com/netdata/netdata/go/plugins/pkg/safewriter" + "github.com/netdata/netdata/go/plugins/pkg/ticker" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/confgroup" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/functions" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/netdataapi" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/safewriter" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/ticker" "github.com/mattn/go-isatty" "gopkg.in/yaml.v2" diff --git a/src/go/plugin/go.d/agent/jobmgr/sim_test.go b/src/go/plugin/go.d/agent/jobmgr/sim_test.go index 9fe67175a..63369c33f 100644 --- a/src/go/plugin/go.d/agent/jobmgr/sim_test.go +++ b/src/go/plugin/go.d/agent/jobmgr/sim_test.go @@ -10,10 +10,11 @@ import ( "testing" "time" + "github.com/netdata/netdata/go/plugins/pkg/netdataapi" + "github.com/netdata/netdata/go/plugins/pkg/safewriter" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/confgroup" "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/module" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/netdataapi" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/safewriter" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/src/go/plugin/go.d/agent/module/charts.go b/src/go/plugin/go.d/agent/module/charts.go index b60b3bac1..70b024702 100644 --- a/src/go/plugin/go.d/agent/module/charts.go +++ b/src/go/plugin/go.d/agent/module/charts.go @@ -78,7 +78,7 @@ type ( } // Chart represents a chart. - // For the full description please visit https://docs.netdata.cloud/collectors/plugins.d/#chart + // For the full description please visit https://docs.netdata.cloud/plugins.d/#chart Chart struct { // typeID is the unique identification of the chart, if not specified, // the orchestrator will use job full name + chart ID as typeID (default behaviour). @@ -128,7 +128,7 @@ type ( } // Dim represents a chart dimension. - // For detailed description please visit https://docs.netdata.cloud/collectors/plugins.d/#dimension. + // For detailed description please visit https://docs.netdata.cloud/plugins.d/#dimension. Dim struct { ID string Name string @@ -141,7 +141,7 @@ type ( } // Var represents a chart variable. - // For detailed description please visit https://docs.netdata.cloud/collectors/plugins.d/#variable + // For detailed description please visit https://docs.netdata.cloud/plugins.d/#variable Var struct { ID string Name string @@ -465,27 +465,19 @@ func checkID(id string) int { } func TestMetricsHasAllChartsDims(t *testing.T, charts *Charts, mx map[string]int64) { - for _, chart := range *charts { - if chart.Obsolete { - continue - } - for _, dim := range chart.Dims { - _, ok := mx[dim.ID] - assert.Truef(t, ok, "missing data for dimension '%s' in chart '%s'", dim.ID, chart.ID) - } - for _, v := range chart.Vars { - _, ok := mx[v.ID] - assert.Truef(t, ok, "missing data for variable '%s' in chart '%s'", v.ID, chart.ID) - } - } + TestMetricsHasAllChartsDimsSkip(t, charts, mx, nil) } -func TestMetricsHasAllChartsDimsSkip(t *testing.T, charts *Charts, mx map[string]int64, skip func(chart *Chart) bool) { +func TestMetricsHasAllChartsDimsSkip(t *testing.T, charts *Charts, mx map[string]int64, skip func(chart *Chart, dim *Dim) bool) { for _, chart := range *charts { - if chart.Obsolete || (skip != nil && skip(chart)) { + if chart.Obsolete { continue } for _, dim := range chart.Dims { + if skip != nil && skip(chart, dim) { + continue + } + _, ok := mx[dim.ID] assert.Truef(t, ok, "missing data for dimension '%s' in chart '%s'", dim.ID, chart.ID) } diff --git a/src/go/plugin/go.d/agent/module/job.go b/src/go/plugin/go.d/agent/module/job.go index 67fae8aa2..3db06ef00 100644 --- a/src/go/plugin/go.d/agent/module/job.go +++ b/src/go/plugin/go.d/agent/module/job.go @@ -16,8 +16,7 @@ import ( "time" "github.com/netdata/netdata/go/plugins/logger" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/netdataapi" - "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes" + "github.com/netdata/netdata/go/plugins/pkg/netdataapi" ) var obsoleteLock = &sync.Mutex{} @@ -228,14 +227,14 @@ func (j *Job) AutoDetection() (err error) { } if err = j.init(); err != nil { - j.Error("init failed") + j.Errorf("init failed: %v", err) j.Unmute() j.disableAutoDetection() return err } if err = j.check(); err != nil { - j.Error("check failed") + j.Errorf("check failed: %v", err) j.Unmute() return err } @@ -244,7 +243,7 @@ func (j *Job) AutoDetection() (err error) { j.Info("check success") if err = j.postCheck(); err != nil { - j.Error("postCheck failed") + j.Errorf("postCheck failed: %v", err) j.disableAutoDetection() return err } @@ -299,13 +298,11 @@ func (j *Job) Cleanup() { return } - if !vnodes.Disabled { - if !j.vnodeCreated && j.vnodeGUID != "" { - _ = j.api.HOSTINFO(j.vnodeGUID, j.vnodeHostname, j.vnodeLabels) - j.vnodeCreated = true - } - _ = j.api.HOST(j.vnodeGUID) + if !j.vnodeCreated && j.vnodeGUID != "" { + _ = j.api.HOSTINFO(j.vnodeGUID, j.vnodeHostname, j.vnodeLabels) + j.vnodeCreated = true } + _ = j.api.HOST(j.vnodeGUID) if j.runChart.created { j.runChart.MarkRemove() @@ -397,15 +394,22 @@ func (j *Job) collect() (result map[string]int64) { } func (j *Job) processMetrics(metrics map[string]int64, startTime time.Time, sinceLastRun int) bool { - if !vnodes.Disabled { - if !j.vnodeCreated && j.vnodeGUID != "" { + if !j.vnodeCreated { + if j.vnodeGUID == "" { + if v := j.module.VirtualNode(); v != nil && v.GUID != "" && v.Hostname != "" { + j.vnodeGUID = v.GUID + j.vnodeHostname = v.Hostname + j.vnodeLabels = v.Labels + } + } + if j.vnodeGUID != "" { _ = j.api.HOSTINFO(j.vnodeGUID, j.vnodeHostname, j.vnodeLabels) j.vnodeCreated = true } - - _ = j.api.HOST(j.vnodeGUID) } + _ = j.api.HOST(j.vnodeGUID) + if !ndInternalMonitoringDisabled && !j.runChart.created { j.runChart.ID = fmt.Sprintf("execution_time_of_%s", j.FullName()) j.createChart(j.runChart) @@ -489,15 +493,15 @@ func (j *Job) createChart(chart *Chart) { if ls == 0 { ls = LabelSourceAuto } - _ = j.api.CLABEL(l.Key, l.Value, ls) + _ = j.api.CLABEL(l.Key, lblReplacer.Replace(l.Value), ls) } } for k, v := range j.labels { if !seen[k] { - _ = j.api.CLABEL(k, v, LabelSourceConf) + _ = j.api.CLABEL(k, lblReplacer.Replace(v), LabelSourceConf) } } - _ = j.api.CLABEL("_collect_job", j.Name(), LabelSourceAuto) + _ = j.api.CLABEL("_collect_job", lblReplacer.Replace(j.Name()), LabelSourceAuto) _ = j.api.CLABELCOMMIT() for _, dim := range chart.Dims { @@ -643,3 +647,5 @@ func handleZero(v int) int { } return v } + +var lblReplacer = strings.NewReplacer("'", "") diff --git a/src/go/plugin/go.d/agent/module/module.go b/src/go/plugin/go.d/agent/module/module.go index 13e20f2ae..8d28d8059 100644 --- a/src/go/plugin/go.d/agent/module/module.go +++ b/src/go/plugin/go.d/agent/module/module.go @@ -7,6 +7,7 @@ import ( "testing" "github.com/netdata/netdata/go/plugins/logger" + "github.com/netdata/netdata/go/plugins/plugin/go.d/agent/vnodes" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -35,6 +36,8 @@ type Module interface { GetBase() *Base Configuration() any + + VirtualNode() *vnodes.VirtualNode } // Base is a helper struct. All modules should embed this struct. @@ -44,12 +47,14 @@ type Base struct { func (b *Base) GetBase() *Base { return b } +func (b *Base) VirtualNode() *vnodes.VirtualNode { return nil } + func TestConfigurationSerialize(t *testing.T, mod Module, cfgJSON, cfgYAML []byte) { t.Helper() tests := map[string]struct { config []byte - unmarshal func(in []byte, out interface{}) (err error) - marshal func(in interface{}) (out []byte, err error) + unmarshal func(in []byte, out any) (err error) + marshal func(in any) (out []byte, err error) }{ "json": {config: cfgJSON, marshal: json.Marshal, unmarshal: json.Unmarshal}, "yaml": {config: cfgYAML, marshal: yaml.Marshal, unmarshal: yaml.Unmarshal}, diff --git a/src/go/plugin/go.d/agent/netdataapi/api.go b/src/go/plugin/go.d/agent/netdataapi/api.go deleted file mode 100644 index 4f2b7a9b5..000000000 --- a/src/go/plugin/go.d/agent/netdataapi/api.go +++ /dev/null @@ -1,213 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package netdataapi - -import ( - "bytes" - "fmt" - "io" - "strconv" -) - -type ( - // API implements Netdata external plugins API. - // https://learn.netdata.cloud/docs/agent/collectors/plugins.d#the-output-of-the-plugin - API struct { - io.Writer - } -) - -const quotes = "' '" - -var ( - end = []byte("END\n\n") - clabelCommit = []byte("CLABEL_COMMIT\n") - newLine = []byte("\n") -) - -func New(w io.Writer) *API { return &API{w} } - -// CHART creates or update a chart. -func (a *API) CHART( - typeID string, - ID string, - name string, - title string, - units string, - family string, - context string, - chartType string, - priority int, - updateEvery int, - options string, - plugin string, - module string) error { - _, err := a.Write([]byte("CHART " + "'" + - typeID + "." + ID + quotes + - name + quotes + - title + quotes + - units + quotes + - family + quotes + - context + quotes + - chartType + quotes + - strconv.Itoa(priority) + quotes + - strconv.Itoa(updateEvery) + quotes + - options + quotes + - plugin + quotes + - module + "'\n")) - return err -} - -// DIMENSION adds or update a dimension to the chart just created. -func (a *API) DIMENSION( - ID string, - name string, - algorithm string, - multiplier int, - divisor int, - options string) error { - _, err := a.Write([]byte("DIMENSION '" + - ID + quotes + - name + quotes + - algorithm + quotes + - strconv.Itoa(multiplier) + quotes + - strconv.Itoa(divisor) + quotes + - options + "'\n")) - return err -} - -// CLABEL adds or update a label to the chart. -func (a *API) CLABEL(key, value string, source int) error { - _, err := a.Write([]byte("CLABEL '" + - key + quotes + - value + quotes + - strconv.Itoa(source) + "'\n")) - return err -} - -// CLABELCOMMIT adds labels to the chart. Should be called after one or more CLABEL. -func (a *API) CLABELCOMMIT() error { - _, err := a.Write(clabelCommit) - return err -} - -// BEGIN initializes data collection for a chart. -func (a *API) BEGIN(typeID string, ID string, msSince int) (err error) { - if msSince > 0 { - _, err = a.Write([]byte("BEGIN " + "'" + typeID + "." + ID + "' " + strconv.Itoa(msSince) + "\n")) - } else { - _, err = a.Write([]byte("BEGIN " + "'" + typeID + "." + ID + "'\n")) - } - return err -} - -// SET sets the value of a dimension for the initialized chart. -func (a *API) SET(ID string, value int64) error { - _, err := a.Write([]byte("SET '" + ID + "' = " + strconv.FormatInt(value, 10) + "\n")) - return err -} - -// SETEMPTY sets the empty value of a dimension for the initialized chart. -func (a *API) SETEMPTY(ID string) error { - _, err := a.Write([]byte("SET '" + ID + "' = \n")) - return err -} - -// VARIABLE sets the value of a CHART scope variable for the initialized chart. -func (a *API) VARIABLE(ID string, value int64) error { - _, err := a.Write([]byte("VARIABLE CHART '" + ID + "' = " + strconv.FormatInt(value, 10) + "\n")) - return err -} - -// END completes data collection for the initialized chart. -func (a *API) END() error { - _, err := a.Write(end) - return err -} - -// DISABLE disables this plugin. This will prevent Netdata from restarting the plugin. -func (a *API) DISABLE() error { - _, err := a.Write([]byte("DISABLE\n")) - return err -} - -// EMPTYLINE writes an empty line. -func (a *API) EMPTYLINE() error { - _, err := a.Write(newLine) - return err -} - -func (a *API) HOSTINFO(guid, hostname string, labels map[string]string) error { - if err := a.HOSTDEFINE(guid, hostname); err != nil { - return err - } - for k, v := range labels { - if err := a.HOSTLABEL(k, v); err != nil { - return err - } - } - return a.HOSTDEFINEEND() -} - -func (a *API) HOSTDEFINE(guid, hostname string) error { - _, err := fmt.Fprintf(a, "HOST_DEFINE '%s' '%s'\n", guid, hostname) - return err -} - -func (a *API) HOSTLABEL(name, value string) error { - _, err := fmt.Fprintf(a, "HOST_LABEL '%s' '%s'\n", name, value) - return err -} - -func (a *API) HOSTDEFINEEND() error { - _, err := fmt.Fprintf(a, "HOST_DEFINE_END\n\n") - return err -} - -func (a *API) HOST(guid string) error { - _, err := a.Write([]byte("HOST " + "'" + - guid + "'\n\n")) - return err -} - -func (a *API) FUNCRESULT(uid, contentType, payload, code, expireTimestamp string) { - var buf bytes.Buffer - - buf.WriteString("FUNCTION_RESULT_BEGIN " + - uid + " " + - code + " " + - contentType + " " + - expireTimestamp + "\n", - ) - - if payload != "" { - buf.WriteString(payload + "\n") - } - - buf.WriteString("FUNCTION_RESULT_END\n\n") - - _, _ = buf.WriteTo(a) -} - -func (a *API) CONFIGCREATE(id, status, configType, path, sourceType, source, supportedCommands string) { - // https://learn.netdata.cloud/docs/contributing/external-plugins/#config - - _, _ = a.Write([]byte("CONFIG " + - id + " " + - "create" + " " + - status + " " + - configType + " " + - path + " " + - sourceType + " '" + - source + "' '" + - supportedCommands + "' 0x0000 0x0000\n\n", - )) -} - -func (a *API) CONFIGDELETE(id string) { - _, _ = a.Write([]byte("CONFIG " + id + " delete\n\n")) -} - -func (a *API) CONFIGSTATUS(id, status string) { - _, _ = a.Write([]byte("CONFIG " + id + " status " + status + "\n\n")) -} diff --git a/src/go/plugin/go.d/agent/netdataapi/api_test.go b/src/go/plugin/go.d/agent/netdataapi/api_test.go deleted file mode 100644 index e5087839b..000000000 --- a/src/go/plugin/go.d/agent/netdataapi/api_test.go +++ /dev/null @@ -1,265 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package netdataapi - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestAPI_CHART(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.CHART( - "", - "id", - "name", - "title", - "units", - "family", - "context", - "line", - 1, - 1, - "", - "plugin", - "module", - ) - - assert.Equal( - t, - "CHART '.id' 'name' 'title' 'units' 'family' 'context' 'line' '1' '1' '' 'plugin' 'module'\n", - buf.String(), - ) -} - -func TestAPI_DIMENSION(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.DIMENSION( - "id", - "name", - "absolute", - 1, - 1, - "", - ) - - assert.Equal( - t, - "DIMENSION 'id' 'name' 'absolute' '1' '1' ''\n", - buf.String(), - ) -} - -func TestAPI_BEGIN(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.BEGIN( - "typeID", - "id", - 0, - ) - - assert.Equal( - t, - "BEGIN 'typeID.id'\n", - buf.String(), - ) - - buf.Reset() - - _ = a.BEGIN( - "typeID", - "id", - 1, - ) - - assert.Equal( - t, - "BEGIN 'typeID.id' 1\n", - buf.String(), - ) -} - -func TestAPI_SET(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.SET("id", 100) - - assert.Equal( - t, - "SET 'id' = 100\n", - buf.String(), - ) -} - -func TestAPI_SETEMPTY(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.SETEMPTY("id") - - assert.Equal( - t, - "SET 'id' = \n", - buf.String(), - ) -} - -func TestAPI_VARIABLE(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.VARIABLE("id", 100) - - assert.Equal( - t, - "VARIABLE CHART 'id' = 100\n", - buf.String(), - ) -} - -func TestAPI_END(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.END() - - assert.Equal( - t, - "END\n\n", - buf.String(), - ) -} - -func TestAPI_CLABEL(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.CLABEL("key", "value", 1) - - assert.Equal( - t, - "CLABEL 'key' 'value' '1'\n", - buf.String(), - ) -} - -func TestAPI_CLABELCOMMIT(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.CLABELCOMMIT() - - assert.Equal( - t, - "CLABEL_COMMIT\n", - buf.String(), - ) -} - -func TestAPI_DISABLE(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.DISABLE() - - assert.Equal( - t, - "DISABLE\n", - buf.String(), - ) -} - -func TestAPI_EMPTYLINE(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.EMPTYLINE() - - assert.Equal( - t, - "\n", - buf.String(), - ) -} - -func TestAPI_HOST(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.HOST("guid") - - assert.Equal( - t, - "HOST 'guid'\n\n", - buf.String(), - ) -} - -func TestAPI_HOSTDEFINE(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.HOSTDEFINE("guid", "hostname") - - assert.Equal( - t, - "HOST_DEFINE 'guid' 'hostname'\n", - buf.String(), - ) -} - -func TestAPI_HOSTLABEL(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.HOSTLABEL("name", "value") - - assert.Equal( - t, - "HOST_LABEL 'name' 'value'\n", - buf.String(), - ) -} - -func TestAPI_HOSTDEFINEEND(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.HOSTDEFINEEND() - - assert.Equal( - t, - "HOST_DEFINE_END\n\n", - buf.String(), - ) -} - -func TestAPI_HOSTINFO(t *testing.T) { - buf := &bytes.Buffer{} - a := API{Writer: buf} - - _ = a.HOSTINFO("guid", "hostname", map[string]string{"label1": "value1"}) - - assert.Equal( - t, - `HOST_DEFINE 'guid' 'hostname' -HOST_LABEL 'label1' 'value1' -HOST_DEFINE_END - -`, - buf.String(), - ) -} - -func TestAPI_FUNCRESULT(t *testing.T) { - -} diff --git a/src/go/plugin/go.d/agent/safewriter/writer.go b/src/go/plugin/go.d/agent/safewriter/writer.go deleted file mode 100644 index 533c1055d..000000000 --- a/src/go/plugin/go.d/agent/safewriter/writer.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package safewriter - -import ( - "io" - "os" - "sync" -) - -var Stdout = New(os.Stdout) - -func New(w io.Writer) io.Writer { - return &writer{ - mx: &sync.Mutex{}, - w: w, - } -} - -type writer struct { - mx *sync.Mutex - w io.Writer -} - -func (w *writer) Write(p []byte) (n int, err error) { - w.mx.Lock() - n, err = w.w.Write(p) - w.mx.Unlock() - return n, err -} diff --git a/src/go/plugin/go.d/agent/ticker/ticker.go b/src/go/plugin/go.d/agent/ticker/ticker.go deleted file mode 100644 index e4228fe4c..000000000 --- a/src/go/plugin/go.d/agent/ticker/ticker.go +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package ticker - -import "time" - -type ( - // Ticker holds a channel that delivers ticks of a clock at intervals. - // The ticks are aligned to interval boundaries. - Ticker struct { - C <-chan int - done chan struct{} - loops int - interval time.Duration - } -) - -// New returns a new Ticker containing a channel that will send the time with a period specified by the duration argument. -// It adjusts the intervals or drops ticks to make up for slow receivers. -// The duration must be greater than zero; if not, New will panic. Stop the Ticker to release associated resources. -func New(interval time.Duration) *Ticker { - ticker := &Ticker{ - interval: interval, - done: make(chan struct{}, 1), - } - ticker.start() - return ticker -} - -func (t *Ticker) start() { - ch := make(chan int) - t.C = ch - go func() { - LOOP: - for { - now := time.Now() - nextRun := now.Truncate(t.interval).Add(t.interval) - - time.Sleep(nextRun.Sub(now)) - select { - case <-t.done: - close(ch) - break LOOP - case ch <- t.loops: - t.loops++ - } - } - }() -} - -// Stop turns off a Ticker. After Stop, no more ticks will be sent. -// Stop does not close the channel, to prevent a read from the channel succeeding incorrectly. -func (t *Ticker) Stop() { - t.done <- struct{}{} -} diff --git a/src/go/plugin/go.d/agent/ticker/ticket_test.go b/src/go/plugin/go.d/agent/ticker/ticket_test.go deleted file mode 100644 index 193085365..000000000 --- a/src/go/plugin/go.d/agent/ticker/ticket_test.go +++ /dev/null @@ -1,50 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later - -package ticker - -import ( - "testing" - "time" -) - -// TODO: often fails Circle CI (~200-240) -var allowedDelta = 500 * time.Millisecond - -func TestTickerParallel(t *testing.T) { - for i := 0; i < 100; i++ { - i := i - go func() { - time.Sleep(time.Second / 100 * time.Duration(i)) - TestTicker(t) - }() - } - time.Sleep(4 * time.Second) -} - -func TestTicker(t *testing.T) { - tk := New(time.Second) - defer tk.Stop() - prev := time.Now() - for i := 0; i < 3; i++ { - <-tk.C - now := time.Now() - diff := abs(now.Round(time.Second).Sub(now)) - if diff >= allowedDelta { - t.Errorf("Ticker is not aligned: expect delta < %v but was: %v (%s)", allowedDelta, diff, now.Format(time.RFC3339Nano)) - } - if i > 0 { - dt := now.Sub(prev) - if abs(dt-time.Second) >= allowedDelta { - t.Errorf("Ticker interval: expect delta < %v ns but was: %v", allowedDelta, abs(dt-time.Second)) - } - } - prev = now - } -} - -func abs(a time.Duration) time.Duration { - if a < 0 { - return -a - } - return a -} diff --git a/src/go/plugin/go.d/agent/vnodes/vnodes.go b/src/go/plugin/go.d/agent/vnodes/vnodes.go index 3d332c261..2c0027b88 100644 --- a/src/go/plugin/go.d/agent/vnodes/vnodes.go +++ b/src/go/plugin/go.d/agent/vnodes/vnodes.go @@ -11,11 +11,10 @@ import ( "github.com/netdata/netdata/go/plugins/logger" + "github.com/google/uuid" "gopkg.in/yaml.v2" ) -var Disabled = false // TODO: remove after Netdata v1.39.0. Fix for "from source" stable-channel installations. - func New(confDir string) *Vnodes { vn := &Vnodes{ Logger: logger.New().With( @@ -39,9 +38,9 @@ type ( vnodes map[string]*VirtualNode } VirtualNode struct { - GUID string `yaml:"guid"` - Hostname string `yaml:"hostname"` - Labels map[string]string `yaml:"labels"` + GUID string `yaml:"guid" json:"guid"` + Hostname string `yaml:"hostname" json:"hostname"` + Labels map[string]string `yaml:"labels" json:"labels"` } ) @@ -101,7 +100,11 @@ func (vn *Vnodes) readConfDir() { for _, v := range cfg { if v.Hostname == "" || v.GUID == "" { - vn.Warningf("skipping virtual node '%+v': some required fields are missing (%s)", v, path) + vn.Warningf("skipping virtual node '%+v': required fields are missing (%s)", v, path) + continue + } + if err := uuid.Validate(v.GUID); err != nil { + vn.Warningf("skipping virtual node '%+v': invalid GUID: %v (%s)", v, err, path) continue } if _, ok := vn.vnodes[v.Hostname]; ok { @@ -127,7 +130,7 @@ func isConfigFile(path string) bool { } } -func loadConfigFile(conf interface{}, path string) error { +func loadConfigFile(conf any, path string) error { f, err := os.Open(path) if err != nil { return err |