Skip to content

Commit 72c79d2

Browse files
fix: add_cloud_metadata: Do not block on String() (#47058) (#47172)
In `addCloudMetadata`, do not block by waiting for `init()` when calling `String()`, but return `add_cloud_metadata=<uninitialized>`. After the timeout, 3 seconds by default, `add_cloud_metadata` will be logged as previously. I've decided to keep the old log format because this is also dumped during agent diagnostics and I figured the complexity is not significant. We can still go for a simpler approach of not logging any metadata instead. (cherry picked from commit 8879dda) Co-authored-by: Orestis Floros <[email protected]>
1 parent 0d0191d commit 72c79d2

File tree

4 files changed

+113
-9
lines changed

4 files changed

+113
-9
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# REQUIRED
2+
# Kind can be one of:
3+
# - breaking-change: a change to previously-documented behavior
4+
# - deprecation: functionality that is being removed in a later release
5+
# - bug-fix: fixes a problem in a previous version
6+
# - enhancement: extends functionality but does not break or fix existing behavior
7+
# - feature: new functionality
8+
# - known-issue: problems that we are aware of in a given version
9+
# - security: impacts on the security of a product or a user’s deployment.
10+
# - upgrade: important information for someone upgrading from a prior version
11+
# - other: does not fit into any of the other categories
12+
kind: bug-fix
13+
14+
# REQUIRED for all kinds
15+
# Change summary; a 80ish characters long description of the change.
16+
summary: Prevent 3s startup delay when add_cloud_metadata is used with debug logs
17+
18+
# REQUIRED for breaking-change, deprecation, known-issue
19+
# Long description; in case the summary is not enough to describe the change
20+
# this field accommodate a description without length limits.
21+
# description:
22+
23+
# REQUIRED for breaking-change, deprecation, known-issue
24+
# impact:
25+
26+
# REQUIRED for breaking-change, deprecation, known-issue
27+
# action:
28+
29+
# REQUIRED for all kinds
30+
# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
31+
component: all
32+
33+
# AUTOMATED
34+
# OPTIONAL to manually add other PR URLs
35+
# PR URL: A link the PR that added the changeset.
36+
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
37+
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
38+
# Please provide it if you are adding a fragment for a different PR.
39+
# pr: https://github.com/owner/repo/1234
40+
41+
# AUTOMATED
42+
# OPTIONAL to manually add other issue URLs
43+
# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
44+
# If not present is automatically filled by the tooling with the issue linked to the PR number.
45+
# issue: https://github.com/owner/repo/1234

libbeat/processors/add_cloud_metadata/add_cloud_metadata.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ func init() {
4646
type addCloudMetadata struct {
4747
initOnce sync.Once
4848
initData *initData
49+
initDone chan struct{}
4950
metadata mapstr.M
5051
logger *logp.Logger
5152
}
@@ -81,7 +82,8 @@ func New(c *cfg.C, log *logp.Logger) (beat.Processor, error) {
8182
tlsConfig: tlsConfig,
8283
overwrite: config.Overwrite,
8384
},
84-
logger: log.Named("add_cloud_metadata"),
85+
initDone: make(chan struct{}),
86+
logger: log.Named("add_cloud_metadata"),
8587
}
8688

8789
go p.init()
@@ -94,7 +96,8 @@ func (r result) String() string {
9496
}
9597

9698
func (p *addCloudMetadata) init() {
97-
p.initOnce.Do(func() {
99+
p.initOnce.Do(func() { // fetch metadata only once
100+
defer close(p.initDone) // signal that init() completed
98101
result := p.fetchMetadata()
99102
if result == nil {
100103
p.logger.Info("add_cloud_metadata: hosting provider type not detected.")
@@ -125,7 +128,14 @@ func (p *addCloudMetadata) Run(event *beat.Event) (*beat.Event, error) {
125128
}
126129

127130
func (p *addCloudMetadata) String() string {
128-
return "add_cloud_metadata=" + p.getMeta().String()
131+
metadataStr := "<uninitialized>"
132+
select {
133+
case <-p.initDone:
134+
// init() completed
135+
metadataStr = p.getMeta().String()
136+
default:
137+
}
138+
return "add_cloud_metadata=" + metadataStr
129139
}
130140

131141
func (p *addCloudMetadata) addMeta(event *beat.Event, meta mapstr.M) error {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Licensed to Elasticsearch B.V. under one or more contributor
2+
// license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright
4+
// ownership. Elasticsearch B.V. licenses this file to you under
5+
// the Apache License, Version 2.0 (the "License"); you may
6+
// not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package add_cloud_metadata
19+
20+
import (
21+
"testing"
22+
"time"
23+
24+
"github.com/stretchr/testify/assert"
25+
"github.com/stretchr/testify/require"
26+
27+
conf "github.com/elastic/elastic-agent-libs/config"
28+
"github.com/elastic/elastic-agent-libs/logp/logptest"
29+
)
30+
31+
func Test_addCloudMetadata_String(t *testing.T) {
32+
const timeout = 100 * time.Millisecond
33+
cfg := conf.MustNewConfigFrom(map[string]any{
34+
"providers": []string{"openstack"},
35+
"host": "fake:1234",
36+
"timeout": timeout.String(),
37+
})
38+
p, err := New(cfg, logptest.NewTestingLogger(t, ""))
39+
require.NoError(t, err)
40+
assert.Eventually(t, func() bool { return p.String() == "add_cloud_metadata=<uninitialized>" }, timeout, 10*time.Millisecond)
41+
assert.Eventually(t, func() bool { return p.String() == "add_cloud_metadata={}" }, 2*timeout, 10*time.Millisecond)
42+
}

x-pack/filebeat/fbreceiver/receiver_test.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,8 @@ func TestNewReceiver(t *testing.T) {
106106
FilterMessageSnippet("add_host_metadata").
107107
FilterMessageSnippet("add_cloud_metadata").
108108
FilterMessageSnippet("add_docker_metadata").
109-
FilterMessageSnippet("add_kubernetes_metadata").
110-
Len() == 1
111-
assert.True(c, processorsLoaded, "processors not loaded")
109+
FilterMessageSnippet("add_kubernetes_metadata")
110+
assert.Len(t, processorsLoaded.All(), 1, "processors not loaded")
112111
// Check that add_host_metadata works, other processors are not guaranteed to add fields in all environments
113112
return assert.Contains(c, logs["r1"][0].Flatten(), "host.architecture")
114113
}, "failed to check processors loaded")
@@ -117,6 +116,14 @@ func TestNewReceiver(t *testing.T) {
117116
}
118117

119118
func BenchmarkFactory(b *testing.B) {
119+
for _, level := range []zapcore.Level{zapcore.InfoLevel, zapcore.DebugLevel} {
120+
b.Run(level.String(), func(b *testing.B) {
121+
benchmarkFactoryWithLogLevel(b, level)
122+
})
123+
}
124+
}
125+
126+
func benchmarkFactoryWithLogLevel(b *testing.B, level zapcore.Level) {
120127
tmpDir := b.TempDir()
121128

122129
cfg := &Config{
@@ -135,7 +142,7 @@ func BenchmarkFactory(b *testing.B) {
135142
"otelconsumer": map[string]any{},
136143
},
137144
"logging": map[string]any{
138-
"level": "info",
145+
"level": level.String(),
139146
"selectors": []string{
140147
"*",
141148
},
@@ -148,7 +155,7 @@ func BenchmarkFactory(b *testing.B) {
148155
core := zapcore.NewCore(
149156
zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig()),
150157
zapcore.Lock(zapcore.AddSync(&zapLogs)),
151-
zapcore.InfoLevel)
158+
level)
152159

153160
factory := NewFactory()
154161

@@ -480,7 +487,7 @@ func TestConsumeContract(t *testing.T) {
480487

481488
gen := newLogGenerator(t, tmpDir)
482489

483-
os.Setenv("OTELCONSUMER_RECEIVERTEST", "1")
490+
t.Setenv("OTELCONSUMER_RECEIVERTEST", "1")
484491

485492
cfg := &Config{
486493
Beatconfig: map[string]any{

0 commit comments

Comments
 (0)