github.com/netdata/go.d.plugin@v0.58.1/agent/vnodes/vnodes.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package vnodes 4 5 import ( 6 "io" 7 "io/fs" 8 "log/slog" 9 "os" 10 "path/filepath" 11 "sync" 12 13 "github.com/netdata/go.d.plugin/logger" 14 15 "gopkg.in/yaml.v2" 16 ) 17 18 var Disabled = false // TODO: remove after Netdata v1.39.0. Fix for "from source" stable-channel installations. 19 20 func New(confDir string) *Vnodes { 21 vn := &Vnodes{ 22 Logger: logger.New().With( 23 slog.String("component", "vnodes"), 24 ), 25 26 confDir: confDir, 27 vnodes: make(map[string]*VirtualNode), 28 } 29 30 vn.readConfDir() 31 32 return vn 33 } 34 35 type ( 36 Vnodes struct { 37 *logger.Logger 38 39 confDir string 40 mux *sync.Mutex 41 vnodes map[string]*VirtualNode 42 } 43 VirtualNode struct { 44 GUID string `yaml:"guid"` 45 Hostname string `yaml:"hostname"` 46 Labels map[string]string `yaml:"labels"` 47 } 48 ) 49 50 func (vn *Vnodes) Lookup(key string) (*VirtualNode, bool) { 51 v, ok := vn.vnodes[key] 52 return v, ok 53 } 54 55 func (vn *Vnodes) Len() int { 56 return len(vn.vnodes) 57 } 58 59 func (vn *Vnodes) readConfDir() { 60 _ = filepath.WalkDir(vn.confDir, func(path string, d fs.DirEntry, err error) error { 61 if err != nil { 62 vn.Warning(err) 63 return nil 64 } 65 66 if !d.Type().IsRegular() || !isConfigFile(path) { 67 return nil 68 } 69 70 var cfg []VirtualNode 71 if err := loadConfigFile(&cfg, path); err != nil { 72 vn.Warning(err) 73 return nil 74 } 75 76 for _, v := range cfg { 77 if v.Hostname == "" || v.GUID == "" { 78 vn.Warningf("skipping virtual node '%+v': some required fields are missing (%s)", v, path) 79 continue 80 } 81 if _, ok := vn.vnodes[v.Hostname]; ok { 82 vn.Warningf("skipping virtual node '%+v': duplicate node (%s)", v, path) 83 continue 84 } 85 86 v := v 87 vn.Debugf("adding virtual node'%+v' (%s)", v, path) 88 vn.vnodes[v.Hostname] = &v 89 } 90 91 return nil 92 }) 93 } 94 95 func isConfigFile(path string) bool { 96 switch filepath.Ext(path) { 97 case ".yaml", ".yml", ".conf": 98 return true 99 default: 100 return false 101 } 102 } 103 104 func loadConfigFile(conf interface{}, path string) error { 105 f, err := os.Open(path) 106 if err != nil { 107 return err 108 } 109 defer func() { _ = f.Close() }() 110 111 if err := yaml.NewDecoder(f).Decode(conf); err != nil && err != io.EOF { 112 return err 113 } 114 115 return nil 116 }