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 }