github.com/netdata/go.d.plugin@v0.58.1/agent/discovery/file/parse.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package file
     4  
     5  import (
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  
    10  	"github.com/netdata/go.d.plugin/agent/confgroup"
    11  
    12  	"gopkg.in/yaml.v2"
    13  )
    14  
    15  type format int
    16  
    17  const (
    18  	unknownFormat format = iota
    19  	unknownEmptyFormat
    20  	staticFormat
    21  	sdFormat
    22  )
    23  
    24  func parse(req confgroup.Registry, path string) (*confgroup.Group, error) {
    25  	bs, err := os.ReadFile(path)
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	if len(bs) == 0 {
    30  		return nil, nil
    31  	}
    32  
    33  	switch cfgFormat(bs) {
    34  	case staticFormat:
    35  		return parseStaticFormat(req, path, bs)
    36  	case sdFormat:
    37  		return parseSDFormat(req, path, bs)
    38  	case unknownEmptyFormat:
    39  		return nil, nil
    40  	default:
    41  		return nil, fmt.Errorf("unknown file format: '%s'", path)
    42  	}
    43  }
    44  
    45  func parseStaticFormat(reg confgroup.Registry, path string, bs []byte) (*confgroup.Group, error) {
    46  	name := fileName(path)
    47  	// TODO: properly handle module renaming
    48  	// See agent/setup.go buildDiscoveryConf() for details
    49  	if name == "wmi" {
    50  		name = "windows"
    51  	}
    52  	modDef, ok := reg.Lookup(name)
    53  	if !ok {
    54  		return nil, nil
    55  	}
    56  
    57  	var modCfg staticConfig
    58  	if err := yaml.Unmarshal(bs, &modCfg); err != nil {
    59  		return nil, err
    60  	}
    61  	for _, cfg := range modCfg.Jobs {
    62  		cfg.SetModule(name)
    63  		def := mergeDef(modCfg.Default, modDef)
    64  		cfg.Apply(def)
    65  	}
    66  	group := &confgroup.Group{
    67  		Configs: modCfg.Jobs,
    68  		Source:  path,
    69  	}
    70  	return group, nil
    71  }
    72  
    73  func parseSDFormat(reg confgroup.Registry, path string, bs []byte) (*confgroup.Group, error) {
    74  	var cfgs sdConfig
    75  	if err := yaml.Unmarshal(bs, &cfgs); err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	var i int
    80  	for _, cfg := range cfgs {
    81  		if def, ok := reg.Lookup(cfg.Module()); ok && cfg.Module() != "" {
    82  			cfg.Apply(def)
    83  			cfgs[i] = cfg
    84  			i++
    85  		}
    86  	}
    87  
    88  	group := &confgroup.Group{
    89  		Configs: cfgs[:i],
    90  		Source:  path,
    91  	}
    92  	return group, nil
    93  }
    94  
    95  func cfgFormat(bs []byte) format {
    96  	var data interface{}
    97  	if err := yaml.Unmarshal(bs, &data); err != nil {
    98  		return unknownFormat
    99  	}
   100  	if data == nil {
   101  		return unknownEmptyFormat
   102  	}
   103  
   104  	type (
   105  		static = map[interface{}]interface{}
   106  		sd     = []interface{}
   107  	)
   108  	switch data.(type) {
   109  	case static:
   110  		return staticFormat
   111  	case sd:
   112  		return sdFormat
   113  	default:
   114  		return unknownFormat
   115  	}
   116  }
   117  
   118  func mergeDef(a, b confgroup.Default) confgroup.Default {
   119  	return confgroup.Default{
   120  		MinUpdateEvery:     firstPositive(a.MinUpdateEvery, b.MinUpdateEvery),
   121  		UpdateEvery:        firstPositive(a.UpdateEvery, b.UpdateEvery),
   122  		AutoDetectionRetry: firstPositive(a.AutoDetectionRetry, b.AutoDetectionRetry),
   123  		Priority:           firstPositive(a.Priority, b.Priority),
   124  	}
   125  }
   126  
   127  func firstPositive(value int, others ...int) int {
   128  	if value > 0 || len(others) == 0 {
   129  		return value
   130  	}
   131  	return firstPositive(others[0], others[1:]...)
   132  }
   133  
   134  func fileName(path string) string {
   135  	_, file := filepath.Split(path)
   136  	ext := filepath.Ext(path)
   137  	return file[:len(file)-len(ext)]
   138  }