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  }