refactor(go.d/azure_monitor): replace profiles magic keyword (#22088)
Ilya Mashchenko committed
Mar 30, 2026 at 21:09 UTC
d1696cde89d3452a42083cf0040081cc552d1e3a
12 files changed
+312
-161
src/go/plugin/go.d/collector/azure_monitor/collector.go
+10
-9
@@ -35,15 +35,16 @@ func New() *Collector {
35
store := metrix.NewCollectorStore()
36
c := &Collector{
37
Config: Config{
38
- UpdateEvery: defaultUpdateEvery,
39
- AutoDetectionRetry: defaultAutoDetectRetry,
40
- Cloud: defaultCloud,
41
- DiscoveryEvery: defaultDiscoveryEvery,
42
- QueryOffset: defaultQueryOffset,
43
- Timeout: defaultTimeout,
44
- MaxConcurrency: defaultMaxConcurrency,
45
- MaxBatchResources: defaultMaxBatchResource,
46
- MaxMetricsPerQuery: defaultMaxMetricsQuery,
38
+ UpdateEvery: defaultUpdateEvery,
39
+ AutoDetectionRetry: defaultAutoDetectRetry,
40
+ Cloud: defaultCloud,
41
+ DiscoveryEvery: defaultDiscoveryEvery,
42
+ QueryOffset: defaultQueryOffset,
43
+ Timeout: defaultTimeout,
44
+ MaxConcurrency: defaultMaxConcurrency,
45
+ MaxBatchResources: defaultMaxBatchResource,
46
+ MaxMetricsPerQuery: defaultMaxMetricsQuery,
47
+ ProfileSelectionMode: profileSelectionModeAuto,
48
},
49
store: store,
50
now: time.Now,
src/go/plugin/go.d/collector/azure_monitor/collector_runtime.go
+4
@@ -74,6 +74,10 @@ type resourceInfo struct {
74
Region string
75
}
76
77
+func (r resourceInfo) String() string {
78
+ return r.Name + " (" + r.Type + ")"
79
+}
80
+
81
type queryBatch struct {
82
Profile *profileRuntime
83
Metrics []*metricRuntime
src/go/plugin/go.d/collector/azure_monitor/collector_test.go
+45
-69
@@ -60,9 +60,12 @@ func TestCollector_Init(t *testing.T) {
60
},
61
"fails on negative timeout": {
62
cfg: Config{
63
- SubscriptionID: "sub-1",
64
- Timeout: confopt.Duration(-time.Second),
65
- Profiles: []string{"postgres_flexible"},
63
+ SubscriptionID: "sub-1",
64
+ Timeout: confopt.Duration(-time.Second),
65
+ ProfileSelectionMode: profileSelectionModeExact,
66
+ ProfileSelectionModeExact: &ProfileSelectionModeExactConfig{
67
+ Profiles: []string{"postgres_flexible"},
68
+ },
69
Auth: cloudauth.AzureADAuthConfig{
70
Mode: cloudauth.AzureADAuthModeDefault,
71
},
@@ -150,7 +153,8 @@ func TestCollector_UsesConfiguredTimeout(t *testing.T) {
153
setup: func(c *Collector, rg *mockResourceGraph, mx *mockMetricsClient) {
154
c.Config = testConfig()
155
c.Config.Timeout = confopt.Duration(timeout)
153
- c.Config.Profiles = []string{"auto"}
156
+ c.Config.ProfileSelectionMode = profileSelectionModeAuto
157
+ c.Config.ProfileSelectionModeExact = nil
158
rg.resources = []map[string]any{
159
{"type": "microsoft.dbforpostgresql/flexibleservers", "count_": int64(1)},
160
}
@@ -357,7 +361,7 @@ func TestCollector_TimeGrainScheduling(t *testing.T) {
361
}
362
363
cfg := testConfig()
360
- cfg.Profiles = []string{"storage_slow"}
364
+ cfg.ProfileSelectionModeExact = &ProfileSelectionModeExactConfig{Profiles: []string{"storage_slow"}}
365
366
catalog := mustLoadStockCatalog(t, map[string]string{
367
"storage_slow.yaml": `
@@ -424,7 +428,8 @@ func TestCollector_InitAutoDiscover(t *testing.T) {
428
429
c := New()
430
c.Config = testConfig()
427
- c.Config.Profiles = []string{"auto"}
431
+ c.Config.ProfileSelectionMode = profileSelectionModeAuto
432
+ c.Config.ProfileSelectionModeExact = nil
433
c.newResourceGraph = func(string, azcore.TokenCredential, azcloud.Configuration) (resourceGraphClient, error) {
434
return rg, nil
435
}
@@ -433,7 +438,8 @@ func TestCollector_InitAutoDiscover(t *testing.T) {
438
}
439
440
require.NoError(t, c.Init(context.Background()))
436
- assert.Contains(t, c.Config.Profiles, "postgres_flexible")
441
+ require.NotEmpty(t, c.runtime.Profiles)
442
+ assert.Equal(t, "postgres_flexible", c.runtime.Profiles[0].ID)
443
}
444
445
func TestCollector_InitAutoDiscoverWithExplicit(t *testing.T) {
@@ -445,7 +451,11 @@ func TestCollector_InitAutoDiscoverWithExplicit(t *testing.T) {
451
452
c := New()
453
c.Config = testConfig()
448
- c.Config.Profiles = []string{"auto", "cosmos_db"}
454
+ c.Config.ProfileSelectionMode = profileSelectionModeCombined
455
+ c.Config.ProfileSelectionModeExact = nil
456
+ c.Config.ProfileSelectionModeCombined = &ProfileSelectionModeCombinedConfig{
457
+ Profiles: []string{"cosmos_db"},
458
+ }
459
c.newResourceGraph = func(string, azcore.TokenCredential, azcloud.Configuration) (resourceGraphClient, error) {
460
return rg, nil
461
}
@@ -454,14 +464,19 @@ func TestCollector_InitAutoDiscoverWithExplicit(t *testing.T) {
464
}
465
466
require.NoError(t, c.Init(context.Background()))
457
- assert.Contains(t, c.Config.Profiles, "cosmos_db")
458
- assert.Contains(t, c.Config.Profiles, "postgres_flexible")
467
+ var ids []string
468
+ for _, p := range c.runtime.Profiles {
469
+ ids = append(ids, p.ID)
470
+ }
471
+ assert.Contains(t, ids, "cosmos_db")
472
+ assert.Contains(t, ids, "postgres_flexible")
473
}
474
461
-func TestCollector_InitEmptyProfilesDefaultsToAuto(t *testing.T) {
475
+func TestCollector_InitDefaultModeIsAuto(t *testing.T) {
476
c := New()
477
c.Config = testConfig()
464
- c.Config.Profiles = []string{}
478
+ c.Config.ProfileSelectionMode = ""
479
+ c.Config.ProfileSelectionModeExact = nil
480
c.newResourceGraph = func(string, azcore.TokenCredential, azcloud.Configuration) (resourceGraphClient, error) {
481
return &mockResourceGraph{}, nil
482
}
@@ -483,7 +498,8 @@ func TestCollector_InitAutoDiscoverNoMatchFails(t *testing.T) {
498
499
c := New()
500
c.Config = testConfig()
486
- c.Config.Profiles = []string{"auto"}
501
+ c.Config.ProfileSelectionMode = profileSelectionModeAuto
502
+ c.Config.ProfileSelectionModeExact = nil
503
c.newResourceGraph = func(string, azcore.TokenCredential, azcloud.Configuration) (resourceGraphClient, error) {
504
return rg, nil
505
}
@@ -496,48 +512,6 @@ func TestCollector_InitAutoDiscoverNoMatchFails(t *testing.T) {
512
assert.Contains(t, err.Error(), "auto-discovery found no Azure resources")
513
}
514
499
-func TestExtractAutoKeyword(t *testing.T) {
500
- tests := map[string]struct {
501
- input []string
502
- wantAuto bool
503
- wantRemain []string
504
- }{
505
- "auto only": {
506
- input: []string{"auto"},
507
- wantAuto: true,
508
- wantRemain: []string{},
509
- },
510
- "auto with explicit": {
511
- input: []string{"auto", "vm", "sql"},
512
- wantAuto: true,
513
- wantRemain: []string{"vm", "sql"},
514
- },
515
- "no auto": {
516
- input: []string{"vm", "sql"},
517
- wantAuto: false,
518
- wantRemain: []string{"vm", "sql"},
519
- },
520
- "empty": {
521
- input: []string{},
522
- wantAuto: false,
523
- wantRemain: []string{},
524
- },
525
- "auto case insensitive": {
526
- input: []string{"AUTO"},
527
- wantAuto: true,
528
- wantRemain: []string{},
529
- },
530
- }
531
-
532
- for name, tc := range tests {
533
- t.Run(name, func(t *testing.T) {
534
- gotAuto, gotRemain := extractAutoKeyword(tc.input)
535
- assert.Equal(t, tc.wantAuto, gotAuto)
536
- assert.Equal(t, tc.wantRemain, gotRemain)
537
- })
538
- }
539
-}
540
-
515
func TestMergeProfileIDs(t *testing.T) {
516
tests := map[string]struct {
517
explicit []string
@@ -570,8 +544,7 @@ func TestMergeProfileIDs(t *testing.T) {
544
}
545
546
func TestBuildCollectorRuntime_DetectsChartIDCollision(t *testing.T) {
573
- cfg := testConfig()
574
- cfg.Profiles = []string{"redis_upper", "redis_lower"}
547
+ profileIDs := []string{"redis_upper", "redis_lower"}
548
549
catalog := mustLoadStockCatalog(t, map[string]string{
550
"redis_upper.yaml": `
@@ -628,23 +601,26 @@ template:
601
`,
602
})
603
631
- _, err := buildCollectorRuntimeFromConfig(cfg, catalog)
604
+ _, err := buildCollectorRuntimeFromConfig(profileIDs, catalog)
605
require.Error(t, err)
606
}
607
608
func testConfig() Config {
609
return Config{
637
- UpdateEvery: 60,
638
- AutoDetectionRetry: 0,
639
- SubscriptionID: "sub-1",
640
- Cloud: "public",
641
- DiscoveryEvery: 300,
642
- QueryOffset: 180,
643
- Timeout: defaultTimeout,
644
- MaxConcurrency: 4,
645
- MaxBatchResources: 50,
646
- MaxMetricsPerQuery: 20,
647
- Profiles: []string{"postgres_flexible"},
610
+ UpdateEvery: 60,
611
+ AutoDetectionRetry: 0,
612
+ SubscriptionID: "sub-1",
613
+ Cloud: "public",
614
+ DiscoveryEvery: 300,
615
+ QueryOffset: 180,
616
+ Timeout: defaultTimeout,
617
+ MaxConcurrency: 4,
618
+ MaxBatchResources: 50,
619
+ MaxMetricsPerQuery: 20,
620
+ ProfileSelectionMode: profileSelectionModeExact,
621
+ ProfileSelectionModeExact: &ProfileSelectionModeExactConfig{
622
+ Profiles: []string{"postgres_flexible"},
623
+ },
624
Auth: cloudauth.AzureADAuthConfig{
625
Mode: cloudauth.AzureADAuthModeDefault,
626
},
src/go/plugin/go.d/collector/azure_monitor/config.go
+60
-23
@@ -24,7 +24,11 @@ const (
24
defaultMaxMetricsQuery = 20
25
)
26
27
-const profileAutoKeyword = "auto"
27
+const (
28
+ profileSelectionModeAuto = "auto"
29
+ profileSelectionModeExact = "exact"
30
+ profileSelectionModeCombined = "combined"
31
+)
32
33
const (
34
cloudPublic = "public"
@@ -33,20 +37,30 @@ const (
37
)
38
39
type Config struct {
36
- Vnode string `yaml:"vnode,omitempty" json:"vnode"`
37
- UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
38
- AutoDetectionRetry int `yaml:"autodetection_retry,omitempty" json:"autodetection_retry"`
39
- SubscriptionID string `yaml:"subscription_id" json:"subscription_id"`
40
- Cloud string `yaml:"cloud,omitempty" json:"cloud"`
41
- DiscoveryEvery int `yaml:"discovery_every,omitempty" json:"discovery_every"`
42
- QueryOffset int `yaml:"query_offset,omitempty" json:"query_offset"`
43
- Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout"`
44
- MaxConcurrency int `yaml:"max_concurrency,omitempty" json:"max_concurrency"`
45
- MaxBatchResources int `yaml:"max_batch_resources,omitempty" json:"max_batch_resources"`
46
- MaxMetricsPerQuery int `yaml:"max_metrics_per_query,omitempty" json:"max_metrics_per_query"`
47
- ResourceGroups []string `yaml:"resource_groups,omitempty" json:"resource_groups"`
48
- Profiles []string `yaml:"profiles,omitempty" json:"profiles"`
49
- Auth cloudauth.AzureADAuthConfig `yaml:"auth" json:"auth"`
40
+ Vnode string `yaml:"vnode,omitempty" json:"vnode"`
41
+ UpdateEvery int `yaml:"update_every,omitempty" json:"update_every"`
42
+ AutoDetectionRetry int `yaml:"autodetection_retry,omitempty" json:"autodetection_retry"`
43
+ SubscriptionID string `yaml:"subscription_id" json:"subscription_id"`
44
+ Cloud string `yaml:"cloud,omitempty" json:"cloud"`
45
+ DiscoveryEvery int `yaml:"discovery_every,omitempty" json:"discovery_every"`
46
+ QueryOffset int `yaml:"query_offset,omitempty" json:"query_offset"`
47
+ Timeout confopt.Duration `yaml:"timeout,omitempty" json:"timeout"`
48
+ MaxConcurrency int `yaml:"max_concurrency,omitempty" json:"max_concurrency"`
49
+ MaxBatchResources int `yaml:"max_batch_resources,omitempty" json:"max_batch_resources"`
50
+ MaxMetricsPerQuery int `yaml:"max_metrics_per_query,omitempty" json:"max_metrics_per_query"`
51
+ ResourceGroups []string `yaml:"resource_groups,omitempty" json:"resource_groups"`
52
+ ProfileSelectionMode string `yaml:"profile_selection_mode,omitempty" json:"profile_selection_mode"`
53
+ ProfileSelectionModeExact *ProfileSelectionModeExactConfig `yaml:"profile_selection_mode_exact,omitempty" json:"profile_selection_mode_exact,omitempty"`
54
+ ProfileSelectionModeCombined *ProfileSelectionModeCombinedConfig `yaml:"profile_selection_mode_combined,omitempty" json:"profile_selection_mode_combined,omitempty"`
55
+ Auth cloudauth.AzureADAuthConfig `yaml:"auth" json:"auth"`
56
+}
57
+
58
+type ProfileSelectionModeExactConfig struct {
59
+ Profiles []string `yaml:"profiles" json:"profiles"`
60
+}
61
+
62
+type ProfileSelectionModeCombinedConfig struct {
63
+ Profiles []string `yaml:"profiles" json:"profiles"`
64
}
65
66
func (c *Config) applyDefaults() {
@@ -77,8 +91,8 @@ func (c *Config) applyDefaults() {
91
if c.MaxMetricsPerQuery <= 0 {
92
c.MaxMetricsPerQuery = defaultMaxMetricsQuery
93
}
80
- if len(c.Profiles) == 0 {
81
- c.Profiles = []string{"auto"}
94
+ if strings.TrimSpace(c.ProfileSelectionMode) == "" {
95
+ c.ProfileSelectionMode = profileSelectionModeAuto
96
}
97
}
98
@@ -120,19 +134,42 @@ func (c Config) validate() error {
134
errs = append(errs, err)
135
}
136
123
- seenProfiles := map[string]struct{}{}
124
- for _, name := range c.Profiles {
137
+ switch strings.ToLower(strings.TrimSpace(c.ProfileSelectionMode)) {
138
+ case profileSelectionModeAuto:
139
+ case profileSelectionModeExact:
140
+ if c.ProfileSelectionModeExact == nil || len(c.ProfileSelectionModeExact.Profiles) == 0 {
141
+ errs = append(errs, errors.New("'profile_selection_mode_exact.profiles' must not be empty when mode is 'exact'"))
142
+ } else {
143
+ errs = append(errs, validateProfilesList(c.ProfileSelectionModeExact.Profiles)...)
144
+ }
145
+ case profileSelectionModeCombined:
146
+ if c.ProfileSelectionModeCombined == nil || len(c.ProfileSelectionModeCombined.Profiles) == 0 {
147
+ errs = append(errs, errors.New("'profile_selection_mode_combined.profiles' must not be empty when mode is 'combined'"))
148
+ } else {
149
+ errs = append(errs, validateProfilesList(c.ProfileSelectionModeCombined.Profiles)...)
150
+ }
151
+ default:
152
+ errs = append(errs, fmt.Errorf("'profile_selection_mode' must be one of: %s, %s, %s",
153
+ profileSelectionModeAuto, profileSelectionModeExact, profileSelectionModeCombined))
154
+ }
155
+
156
+ return errors.Join(errs...)
157
+}
158
+
159
+func validateProfilesList(profiles []string) []error {
160
+ var errs []error
161
+ seen := map[string]struct{}{}
162
+ for _, name := range profiles {
163
n := strings.TrimSpace(name)
164
if n == "" {
165
errs = append(errs, errors.New("'profiles' contains an empty value"))
166
continue
167
}
168
norm := stringsLowerTrim(n)
131
- if _, ok := seenProfiles[norm]; ok {
169
+ if _, ok := seen[norm]; ok {
170
errs = append(errs, fmt.Errorf("'profiles' contains duplicate value '%s'", n))
171
}
134
- seenProfiles[norm] = struct{}{}
172
+ seen[norm] = struct{}{}
173
}
136
-
137
- return errors.Join(errs...)
174
+ return errs
175
}
src/go/plugin/go.d/collector/azure_monitor/config_schema.json
+100
-16
@@ -91,19 +91,16 @@
91
"type": "string"
92
}
93
},
94
- "profiles": {
95
- "title": "Profiles",
96
- "description": "Profile ids to enable. Use 'auto' for automatic discovery based on resource types in the subscription. Combine with explicit ids: ['auto', 'custom_profile']. Empty list disables collection.",
97
- "type": [
98
- "array",
99
- "null"
94
+ "profile_selection_mode": {
95
+ "title": "Profile selection mode",
96
+ "description": "How profiles are selected for monitoring.",
97
+ "type": "string",
98
+ "enum": [
99
+ "auto",
100
+ "exact",
101
+ "combined"
102
],
101
- "items": {
102
- "type": "string"
103
- },
104
- "default": [
105
- "auto"
106
- ]
103
+ "default": "auto"
104
},
105
"auth": {
106
"title": "Authentication",
@@ -205,7 +202,78 @@
202
"required": [
203
"subscription_id",
204
"auth"
208
- ]
205
+ ],
206
+ "dependencies": {
207
+ "profile_selection_mode": {
208
+ "oneOf": [
209
+ {
210
+ "properties": {
211
+ "profile_selection_mode": {
212
+ "const": "auto"
213
+ }
214
+ }
215
+ },
216
+ {
217
+ "properties": {
218
+ "profile_selection_mode": {
219
+ "const": "exact"
220
+ },
221
+ "profile_selection_mode_exact": {
222
+ "title": "Exact profile selection",
223
+ "description": "Settings for exact profile selection mode.",
224
+ "type": "object",
225
+ "properties": {
226
+ "profiles": {
227
+ "title": "Profiles",
228
+ "description": "Profile ids to enable.",
229
+ "type": "array",
230
+ "items": {
231
+ "type": "string"
232
+ },
233
+ "minItems": 1
234
+ }
235
+ },
236
+ "required": [
237
+ "profiles"
238
+ ]
239
+ }
240
+ },
241
+ "required": [
242
+ "profile_selection_mode_exact"
243
+ ]
244
+ },
245
+ {
246
+ "properties": {
247
+ "profile_selection_mode": {
248
+ "const": "combined"
249
+ },
250
+ "profile_selection_mode_combined": {
251
+ "title": "Combined profile selection",
252
+ "description": "Settings for combined profile selection mode. Listed profiles are merged with auto-discovered profiles.",
253
+ "type": "object",
254
+ "properties": {
255
+ "profiles": {
256
+ "title": "Profiles",
257
+ "description": "Profile ids to merge with auto-discovered profiles.",
258
+ "type": "array",
259
+ "items": {
260
+ "type": "string"
261
+ },
262
+ "minItems": 1
263
+ }
264
+ },
265
+ "required": [
266
+ "profiles"
267
+ ]
268
+ }
269
+ },
270
+ "required": [
271
+ "profile_selection_mode_combined"
272
+ ]
273
+ }
274
+ ]
275
+ }
276
+ }
277
},
278
"uiSchema": {
279
"uiOptions": {
@@ -214,8 +282,22 @@
282
"resource_groups": {
283
"ui:listFlavour": "list"
284
},
217
- "profiles": {
218
- "ui:listFlavour": "list"
285
+ "profile_selection_mode": {
286
+ "ui:widget": "radio",
287
+ "ui:options": {
288
+ "inline": true
289
+ },
290
+ "ui:help": "Choose how profiles are selected.\n\n- `auto`: Automatically discovers resource types in the subscription via Azure Resource Graph and enables matching profiles.\n- `exact`: Uses only the explicitly listed profile ids.\n- `combined`: Merges explicitly listed profile ids with auto-discovered profiles."
291
+ },
292
+ "profile_selection_mode_exact": {
293
+ "profiles": {
294
+ "ui:listFlavour": "list"
295
+ }
296
+ },
297
+ "profile_selection_mode_combined": {
298
+ "profiles": {
299
+ "ui:listFlavour": "list"
300
+ }
301
},
302
"timeout": {
303
"ui:help": "Accepts decimals for sub-second granularity (for example, `0.5` for 500ms)."
@@ -270,7 +352,9 @@
352
{
353
"title": "Profiles",
354
"fields": [
273
- "profiles",
355
+ "profile_selection_mode",
356
+ "profile_selection_mode_exact",
357
+ "profile_selection_mode_combined",
358
"resource_groups"
359
]
360
},
src/go/plugin/go.d/collector/azure_monitor/discover.go
+5
@@ -5,6 +5,7 @@ package azure_monitor
5
import (
6
"context"
7
"fmt"
8
+ "slices"
9
"strings"
10
"time"
11
@@ -23,6 +24,10 @@ func (c *Collector) refreshDiscovery(ctx context.Context, force bool) ([]resourc
24
return nil, err
25
}
26
27
+ if !slices.Equal(resources, c.discovery.Resources) {
28
+ c.Infof("discovered %d resources: %v", len(resources), resources)
29
+ }
30
+
31
c.discovery = discoveryState{
32
Resources: resources,
33
ByType: byType,
src/go/plugin/go.d/collector/azure_monitor/init.go
+26
-29
@@ -57,7 +57,7 @@ func (c *Collector) initInstruments(runtime *collectorRuntime) error {
57
}
58
59
func (c *Collector) prepareInitResult(ctx context.Context) (*initResult, error) {
60
- cfg, catalog, autoDiscover, err := c.prepareInitConfig()
60
+ cfg, catalog, autoDiscover, explicitProfiles, err := c.prepareInitConfig()
61
if err != nil {
62
return nil, err
63
}
@@ -67,19 +67,23 @@ func (c *Collector) prepareInitResult(ctx context.Context) (*initResult, error)
67
return nil, err
68
}
69
70
+ var profileIDs []string
71
+
72
if autoDiscover {
71
- profiles, err := resolveAutoProfiles(ctx, cfg.SubscriptionID, cfg.Timeout.Duration(), resourceGraph, catalog, cfg.Profiles)
73
+ profiles, err := resolveAutoProfiles(ctx, cfg.SubscriptionID, cfg.Timeout.Duration(), resourceGraph, catalog, explicitProfiles)
74
if err != nil {
75
return nil, fmt.Errorf("auto-discover resource types: %w", err)
76
}
75
- cfg.Profiles = profiles
76
- if len(cfg.Profiles) == 0 {
77
+ if len(profiles) == 0 {
78
return nil, errors.New("auto-discovery found no Azure resources matching any known profile")
79
}
79
- c.Infof("auto-discovery resolved profiles: %v", cfg.Profiles)
80
+ c.Infof("auto-discovery resolved profiles: %v", profiles)
81
+ profileIDs = profiles
82
+ } else {
83
+ profileIDs = explicitProfiles
84
}
85
82
- runtime, err := buildCollectorRuntimeFromConfig(cfg, catalog)
86
+ runtime, err := buildCollectorRuntimeFromConfig(profileIDs, catalog)
87
if err != nil {
88
return nil, fmt.Errorf("build collector runtime: %w", err)
89
}
@@ -92,27 +96,33 @@ func (c *Collector) prepareInitResult(ctx context.Context) (*initResult, error)
96
}, nil
97
}
98
95
-func (c *Collector) prepareInitConfig() (Config, azureprofiles.Catalog, bool, error) {
99
+func (c *Collector) prepareInitConfig() (Config, azureprofiles.Catalog, bool, []string, error) {
100
cfg := c.Config
101
cfg.applyDefaults()
102
103
catalog, err := c.loadProfileCatalog()
104
if err != nil {
101
- return Config{}, azureprofiles.Catalog{}, false, fmt.Errorf("load profiles catalog: %w", err)
105
+ return Config{}, azureprofiles.Catalog{}, false, nil, fmt.Errorf("load profiles catalog: %w", err)
106
}
107
104
- autoDiscover, explicitProfiles := extractAutoKeyword(cfg.Profiles)
105
- cfg.Profiles = explicitProfiles
106
-
107
- if !autoDiscover && len(cfg.Profiles) == 0 {
108
- return Config{}, azureprofiles.Catalog{}, false, errors.New("no profiles configured; use 'auto' for auto-discovery or specify profile ids")
108
+ if err := cfg.validate(); err != nil {
109
+ return Config{}, azureprofiles.Catalog{}, false, nil, fmt.Errorf("config validation: %w", err)
110
}
111
111
- if err := cfg.validate(); err != nil {
112
- return Config{}, azureprofiles.Catalog{}, false, fmt.Errorf("config validation: %w", err)
112
+ var autoDiscover bool
113
+ var explicitProfiles []string
114
+
115
+ switch stringsLowerTrim(cfg.ProfileSelectionMode) {
116
+ case profileSelectionModeAuto:
117
+ autoDiscover = true
118
+ case profileSelectionModeExact:
119
+ explicitProfiles = cfg.ProfileSelectionModeExact.Profiles
120
+ case profileSelectionModeCombined:
121
+ autoDiscover = true
122
+ explicitProfiles = cfg.ProfileSelectionModeCombined.Profiles
123
}
124
115
- return cfg, catalog, autoDiscover, nil
125
+ return cfg, catalog, autoDiscover, explicitProfiles, nil
126
}
127
128
func (c *Collector) prepareInitClients(cfg Config) (resourceGraphClient, *queryExecutor, error) {
@@ -153,19 +163,6 @@ func createCredential(auth cloudauth.AzureADAuthConfig, cloudCfg azcloud.Configu
163
})
164
}
165
156
-func extractAutoKeyword(profiles []string) (bool, []string) {
157
- auto := false
158
- filtered := make([]string, 0, len(profiles))
159
- for _, p := range profiles {
160
- if stringsLowerTrim(p) == profileAutoKeyword {
161
- auto = true
162
- continue
163
- }
164
- filtered = append(filtered, p)
165
- }
166
- return auto, filtered
167
-}
168
-
166
func mergeProfileIDs(explicit, discovered []string) []string {
167
seen := make(map[string]struct{}, len(explicit)+len(discovered))
168
merged := make([]string, 0, len(explicit)+len(discovered))
src/go/plugin/go.d/collector/azure_monitor/metadata.yaml
+14
-4
@@ -41,7 +41,7 @@ modules:
41
default_behavior:
42
auto_detection:
43
description: |
44
- When `profiles` includes `auto` (the default), the collector queries Azure Resource Graph
44
+ When `profile_selection_mode` is `auto` (the default), the collector queries Azure Resource Graph
45
to discover which resource types exist in the subscription and enables matching built-in profiles automatically.
46
limits:
47
description: |
@@ -142,9 +142,19 @@ modules:
142
default_value: 20
143
required: false
144
group: Limits
145
- - name: profiles
146
- description: "Profile ids to enable. Use `auto` to discover resource types via Azure Resource Graph and enable matching profiles. Combine with explicit ids: `[auto, custom_profile]`."
147
- default_value: "[auto]"
145
+ - name: profile_selection_mode
146
+ description: "Profile selection mode: `auto` discovers matching profiles via Azure Resource Graph, `exact` uses only listed profile ids, `combined` merges listed ids with auto-discovered profiles."
147
+ default_value: "auto"
148
+ required: false
149
+ group: Profiles
150
+ - name: profile_selection_mode_exact.profiles
151
+ description: Profile ids to enable (used when `profile_selection_mode` is `exact`).
152
+ default_value: "[]"
153
+ required: false
154
+ group: Profiles
155
+ - name: profile_selection_mode_combined.profiles
156
+ description: Profile ids to merge with auto-discovered profiles (used when `profile_selection_mode` is `combined`).
157
+ default_value: "[]"
158
required: false
159
group: Profiles
160
- name: resource_groups
src/go/plugin/go.d/collector/azure_monitor/plan.go
+2
-2
@@ -12,8 +12,8 @@ import (
12
"gopkg.in/yaml.v3"
13
)
14
15
-func buildCollectorRuntimeFromConfig(cfg Config, catalog azureprofiles.Catalog) (*collectorRuntime, error) {
16
- profiles, err := catalog.Resolve(cfg.Profiles)
15
+func buildCollectorRuntimeFromConfig(profileIDs []string, catalog azureprofiles.Catalog) (*collectorRuntime, error) {
16
+ profiles, err := catalog.Resolve(profileIDs)
17
if err != nil {
18
return nil, err
19
}
src/go/plugin/go.d/collector/azure_monitor/testdata/config.json
+6
-3
@@ -9,9 +9,12 @@
9
"max_concurrency": 4,
10
"max_batch_resources": 50,
11
"max_metrics_per_query": 20,
12
- "profiles": [
13
- "sql_managed_instance"
14
- ],
12
+ "profile_selection_mode": "exact",
13
+ "profile_selection_mode_exact": {
14
+ "profiles": [
15
+ "sql_managed_instance"
16
+ ]
17
+ },
18
"resource_groups": [
19
"rg-a"
20
],
src/go/plugin/go.d/collector/azure_monitor/testdata/config.yaml
+4
-2
@@ -7,8 +7,10 @@ timeout: 30
7
max_concurrency: 4
8
max_batch_resources: 50
9
max_metrics_per_query: 20
10
-profiles:
11
- - sql_managed_instance
10
+profile_selection_mode: exact
11
+profile_selection_mode_exact:
12
+ profiles:
13
+ - sql_managed_instance
14
resource_groups:
15
- rg-a
16
auth:
src/go/plugin/go.d/config/go.d/azure_monitor.conf
+36
-4
@@ -8,14 +8,46 @@
8
## Filenames are packaging only; matching filename == profile id is recommended.
9
## The default profile catalog is loaded once per go.d process and cached after the first successful load.
10
## Changes to profile files under those default dirs require a go.d process restart to take effect.
11
-## `auto` resolves matching profiles once during collector initialization.
11
+##
12
+## Profile selection modes:
13
+## auto - discovers resource types via Azure Resource Graph and enables matching profiles (default).
14
+## exact - uses only the explicitly listed profile ids.
15
+## combined - merges explicitly listed profile ids with auto-discovered profiles.
16
17
#jobs:
14
-# - name: example
18
+# - name: example_auto
19
+# subscription_id: "<subscription-id>"
20
+# timeout: 30
21
+# profile_selection_mode: auto
22
+# auth:
23
+# mode: service_principal
24
+# mode_service_principal:
25
+# tenant_id: "<tenant-id>"
26
+# client_id: "<client-id>"
27
+# client_secret: "<client-secret>"
28
+#
29
+# - name: example_exact
30
+# subscription_id: "<subscription-id>"
31
+# timeout: 30
32
+# profile_selection_mode: exact
33
+# profile_selection_mode_exact:
34
+# profiles:
35
+# - postgres_flexible
36
+# - cosmos_db
37
+# auth:
38
+# mode: service_principal
39
+# mode_service_principal:
40
+# tenant_id: "<tenant-id>"
41
+# client_id: "<client-id>"
42
+# client_secret: "<client-secret>"
43
+#
44
+# - name: example_combined
45
# subscription_id: "<subscription-id>"
46
# timeout: 30
17
-# profiles:
18
-# - auto
47
+# profile_selection_mode: combined
48
+# profile_selection_mode_combined:
49
+# profiles:
50
+# - custom_profile
51
# auth:
52
# mode: service_principal
53
# mode_service_principal: