Skip to content

Commit caea6ba

Browse files
fix: Safeguard closing nil channels in azure metrics module (#50498)
* fix: Safeguard closing nil channels in azure metrics module * add changelog
1 parent 9702d0a commit caea6ba

3 files changed

Lines changed: 66 additions & 13 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Kind can be one of:
2+
# - breaking-change: a change to previously-documented behavior
3+
# - deprecation: functionality that is being removed in a later release
4+
# - bug-fix: fixes a problem in a previous version
5+
# - enhancement: extends functionality but does not break or fix existing behavior
6+
# - feature: new functionality
7+
# - known-issue: problems that we are aware of in a given version
8+
# - security: impacts on the security of a product or a user’s deployment.
9+
# - upgrade: important information for someone upgrading from a prior version
10+
# - other: does not fit into any of the other categories
11+
kind: bug-fix
12+
13+
# Change summary; a 80ish characters long description of the change.
14+
summary: Fix panic in azure module when all configured resources match no Azure resources
15+
16+
# Long description; in case the summary is not enough to describe the change
17+
# this field accommodate a description without length limits.
18+
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
19+
#description:
20+
21+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
22+
component: metricbeat
23+
24+
# PR URL; optional; the PR number that added the changeset.
25+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
26+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
27+
# Please provide it if you are adding a fragment for a different PR.
28+
pr: https://github.com/elastic/beats/pull/50498
29+
30+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
31+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
32+
#issue: https://github.com/owner/repo/1234

x-pack/metricbeat/module/azure/client_batch.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,19 @@ func (client *BatchClient) InitResources(fn concurrentMapResourceMetrics) error
137137
fn(client, resourceList, resourceConfig, &wg)
138138
client.Log.Infof("Finished collection with %d metric definitions", len(resourceList))
139139
}
140-
go func() {
141-
wg.Wait() // Wait for all the resource collection goroutines to finish
142-
// Once all the goroutines are done, close the channels
143-
client.Log.Debug("All collections finished. Closing channels ")
144-
close(client.ResourceConfigurations.MetricDefinitionsChan)
145-
close(client.ResourceConfigurations.ErrorChan)
146-
}()
140+
// Only launch the closer goroutine if at least one resource config produced
141+
// results and created the channels. When every config returns an empty
142+
// list the channels stay nil and the consumer will surface the
143+
// "no resources were found" error via its own nil-check.
144+
if client.ResourceConfigurations.MetricDefinitionsChan != nil && client.ResourceConfigurations.ErrorChan != nil {
145+
go func() {
146+
wg.Wait() // Wait for all the resource collection goroutines to finish
147+
// Once all the goroutines are done, close the channels
148+
client.Log.Debug("All collections finished. Closing channels ")
149+
close(client.ResourceConfigurations.MetricDefinitionsChan)
150+
close(client.ResourceConfigurations.ErrorChan)
151+
}()
152+
}
147153
return nil
148154
}
149155

x-pack/metricbeat/module/azure/client_batch_test.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,21 @@ func TestInitResourcesForBatch(t *testing.T) {
4747
assert.Error(t, err, "failed to retrieve resources: invalid resource query")
4848
m.AssertExpectations(t)
4949
})
50+
t.Run("does not panic when all resource configs return an empty list", func(t *testing.T) {
51+
client := NewMockBatchClient(logger)
52+
client.Config = resourceQueryConfig
53+
m := &MockService{}
54+
// empty list + nil error: the "no resources of this type exist" path
55+
m.On("GetResourceDefinitions", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return([]*armresources.GenericResourceExpanded{}, nil)
56+
client.AzureMonitorService = m
57+
58+
err := client.InitResources(mockConcurrentMapResourceMetrics)
59+
require.NoError(t, err)
60+
61+
// The closer goroutine launched by InitResources runs asynchronously and
62+
// will panic on close(nil) if the channels were never initialized.
63+
time.Sleep(100 * time.Millisecond)
64+
})
5065
}
5166

5267
func TestGetMetricsInBatch(t *testing.T) {
@@ -99,7 +114,7 @@ func TestGetMetricsInBatch(t *testing.T) {
99114
mr := MockReporterV2{}
100115
mr.On("Error", mock.Anything).Return(true)
101116
results := client.GetMetricsInBatch(groupedMetrics, referenceTime, &mr)
102-
assert.Equal(t, len(results), 0)
117+
assert.Empty(t, results, "expected no metric values when QueryResources returns an error")
103118
m.AssertExpectations(t)
104119
})
105120

@@ -169,12 +184,12 @@ func TestGetMetricsInBatch(t *testing.T) {
169184
mr := MockReporterV2{}
170185

171186
metricValues := client.GetMetricsInBatch(groupedMetrics, referenceTime, &mr)
172-
require.Equal(t, len(metricValues), 1)
173-
require.Equal(t, len(metricValues[0].Values), 1)
187+
require.Len(t, metricValues, 1, "expected exactly one metric value group")
188+
require.Len(t, metricValues[0].Values, 1, "expected exactly one value in the metric group")
174189

175-
assert.Equal(t, *metricValues[0].Values[0].avg, 1.0)
176-
assert.Equal(t, *metricValues[0].Values[0].max, 2.0)
177-
assert.Equal(t, *metricValues[0].Values[0].min, 3.0)
190+
assert.InDelta(t, 1.0, *metricValues[0].Values[0].avg, 0.0001)
191+
assert.InDelta(t, 2.0, *metricValues[0].Values[0].max, 0.0001)
192+
assert.InDelta(t, 3.0, *metricValues[0].Values[0].min, 0.0001)
178193

179194
m.AssertExpectations(t)
180195
})

0 commit comments

Comments
 (0)