master
go 359 lines 9.75 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package agent
4
5 import (
6 "os"
7 "path/filepath"
8 "testing"
9
10 "github.com/netdata/netdata/go/plugins/plugin/agent/discovery"
11 "github.com/netdata/netdata/go/plugins/plugin/agent/policy"
12 "github.com/netdata/netdata/go/plugins/plugin/agent/secrets/secretstore"
13 "github.com/netdata/netdata/go/plugins/plugin/framework/collectorapi"
14 "github.com/netdata/netdata/go/plugins/plugin/framework/confgroup"
15
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18 "gopkg.in/yaml.v2"
19 )
20
21 func TestConfig_UnmarshalYAML(t *testing.T) {
22 tests := map[string]struct {
23 input string
24 wantCfg config
25 }{
26 "valid configuration": {
27 input: "enabled: yes\ndefault_run: yes\nmodules:\n module1: yes\n module2: yes",
28 wantCfg: config{
29 Enabled: true,
30 DefaultRun: true,
31 Modules: map[string]bool{
32 "module1": true,
33 "module2": true,
34 },
35 },
36 },
37 "valid configuration with broken modules section": {
38 input: "enabled: yes\ndefault_run: yes\nmodules:\nmodule1: yes\nmodule2: yes",
39 wantCfg: config{
40 Enabled: true,
41 DefaultRun: true,
42 Modules: map[string]bool{
43 "module1": true,
44 "module2": true,
45 },
46 },
47 },
48 }
49
50 for name, test := range tests {
51 t.Run(name, func(t *testing.T) {
52 var cfg config
53 err := yaml.Unmarshal([]byte(test.input), &cfg)
54 require.NoError(t, err)
55 assert.Equal(t, test.wantCfg, cfg)
56 })
57 }
58 }
59
60 func TestAgent_loadConfig(t *testing.T) {
61 tests := map[string]struct {
62 agent *Agent
63 wantCfg config
64 }{
65 "valid config file": {
66 agent: &Agent{
67 Name: "agent-valid",
68 ConfigDir: []string{"testdata"},
69 },
70 wantCfg: config{
71 Enabled: true,
72 DefaultRun: true,
73 MaxProcs: 1,
74 Modules: map[string]bool{
75 "module1": true,
76 "module2": true,
77 },
78 },
79 },
80 "no config path provided": {
81 agent: &Agent{},
82 wantCfg: defaultConfig(),
83 },
84 "config file not found": {
85 agent: &Agent{
86 Name: "agent",
87 ConfigDir: []string{"testdata/not-exist"},
88 },
89 wantCfg: defaultConfig(),
90 },
91 "empty config file": {
92 agent: &Agent{
93 Name: "agent-empty",
94 ConfigDir: []string{"testdata"},
95 },
96 wantCfg: defaultConfig(),
97 },
98 "invalid syntax config file": {
99 agent: &Agent{
100 Name: "agent-invalid-syntax",
101 ConfigDir: []string{"testdata"},
102 },
103 wantCfg: defaultConfig(),
104 },
105 }
106
107 for name, test := range tests {
108 t.Run(name, func(t *testing.T) {
109 assert.Equal(t, test.wantCfg, test.agent.loadPluginConfig())
110 })
111 }
112 }
113
114 func TestAgent_loadEnabledModules(t *testing.T) {
115 tests := map[string]struct {
116 agent *Agent
117 cfg config
118 wantModules collectorapi.Registry
119 }{
120 "load all, module disabled by default but explicitly enabled": {
121 agent: &Agent{
122 ModuleRegistry: collectorapi.Registry{
123 "module1": collectorapi.Creator{Defaults: collectorapi.Defaults{Disabled: true}},
124 },
125 },
126 cfg: config{
127 Modules: map[string]bool{"module1": true},
128 },
129 wantModules: collectorapi.Registry{
130 "module1": collectorapi.Creator{Defaults: collectorapi.Defaults{Disabled: true}},
131 },
132 },
133 "load all, module disabled by default and not explicitly enabled": {
134 agent: &Agent{
135 ModuleRegistry: collectorapi.Registry{
136 "module1": collectorapi.Creator{Defaults: collectorapi.Defaults{Disabled: true}},
137 },
138 },
139 wantModules: collectorapi.Registry{},
140 },
141 "load all, module in config modules (default_run=true)": {
142 agent: &Agent{
143 ModuleRegistry: collectorapi.Registry{
144 "module1": collectorapi.Creator{},
145 },
146 },
147 cfg: config{
148 Modules: map[string]bool{"module1": true},
149 DefaultRun: true,
150 },
151 wantModules: collectorapi.Registry{
152 "module1": collectorapi.Creator{},
153 },
154 },
155 "load all, module not in config modules (default_run=true)": {
156 agent: &Agent{
157 ModuleRegistry: collectorapi.Registry{"module1": collectorapi.Creator{}},
158 },
159 cfg: config{
160 DefaultRun: true,
161 },
162 wantModules: collectorapi.Registry{"module1": collectorapi.Creator{}},
163 },
164 "load all, module in config modules (default_run=false)": {
165 agent: &Agent{
166 ModuleRegistry: collectorapi.Registry{
167 "module1": collectorapi.Creator{},
168 },
169 },
170 cfg: config{
171 Modules: map[string]bool{"module1": true},
172 },
173 wantModules: collectorapi.Registry{
174 "module1": collectorapi.Creator{},
175 },
176 },
177 "load all, module not in config modules (default_run=false)": {
178 agent: &Agent{
179 ModuleRegistry: collectorapi.Registry{
180 "module1": collectorapi.Creator{},
181 },
182 },
183 wantModules: collectorapi.Registry{},
184 },
185 "load specific, module exist in registry": {
186 agent: &Agent{
187 RunModule: "module1",
188 ModuleRegistry: collectorapi.Registry{
189 "module1": collectorapi.Creator{},
190 },
191 },
192 wantModules: collectorapi.Registry{
193 "module1": collectorapi.Creator{},
194 },
195 },
196 "load specific, module doesnt exist in registry": {
197 agent: &Agent{
198 RunModule: "module3",
199 ModuleRegistry: collectorapi.Registry{},
200 },
201 wantModules: collectorapi.Registry{},
202 },
203 }
204
205 for name, test := range tests {
206 t.Run(name, func(t *testing.T) {
207 assert.Equal(t, test.wantModules, test.agent.loadEnabledModules(test.cfg))
208 })
209 }
210 }
211
212 func TestIsStockConfig(t *testing.T) {
213 assert.True(t, isStockConfig("/usr/lib/netdata/conf.d/go.d/module.conf"))
214 assert.False(t, isStockConfig("/etc/netdata/go.d/module.conf"))
215 }
216
217 func TestAgent_setupSecretStoreConfigs(t *testing.T) {
218 t.Run("loads merged configs from collectors dirs only", func(t *testing.T) {
219 base := t.TempDir()
220 configRoot := filepath.Join(base, "etc", "netdata")
221 userCollectors := filepath.Join(base, "etc", "netdata", "go.d")
222 stockCollectors := filepath.Join(base, "usr", "lib", "netdata", "conf.d", "go.d")
223
224 mustWriteAgentSecretStoreConfigFile(t, filepath.Join(configRoot, "ss", "vault.conf"), `
225 jobs:
226 - name: ignored
227 mode: token
228 mode_token:
229 token: config-dir-should-not-load
230 addr: https://vault.example
231 `)
232 mustWriteAgentSecretStoreConfigFile(t, filepath.Join(userCollectors, "ss", "vault.conf"), `
233 jobs:
234 - name: vault_prod
235 mode: token
236 mode_token:
237 token: user-token
238 addr: https://vault.example
239 `)
240 mustWriteAgentSecretStoreConfigFile(t, filepath.Join(stockCollectors, "ss", "aws-sm.conf"), `
241 jobs:
242 - name: aws_prod
243 auth_mode: env
244 region: us-east-1
245 `)
246
247 agent := &Agent{
248 ConfigDir: []string{configRoot},
249 CollectorsConfDir: []string{userCollectors, stockCollectors},
250 }
251
252 cfgs := agent.setupSecretStoreConfigs()
253 require.Len(t, cfgs, 2)
254 assert.Equal(t, "vault_prod", cfgs[0].Name())
255 assert.Equal(t, secretstore.KindVault, cfgs[0].Kind())
256 assert.Equal(t, confgroup.TypeUser, cfgs[0].SourceType())
257 assert.Equal(t, "aws_prod", cfgs[1].Name())
258 assert.Equal(t, secretstore.KindAWSSM, cfgs[1].Kind())
259 assert.Equal(t, confgroup.TypeStock, cfgs[1].SourceType())
260 })
261
262 t.Run("no collectors config dirs returns nil", func(t *testing.T) {
263 agent := &Agent{}
264 assert.Nil(t, agent.setupSecretStoreConfigs())
265 })
266 }
267
268 func mustWriteAgentSecretStoreConfigFile(t *testing.T, path, content string) {
269 t.Helper()
270 require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
271 require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
272 }
273
274 func TestAgent_buildDiscoveryConf(t *testing.T) {
275 providers := []discovery.ProviderFactory{
276 discovery.NewProviderFactory("noop", nil),
277 }
278 enabled := collectorapi.Registry{
279 "module1": collectorapi.Creator{},
280 }
281
282 t.Run("inside k8s with no collectors dir disables provider list", func(t *testing.T) {
283 a := &Agent{
284 IsInsideK8s: true,
285 DiscoveryProviders: providers,
286 }
287
288 cfg := a.buildDiscoveryConf(enabled, nil)
289 assert.True(t, cfg.BuildContext.Policy.IsInsideK8s)
290 assert.Empty(t, cfg.Providers)
291 })
292
293 t.Run("outside k8s keeps injected providers", func(t *testing.T) {
294 a := &Agent{
295 IsInsideK8s: false,
296 DiscoveryProviders: providers,
297 }
298
299 cfg := a.buildDiscoveryConf(enabled, nil)
300 assert.False(t, cfg.BuildContext.Policy.IsInsideK8s)
301 assert.Len(t, cfg.Providers, 1)
302 })
303 }
304
305 func TestAgent_buildDiscoveryConf_serviceDiscoveryGating(t *testing.T) {
306 providers := []discovery.ProviderFactory{
307 discovery.NewProviderFactory("noop", nil),
308 }
309 enabled := collectorapi.Registry{
310 "module1": collectorapi.Creator{},
311 }
312
313 tests := map[string]struct {
314 agent *Agent
315 wantSDDir []string
316 wantWatchPath []string
317 }{
318 "terminal mode disables service discovery without changing collector watch paths": {
319 agent: &Agent{
320 runModePolicy: policy.Agent(true),
321 ServiceDiscoveryConfigDir: []string{"sd"},
322 CollectorsConfDir: []string{"collectors"},
323 CollectorsConfigWatchPath: []string{"watch/*.conf"},
324 DiscoveryProviders: providers,
325 },
326 wantWatchPath: []string{"watch/*.conf"},
327 },
328 "plugin-level disable overrides non-terminal service discovery policy": {
329 agent: &Agent{
330 runModePolicy: policy.Agent(false),
331 DisableServiceDiscovery: true,
332 ServiceDiscoveryConfigDir: []string{"sd"},
333 CollectorsConfDir: []string{"collectors"},
334 DiscoveryProviders: providers,
335 },
336 },
337 "non-terminal mode keeps service discovery enabled": {
338 agent: &Agent{
339 runModePolicy: policy.Agent(false),
340 ServiceDiscoveryConfigDir: []string{"sd"},
341 CollectorsConfDir: []string{"collectors"},
342 CollectorsConfigWatchPath: []string{"watch/*.conf"},
343 DiscoveryProviders: providers,
344 },
345 wantSDDir: []string{"sd"},
346 wantWatchPath: []string{"watch/*.conf"},
347 },
348 }
349
350 for name, test := range tests {
351 t.Run(name, func(t *testing.T) {
352 require.NotNil(t, test.agent)
353
354 cfg := test.agent.buildDiscoveryConf(enabled, nil)
355 assert.Equal(t, test.wantSDDir, []string(cfg.BuildContext.Paths.ServiceDiscoveryConfigDir))
356 assert.Equal(t, test.wantWatchPath, cfg.BuildContext.Paths.CollectorsConfigWatchPath)
357 })
358 }
359 }