master
go 142 lines 2.78 KB
Raw
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/netdata/go/plugins/plugin/framework/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
62 for _, cfg := range modCfg.Jobs {
63 cfg.SetModule(name)
64 def := mergeDef(modCfg.Default, modDef)
65 cfg.ApplyDefaults(def)
66 }
67
68 group := &confgroup.Group{
69 Configs: modCfg.Jobs,
70 Source: path,
71 }
72
73 return group, nil
74 }
75
76 func parseSDFormat(reg confgroup.Registry, path string, bs []byte) (*confgroup.Group, error) {
77 var cfgs sdConfig
78 if err := yaml.Unmarshal(bs, &cfgs); err != nil {
79 return nil, err
80 }
81
82 var i int
83 for _, cfg := range cfgs {
84 if def, ok := reg.Lookup(cfg.Module()); ok && cfg.Module() != "" {
85 cfg.ApplyDefaults(def)
86 cfgs[i] = cfg
87 i++
88 }
89 }
90
91 group := &confgroup.Group{
92 Configs: cfgs[:i],
93 Source: path,
94 }
95
96 return group, nil
97 }
98
99 func cfgFormat(bs []byte) format {
100 var data any
101 if err := yaml.Unmarshal(bs, &data); err != nil {
102 return unknownFormat
103 }
104 if data == nil {
105 return unknownEmptyFormat
106 }
107
108 type (
109 static = map[any]any
110 sd = []any
111 )
112 switch data.(type) {
113 case static:
114 return staticFormat
115 case sd:
116 return sdFormat
117 default:
118 return unknownFormat
119 }
120 }
121
122 func mergeDef(a, b confgroup.Default) confgroup.Default {
123 return confgroup.Default{
124 MinUpdateEvery: firstPositive(a.MinUpdateEvery, b.MinUpdateEvery),
125 UpdateEvery: firstPositive(a.UpdateEvery, b.UpdateEvery),
126 AutoDetectionRetry: firstPositive(a.AutoDetectionRetry, b.AutoDetectionRetry),
127 Priority: firstPositive(a.Priority, b.Priority),
128 }
129 }
130
131 func firstPositive(value int, others ...int) int {
132 if value > 0 || len(others) == 0 {
133 return value
134 }
135 return firstPositive(others[0], others[1:]...)
136 }
137
138 func fileName(path string) string {
139 _, file := filepath.Split(path)
140 ext := filepath.Ext(path)
141 return file[:len(file)-len(ext)]
142 }