Skip to content

Commit 4c9d865

Browse files
authored
Pass context with timeout to FQDN lookup (#4147)
* Pass context with timeout to FQDN lookup * Add temporary gomod replace * Add CHANGELOG fragment * Updating dependency * Update method calls * Extract function * Fallback to OS-provided hostname on FQDN lookup error * Refactoring: reuse * Adding unit tests * Add comment
1 parent 510dfca commit 4c9d865

File tree

5 files changed

+147
-31
lines changed

5 files changed

+147
-31
lines changed
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: Set timeout of 1 minute for FQDN lookups
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: elastic-agent
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/elastic-agent/pull/4147
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

internal/pkg/agent/application/info/agent_metadata.go

+5-19
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ import (
88
"context"
99
"fmt"
1010

11+
"github.com/elastic/elastic-agent/pkg/features"
12+
1113
"runtime"
1214
"strings"
1315

1416
"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
1517
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
1618
"github.com/elastic/elastic-agent/internal/pkg/release"
19+
"github.com/elastic/elastic-agent/internal/pkg/util"
1720
"github.com/elastic/elastic-agent/pkg/core/logger"
18-
"github.com/elastic/elastic-agent/pkg/features"
1921

2022
"github.com/elastic/go-sysinfo"
2123
"github.com/elastic/go-sysinfo/types"
@@ -149,15 +151,7 @@ func (i *AgentInfo) ECSMetadata(l *logger.Logger) (*ECSMeta, error) {
149151
}
150152

151153
info := sysInfo.Info()
152-
hostname := info.Hostname
153-
if features.FQDN() {
154-
fqdn, err := sysInfo.FQDN()
155-
if err != nil {
156-
l.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostname)
157-
} else {
158-
hostname = fqdn
159-
}
160-
}
154+
hostname := util.GetHostName(features.FQDN(), info, sysInfo, l)
161155

162156
return &ECSMeta{
163157
Elastic: &ElasticECSMeta{
@@ -205,15 +199,7 @@ func (i *AgentInfo) ECSMetadataFlatMap(l *logger.Logger) (map[string]interface{}
205199
}
206200

207201
info := sysInfo.Info()
208-
hostname := info.Hostname
209-
if features.FQDN() {
210-
fqdn, err := sysInfo.FQDN()
211-
if err != nil {
212-
l.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostname)
213-
} else {
214-
hostname = fqdn
215-
}
216-
}
202+
hostname := util.GetHostName(features.FQDN(), info, sysInfo, l)
217203

218204
// Agent
219205
meta[agentIDKey] = i.agentID

internal/pkg/composable/providers/host/host.go

+4-12
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import (
1111
"runtime"
1212
"time"
1313

14-
"github.com/elastic/elastic-agent/pkg/features"
15-
"github.com/elastic/go-sysinfo"
16-
1714
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
1815
"github.com/elastic/elastic-agent/internal/pkg/composable"
1916
"github.com/elastic/elastic-agent/internal/pkg/config"
2017
corecomp "github.com/elastic/elastic-agent/internal/pkg/core/composable"
18+
"github.com/elastic/elastic-agent/internal/pkg/util"
2119
"github.com/elastic/elastic-agent/pkg/core/logger"
20+
"github.com/elastic/elastic-agent/pkg/features"
21+
"github.com/elastic/go-sysinfo"
2222
)
2323

2424
const (
@@ -137,15 +137,7 @@ func getHostInfo(log *logger.Logger) func() (map[string]interface{}, error) {
137137
}
138138

139139
info := sysInfo.Info()
140-
name := info.Hostname
141-
if features.FQDN() {
142-
fqdn, err := sysInfo.FQDN()
143-
if err != nil {
144-
log.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), name)
145-
} else {
146-
name = fqdn
147-
}
148-
}
140+
name := util.GetHostName(features.FQDN(), info, sysInfo, log)
149141

150142
return map[string]interface{}{
151143
"id": info.UniqueID,

internal/pkg/util/host.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package util
6+
7+
import (
8+
"context"
9+
"time"
10+
11+
"github.com/elastic/elastic-agent/pkg/core/logger"
12+
"github.com/elastic/go-sysinfo/types"
13+
)
14+
15+
// GetHostName returns the host's FQDN if the FDQN feature flag is enabled; otherwise, it
16+
// returns the OS-provided hostname.
17+
func GetHostName(isFqdnFeatureEnabled bool, hostInfo types.HostInfo, host types.Host, log *logger.Logger) string {
18+
if !isFqdnFeatureEnabled {
19+
return hostInfo.Hostname
20+
}
21+
22+
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
23+
defer cancel()
24+
25+
fqdn, err := host.FQDNWithContext(ctx)
26+
if err != nil {
27+
// If we are unable to lookup the FQDN, we fallback to the OS-provided hostname
28+
log.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostInfo.Hostname)
29+
return hostInfo.Hostname
30+
}
31+
32+
return fqdn
33+
}

internal/pkg/util/host_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
package util
6+
7+
import (
8+
"context"
9+
"errors"
10+
"testing"
11+
12+
"github.com/elastic/elastic-agent-libs/logp"
13+
14+
"github.com/stretchr/testify/require"
15+
16+
"github.com/elastic/go-sysinfo/types"
17+
)
18+
19+
func TestGetHostName(t *testing.T) {
20+
cases := map[string]struct {
21+
fqdnFeatureEnabled bool
22+
hostInfo types.HostInfo
23+
host types.Host
24+
log *logp.Logger
25+
26+
expected string
27+
}{
28+
"fqdn_feature_disabled": {
29+
fqdnFeatureEnabled: false,
30+
hostInfo: types.HostInfo{Hostname: "foobar"},
31+
expected: "foobar",
32+
},
33+
"fqdn_lookup_fails": {
34+
fqdnFeatureEnabled: true,
35+
hostInfo: types.HostInfo{Hostname: "foobar"},
36+
host: &mockHost{
37+
fqdn: "",
38+
fqdnErr: errors.New("fqdn lookup failed while testing"),
39+
},
40+
log: logp.NewLogger("testing"),
41+
expected: "foobar",
42+
},
43+
"fqdn_lookup_succeeds": {
44+
fqdnFeatureEnabled: true,
45+
hostInfo: types.HostInfo{Hostname: "foobar"},
46+
host: &mockHost{
47+
fqdn: "qux",
48+
fqdnErr: nil,
49+
},
50+
expected: "qux",
51+
},
52+
}
53+
54+
for name, test := range cases {
55+
t.Run(name, func(t *testing.T) {
56+
hostname := GetHostName(test.fqdnFeatureEnabled, test.hostInfo, test.host, test.log)
57+
require.Equal(t, test.expected, hostname)
58+
})
59+
}
60+
}
61+
62+
type mockHost struct {
63+
fqdn string
64+
fqdnErr error
65+
}
66+
67+
func (m *mockHost) CPUTime() (types.CPUTimes, error) { return types.CPUTimes{}, nil }
68+
func (m *mockHost) Info() types.HostInfo { return types.HostInfo{} }
69+
func (m *mockHost) Memory() (*types.HostMemoryInfo, error) { return nil, nil }
70+
func (m *mockHost) FQDNWithContext(ctx context.Context) (string, error) {
71+
return m.fqdn, m.fqdnErr
72+
}
73+
func (m *mockHost) FQDN() (string, error) { return m.FQDNWithContext(context.Background()) }

0 commit comments

Comments
 (0)