Skip to content

Commit e9276a7

Browse files
authored
[signalfxexporter] Fix invalid and missing host metadata (#40218)
#### Description Fix invalid and missing host metadata when the collector is deployed in a containerized environment and the host root filesystem is mounted to non-standard location. This PR introduces the `root_path` config option which is used to create gopsutil context using https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/internal/gopsutilenv Ideally this should be merged after #40213 To make sure gopsutilenv detects inconsistency if the root_path value between the hostmetrics receiver and sfx exporter are different #### Testing Unit tests and manual deployment #### Documentation README updated Signed-off-by: Dani Louca <[email protected]>
1 parent 831984b commit e9276a7

File tree

22 files changed

+252
-154
lines changed

22 files changed

+252
-154
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: bug_fix
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
7+
component: signalfxexporter
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Fix invalid and missing host metadata when the collector is deployed in a containerized environment and |
11+
the host root filesystem is mounted to non-standard location.
12+
13+
# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
14+
issues: [40218]
15+
16+
# (Optional) One or more lines of additional information to render under the primary note.
17+
# These lines will be padded with 2 spaces and then inserted directly into the document.
18+
# Use pipe (|) for multiline entries.
19+
subtext: Use the newly introduced `root_path` configuration option to specify the root path of the host filesystem.|
20+
This is required when the host root filesystem is mounted to a non-standard location.
21+
22+
# If your change doesn't affect end users or the exported elements of any package,
23+
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
24+
# Optional: The change log or logs in which this entry should be included.
25+
# e.g. '[user]' or '[user, api]'
26+
# Include 'user' if the change is relevant to end users.
27+
# Include 'api' if there is a change to a library API.
28+
# Default: '[user]'
29+
change_logs: []

cmd/opampsupervisor/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,3 +325,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/stefe
325325
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/stefreceiver => ../../receiver/stefreceiver
326326

327327
replace github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor => ../../processor/deltatocumulativeprocessor
328+
329+
replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv => ../../internal/gopsutilenv

exporter/elasticsearchexporter/integrationtest/go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,3 +348,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/exporter/stefe
348348
replace github.com/open-telemetry/opentelemetry-collector-contrib/receiver/stefreceiver => ../../../receiver/stefreceiver
349349

350350
replace github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor => ../../../processor/deltatocumulativeprocessor
351+
352+
replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv => ../../../internal/gopsutilenv

exporter/signalfxexporter/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ The following configuration options can also be configured:
102102
processor is enabled in the pipeline with one of the cloud provider detectors
103103
or environment variable detector setting a unique value to `host.name` attribute
104104
within your k8s cluster. And keep `override=true` in resourcedetection config.
105+
- `root_path`: Used by the host metadata to identify the root filesystem.
106+
This is needed when running in a containerized environment and the host root
107+
filesystem is not `/`. Example: if the root filesystem is mounted under `/hostfs`, set
108+
`root_path` to `/hostfs`.
109+
Note: all components using `root_path` must have the same value; this currently applies
110+
to hostmetrics receiver and signalfx exporter.
105111
- `exclude_properties`: A list of property filters to limit dimension update content.
106112
Property filters can contain any number of the following fields, supporting (negated)
107113
string literals, re2 `/regex/`, and [glob](https://github.com/gobwas/glob) syntax values:

exporter/signalfxexporter/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/correlation"
2121
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation"
2222
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation/dpfilters"
23+
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv"
2324
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk"
2425
)
2526

@@ -114,6 +115,9 @@ type Config struct {
114115
// And keep `override=true` in resourcedetection config.
115116
SyncHostMetadata bool `mapstructure:"sync_host_metadata"`
116117

118+
// RootPath is the host's root directory used when syncing metadata; applies to linux only.
119+
RootPath string `mapstructure:"root_path"`
120+
117121
// ExcludeMetrics defines dpfilter.MetricFilters that will determine metrics to be
118122
// excluded from sending to SignalFx backend. If translations enabled with
119123
// TranslationRules options, the exclusion will be applied on translated metrics.
@@ -224,6 +228,10 @@ func (cfg *Config) Validate() error {
224228
return errors.New(`cannot have a negative "timeout"`)
225229
}
226230

231+
if err := gopsutilenv.ValidateRootPath(cfg.RootPath); err != nil {
232+
return fmt.Errorf("invalid root_path: %w", err)
233+
}
234+
227235
return nil
228236
}
229237

exporter/signalfxexporter/exporter.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/dimensions"
2222
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/hostmetadata"
2323
"github.com/open-telemetry/opentelemetry-collector-contrib/exporter/signalfxexporter/internal/translation"
24+
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv"
2425
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk"
2526
metadata "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata"
2627
)
@@ -157,7 +158,8 @@ func (se *signalfxExporter) start(ctx context.Context, host component.Host) (err
157158

158159
var hms *hostmetadata.Syncer
159160
if se.config.SyncHostMetadata {
160-
hms = hostmetadata.NewSyncer(se.logger, dimClient)
161+
envMap := gopsutilenv.SetGoPsutilEnvVars(se.config.RootPath)
162+
hms = hostmetadata.NewSyncer(se.logger, dimClient, envMap)
161163
}
162164
se.dimClient = dimClient
163165
se.pushMetricsData = dpClient.pushMetricsData

exporter/signalfxexporter/go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/gogo/protobuf v1.3.2
99
github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.127.0
1010
github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.127.0
11+
github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv v0.0.0-00010101000000-000000000000
1112
github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk v0.127.0
1213
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchperresourceattr v0.127.0
1314
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/experimentalmetricmetadata v0.127.0
@@ -35,6 +36,7 @@ require (
3536
go.uber.org/goleak v1.3.0
3637
go.uber.org/multierr v1.11.0
3738
go.uber.org/zap v1.27.0
39+
golang.org/x/net v0.40.0
3840
golang.org/x/sys v0.33.0
3941
gopkg.in/yaml.v3 v3.0.1
4042
)
@@ -102,7 +104,6 @@ require (
102104
go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect
103105
go.opentelemetry.io/otel/trace v1.36.0 // indirect
104106
golang.org/x/crypto v0.38.0 // indirect
105-
golang.org/x/net v0.40.0 // indirect
106107
golang.org/x/text v0.25.0 // indirect
107108
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
108109
google.golang.org/grpc v1.72.2 // indirect
@@ -136,3 +137,5 @@ replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil
136137
replace github.com/go-openapi/spec v0.20.5 => github.com/go-openapi/spec v0.20.6
137138

138139
replace github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden => ../../pkg/golden
140+
141+
replace github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv => ../../internal/gopsutilenv

exporter/signalfxexporter/internal/hostmetadata/host.go

Lines changed: 9 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ package hostmetadata // import "github.com/open-telemetry/opentelemetry-collecto
88

99
import (
1010
"context"
11-
"errors"
12-
"os"
13-
"path/filepath"
14-
"regexp"
1511
"strconv"
1612
"time"
1713

@@ -20,23 +16,14 @@ import (
2016
"github.com/shirou/gopsutil/v4/mem"
2117
)
2218

23-
// etcPath is the path to host etc and can be set using the env var "HOST_ETC"
24-
// this is to maintain consistency with gopsutil
25-
var etcPath = func() string {
26-
if etcPath := os.Getenv("HOST_ETC"); etcPath != "" {
27-
return etcPath
28-
}
29-
return "/etc"
30-
}
31-
3219
const cpuStatsTimeout = 10 * time.Second
3320

3421
// Map library functions to unexported package variables for testing purposes.
3522
var (
3623
cpuInfo = cpu.InfoWithContext
3724
cpuCounts = cpu.CountsWithContext
38-
memVirtualMemory = mem.VirtualMemory
39-
hostInfo = host.Info
25+
memVirtualMemory = mem.VirtualMemoryWithContext
26+
hostInfo = host.InfoWithContext
4027
)
4128

4229
// hostCPU information about the host
@@ -62,14 +49,14 @@ func (c *hostCPU) toStringMap() map[string]string {
6249
}
6350

6451
// getCPU - adds information about the host cpu to the supplied map
65-
func getCPU() (info *hostCPU, err error) {
52+
func getCPU(ctx context.Context) (info *hostCPU, err error) {
6653
info = &hostCPU{}
6754

6855
// get physical cpu stats
6956
var cpus []cpu.InfoStat
7057

7158
// On Windows this can sometimes take longer than the default timeout (10 seconds).
72-
ctx, cancel := context.WithTimeout(context.Background(), cpuStatsTimeout)
59+
ctx, cancel := context.WithTimeout(ctx, cpuStatsTimeout)
7360
defer cancel()
7461

7562
cpus, err = cpuInfo(ctx)
@@ -119,9 +106,9 @@ func (o *hostOS) toStringMap() map[string]string {
119106
}
120107

121108
// getOS returns a struct with information about the host os
122-
func getOS() (info *hostOS, err error) {
109+
func getOS(ctx context.Context) (info *hostOS, err error) {
123110
info = &hostOS{}
124-
hInfo, err := hostInfo()
111+
hInfo, err := hostInfo(ctx)
125112
if err != nil {
126113
return info, err
127114
}
@@ -130,31 +117,10 @@ func getOS() (info *hostOS, err error) {
130117
info.HostKernelName = hInfo.OS
131118
// in gopsutil KernelVersion returns what we would expect for Kernel Release
132119
info.HostKernelRelease = hInfo.KernelVersion
133-
err = fillPlatformSpecificOSData(info)
120+
err = fillPlatformSpecificOSData(ctx, info)
134121
return info, err
135122
}
136123

137-
// getLinuxVersion - adds information about the host linux version to the supplied map
138-
func getLinuxVersion() (string, error) {
139-
etc := etcPath()
140-
if value, err := getStringFromFile(`DISTRIB_DESCRIPTION="(.*)"`, filepath.Join(etc, "lsb-release")); err == nil {
141-
return value, nil
142-
}
143-
if value, err := getStringFromFile(`PRETTY_NAME="(.*)"`, filepath.Join(etc, "os-release")); err == nil {
144-
return value, nil
145-
}
146-
if value, err := os.ReadFile(filepath.Join(etc, "centos-release")); err == nil {
147-
return string(value), nil
148-
}
149-
if value, err := os.ReadFile(filepath.Join(etc, "redhat-release")); err == nil {
150-
return string(value), nil
151-
}
152-
if value, err := os.ReadFile(filepath.Join(etc, "system-release")); err == nil {
153-
return string(value), nil
154-
}
155-
return "", errors.New("unable to find linux version")
156-
}
157-
158124
// Memory stores memory collected from the host
159125
type Memory struct {
160126
Total int
@@ -169,27 +135,15 @@ func (m *Memory) toStringMap() map[string]string {
169135
}
170136

171137
// getMemory returns the amount of memory on the host as datatype.USize
172-
func getMemory() (*Memory, error) {
138+
func getMemory(ctx context.Context) (*Memory, error) {
173139
m := &Memory{}
174-
memoryStat, err := memVirtualMemory()
140+
memoryStat, err := memVirtualMemory(ctx)
175141
if err == nil {
176142
m.Total = int(memoryStat.Total)
177143
}
178144
return m, err
179145
}
180146

181-
func getStringFromFile(pattern string, path string) (string, error) {
182-
var err error
183-
var file []byte
184-
reg := regexp.MustCompile(pattern)
185-
if file, err = os.ReadFile(path); err == nil {
186-
if match := reg.FindSubmatch(file); len(match) > 1 {
187-
return string(match[1]), nil
188-
}
189-
}
190-
return "", err
191-
}
192-
193147
func bytesToKilobytes(b int) int {
194148
return b / 1024
195149
}

exporter/signalfxexporter/internal/hostmetadata/host_linux.go

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,22 @@ package hostmetadata // import "github.com/open-telemetry/opentelemetry-collecto
1010

1111
import (
1212
"bytes"
13+
"errors"
14+
"os"
15+
"path/filepath"
16+
"regexp"
1317

18+
"golang.org/x/net/context"
1419
"golang.org/x/sys/unix"
20+
21+
"github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv"
1522
)
1623

1724
// syscallUname maps to the golib system call, but can be modified for testing
1825
var syscallUname = unix.Uname
1926

20-
func fillPlatformSpecificOSData(info *hostOS) error {
21-
info.HostLinuxVersion, _ = getLinuxVersion()
27+
func fillPlatformSpecificOSData(ctx context.Context, info *hostOS) error {
28+
info.HostLinuxVersion, _ = getLinuxVersion(ctx)
2229

2330
uname := &unix.Utsname{}
2431
if err := syscallUname(uname); err != nil {
@@ -43,3 +50,36 @@ func fillPlatformSpecificCPUData(info *hostCPU) error {
4350
info.HostProcessor = info.HostMachine
4451
return nil
4552
}
53+
54+
// getLinuxVersion - adds information about the host linux version to the supplied map
55+
func getLinuxVersion(ctx context.Context) (string, error) {
56+
etc := gopsutilenv.GetEnvWithContext(ctx, "HOST_ETC", "/etc")
57+
if value, err := getStringFromFile(`DISTRIB_DESCRIPTION="(.*)"`, filepath.Join(etc, "lsb-release")); err == nil {
58+
return value, nil
59+
}
60+
if value, err := getStringFromFile(`PRETTY_NAME="(.*)"`, filepath.Join(etc, "os-release")); err == nil {
61+
return value, nil
62+
}
63+
if value, err := os.ReadFile(filepath.Join(etc, "centos-release")); err == nil {
64+
return string(value), nil
65+
}
66+
if value, err := os.ReadFile(filepath.Join(etc, "redhat-release")); err == nil {
67+
return string(value), nil
68+
}
69+
if value, err := os.ReadFile(filepath.Join(etc, "system-release")); err == nil {
70+
return string(value), nil
71+
}
72+
return "", errors.New("unable to find linux version")
73+
}
74+
75+
func getStringFromFile(pattern string, path string) (string, error) {
76+
var err error
77+
var file []byte
78+
reg := regexp.MustCompile(pattern)
79+
if file, err = os.ReadFile(path); err == nil {
80+
if match := reg.FindSubmatch(file); len(match) > 1 {
81+
return string(match[1]), nil
82+
}
83+
}
84+
return "", err
85+
}

0 commit comments

Comments
 (0)