summaryrefslogtreecommitdiffstats
path: root/src/go/collectors/go.d.plugin/agent/vnodes/vnodes.go
blob: 2c59f2ad1044a2ce3bbb5d3e37a51d63109679c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// SPDX-License-Identifier: GPL-3.0-or-later

package vnodes

import (
	"io"
	"io/fs"
	"log/slog"
	"os"
	"path/filepath"

	"github.com/netdata/netdata/go/go.d.plugin/logger"

	"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(
			slog.String("component", "vnodes"),
		),

		confDir: confDir,
		vnodes:  make(map[string]*VirtualNode),
	}

	vn.readConfDir()

	return vn
}

type (
	Vnodes struct {
		*logger.Logger

		confDir string
		vnodes  map[string]*VirtualNode
	}
	VirtualNode struct {
		GUID     string            `yaml:"guid"`
		Hostname string            `yaml:"hostname"`
		Labels   map[string]string `yaml:"labels"`
	}
)

func (vn *Vnodes) Lookup(key string) (*VirtualNode, bool) {
	v, ok := vn.vnodes[key]
	return v, ok
}

func (vn *Vnodes) Len() int {
	return len(vn.vnodes)
}

func (vn *Vnodes) readConfDir() {
	_ = filepath.WalkDir(vn.confDir, func(path string, d fs.DirEntry, err error) error {
		if err != nil {
			vn.Warning(err)
			return nil
		}

		if !d.Type().IsRegular() || !isConfigFile(path) {
			return nil
		}

		var cfg []VirtualNode
		if err := loadConfigFile(&cfg, path); err != nil {
			vn.Warning(err)
			return nil
		}

		for _, v := range cfg {
			if v.Hostname == "" || v.GUID == "" {
				vn.Warningf("skipping virtual node '%+v': some required fields are missing (%s)", v, path)
				continue
			}
			if _, ok := vn.vnodes[v.Hostname]; ok {
				vn.Warningf("skipping virtual node '%+v': duplicate node (%s)", v, path)
				continue
			}

			v := v
			vn.Debugf("adding virtual node'%+v' (%s)", v, path)
			vn.vnodes[v.Hostname] = &v
		}

		return nil
	})
}

func isConfigFile(path string) bool {
	switch filepath.Ext(path) {
	case ".yaml", ".yml", ".conf":
		return true
	default:
		return false
	}
}

func loadConfigFile(conf interface{}, path string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()

	if err := yaml.NewDecoder(f).Decode(conf); err != nil && err != io.EOF {
		return err
	}

	return nil
}