Skip to content

Commit

Permalink
Pass context with timeout to FQDN lookup (#4147) (#4263)
Browse files Browse the repository at this point in the history
* 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

(cherry picked from commit 4c9d865)

Co-authored-by: Shaunak Kashyap <[email protected]>
Co-authored-by: Pierre HILBERT <[email protected]>
  • Loading branch information
3 people authored Feb 15, 2024
1 parent f2bb27d commit d2c0b87
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 31 deletions.
32 changes: 32 additions & 0 deletions changelog/fragments/1706213166-fqdn-ctx.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Kind can be one of:
# - breaking-change: a change to previously-documented behavior
# - deprecation: functionality that is being removed in a later release
# - bug-fix: fixes a problem in a previous version
# - enhancement: extends functionality but does not break or fix existing behavior
# - feature: new functionality
# - known-issue: problems that we are aware of in a given version
# - security: impacts on the security of a product or a user’s deployment.
# - upgrade: important information for someone upgrading from a prior version
# - other: does not fit into any of the other categories
kind: bug-fix

# Change summary; a 80ish characters long description of the change.
summary: Set timeout of 1 minute for FQDN lookups

# Long description; in case the summary is not enough to describe the change
# this field accommodate a description without length limits.
# NOTE: This field will be rendered only for breaking-change and known-issue kinds at the moment.
#description:

# Affected component; usually one of "elastic-agent", "fleet-server", "filebeat", "metricbeat", "auditbeat", "all", etc.
component: elastic-agent

# PR URL; optional; the PR number that added the changeset.
# If not present is automatically filled by the tooling finding the PR where this changelog fragment has been added.
# NOTE: the tooling supports backports, so it's able to fill the original PR number instead of the backport PR number.
# Please provide it if you are adding a fragment for a different PR.
pr: https://github.com/elastic/elastic-agent/pull/4147

# Issue URL; optional; the GitHub issue related to this changeset (either closes or is part of).
# If not present is automatically filled by the tooling with the issue linked to the PR number.
#issue: https://github.com/owner/repo/1234
24 changes: 5 additions & 19 deletions internal/pkg/agent/application/info/agent_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import (
"context"
"fmt"

"github.com/elastic/elastic-agent/pkg/features"

"runtime"
"strings"

"github.com/elastic/elastic-agent/internal/pkg/agent/application/paths"
"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
"github.com/elastic/elastic-agent/internal/pkg/release"
"github.com/elastic/elastic-agent/internal/pkg/util"
"github.com/elastic/elastic-agent/pkg/core/logger"
"github.com/elastic/elastic-agent/pkg/features"

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

info := sysInfo.Info()
hostname := info.Hostname
if features.FQDN() {
fqdn, err := sysInfo.FQDN()
if err != nil {
l.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostname)
} else {
hostname = fqdn
}
}
hostname := util.GetHostName(features.FQDN(), info, sysInfo, l)

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

info := sysInfo.Info()
hostname := info.Hostname
if features.FQDN() {
fqdn, err := sysInfo.FQDN()
if err != nil {
l.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostname)
} else {
hostname = fqdn
}
}
hostname := util.GetHostName(features.FQDN(), info, sysInfo, l)

// Agent
meta[agentIDKey] = i.agentID
Expand Down
16 changes: 4 additions & 12 deletions internal/pkg/composable/providers/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import (
"runtime"
"time"

"github.com/elastic/elastic-agent/pkg/features"
"github.com/elastic/go-sysinfo"

"github.com/elastic/elastic-agent/internal/pkg/agent/errors"
"github.com/elastic/elastic-agent/internal/pkg/composable"
"github.com/elastic/elastic-agent/internal/pkg/config"
corecomp "github.com/elastic/elastic-agent/internal/pkg/core/composable"
"github.com/elastic/elastic-agent/internal/pkg/util"
"github.com/elastic/elastic-agent/pkg/core/logger"
"github.com/elastic/elastic-agent/pkg/features"
"github.com/elastic/go-sysinfo"
)

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

info := sysInfo.Info()
name := info.Hostname
if features.FQDN() {
fqdn, err := sysInfo.FQDN()
if err != nil {
log.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), name)
} else {
name = fqdn
}
}
name := util.GetHostName(features.FQDN(), info, sysInfo, log)

return map[string]interface{}{
"id": info.UniqueID,
Expand Down
33 changes: 33 additions & 0 deletions internal/pkg/util/host.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package util

import (
"context"
"time"

"github.com/elastic/elastic-agent/pkg/core/logger"
"github.com/elastic/go-sysinfo/types"
)

// GetHostName returns the host's FQDN if the FDQN feature flag is enabled; otherwise, it
// returns the OS-provided hostname.
func GetHostName(isFqdnFeatureEnabled bool, hostInfo types.HostInfo, host types.Host, log *logger.Logger) string {
if !isFqdnFeatureEnabled {
return hostInfo.Hostname
}

ctx, cancel := context.WithTimeout(context.Background(), 1*time.Minute)
defer cancel()

fqdn, err := host.FQDNWithContext(ctx)
if err != nil {
// If we are unable to lookup the FQDN, we fallback to the OS-provided hostname
log.Debugf("unable to lookup FQDN: %s, using hostname = %s", err.Error(), hostInfo.Hostname)
return hostInfo.Hostname
}

return fqdn
}
73 changes: 73 additions & 0 deletions internal/pkg/util/host_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package util

import (
"context"
"errors"
"testing"

"github.com/elastic/elastic-agent-libs/logp"

"github.com/stretchr/testify/require"

"github.com/elastic/go-sysinfo/types"
)

func TestGetHostName(t *testing.T) {
cases := map[string]struct {
fqdnFeatureEnabled bool
hostInfo types.HostInfo
host types.Host
log *logp.Logger

expected string
}{
"fqdn_feature_disabled": {
fqdnFeatureEnabled: false,
hostInfo: types.HostInfo{Hostname: "foobar"},
expected: "foobar",
},
"fqdn_lookup_fails": {
fqdnFeatureEnabled: true,
hostInfo: types.HostInfo{Hostname: "foobar"},
host: &mockHost{
fqdn: "",
fqdnErr: errors.New("fqdn lookup failed while testing"),
},
log: logp.NewLogger("testing"),
expected: "foobar",
},
"fqdn_lookup_succeeds": {
fqdnFeatureEnabled: true,
hostInfo: types.HostInfo{Hostname: "foobar"},
host: &mockHost{
fqdn: "qux",
fqdnErr: nil,
},
expected: "qux",
},
}

for name, test := range cases {
t.Run(name, func(t *testing.T) {
hostname := GetHostName(test.fqdnFeatureEnabled, test.hostInfo, test.host, test.log)
require.Equal(t, test.expected, hostname)
})
}
}

type mockHost struct {
fqdn string
fqdnErr error
}

func (m *mockHost) CPUTime() (types.CPUTimes, error) { return types.CPUTimes{}, nil }
func (m *mockHost) Info() types.HostInfo { return types.HostInfo{} }
func (m *mockHost) Memory() (*types.HostMemoryInfo, error) { return nil, nil }
func (m *mockHost) FQDNWithContext(ctx context.Context) (string, error) {
return m.fqdn, m.fqdnErr
}
func (m *mockHost) FQDN() (string, error) { return m.FQDNWithContext(context.Background()) }

0 comments on commit d2c0b87

Please sign in to comment.