Skip to content

Commit 723bfa9

Browse files
dloucasfxdmitryax
andauthored
[chore] gopsutilenv module (#40132)
#### Description Extracts and improves the gopsutilenv logic from hostmetrics receiver and move it to its own module This will be used by hostmetrics , signalfx exporter and/or any components that uses gopsutil with non-standard root fs Part of #40110 #### Testing expanded the unit tests Signed-off-by: Dani Louca <[email protected]> Co-authored-by: Dmitry Anoshin <[email protected]>
1 parent 75bf3d5 commit 723bfa9

File tree

10 files changed

+296
-0
lines changed

10 files changed

+296
-0
lines changed

internal/gopsutilenv/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
include ../../Makefile.Common

internal/gopsutilenv/go.mod

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv
2+
3+
go 1.23.0
4+
5+
require (
6+
github.com/shirou/gopsutil/v4 v4.25.4
7+
github.com/stretchr/testify v1.10.0
8+
)
9+
10+
require (
11+
github.com/davecgh/go-spew v1.1.1 // indirect
12+
github.com/pmezard/go-difflib v1.0.0 // indirect
13+
gopkg.in/yaml.v3 v3.0.1 // indirect
14+
)

internal/gopsutilenv/go.sum

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build linux
5+
6+
package gopsutilenv // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv"
7+
8+
import (
9+
"context"
10+
"fmt"
11+
"os"
12+
"path/filepath"
13+
14+
"github.com/shirou/gopsutil/v4/common"
15+
)
16+
17+
var gopsutilEnvVars = map[common.EnvKeyType]string{
18+
common.HostProcEnvKey: "/proc",
19+
common.HostSysEnvKey: "/sys",
20+
common.HostEtcEnvKey: "/etc",
21+
common.HostVarEnvKey: "/var",
22+
common.HostRunEnvKey: "/run",
23+
common.HostDevEnvKey: "/dev",
24+
}
25+
26+
// This exists to validate that different components that use this module do not
27+
// have inconsistent root_path configurations. The root_path is passed down to gopsutil
28+
// through context, so it must be consistent across the process.
29+
var globalRootPath string
30+
31+
func ValidateRootPath(rootPath string) error {
32+
if rootPath == "" || rootPath == "/" {
33+
return nil
34+
}
35+
36+
if globalRootPath != "" && rootPath != globalRootPath {
37+
return fmt.Errorf("inconsistent root_path configuration detected among components: `%s` != `%s`", globalRootPath, rootPath)
38+
}
39+
globalRootPath = rootPath
40+
41+
if _, err := os.Stat(rootPath); err != nil {
42+
return fmt.Errorf("invalid root_path: %w", err)
43+
}
44+
45+
return nil
46+
}
47+
48+
func SetGoPsutilEnvVars(rootPath string) common.EnvMap {
49+
m := common.EnvMap{}
50+
if rootPath == "" || rootPath == "/" {
51+
return m
52+
}
53+
54+
for envVarKey, defaultValue := range gopsutilEnvVars {
55+
_, ok := os.LookupEnv(string(envVarKey))
56+
if ok {
57+
continue // don't override if existing env var is set
58+
}
59+
m[envVarKey] = filepath.Join(rootPath, defaultValue)
60+
}
61+
return m
62+
}
63+
64+
// SetGlobalRootPath mainly used for unit tests
65+
func SetGlobalRootPath(rootPath string) {
66+
globalRootPath = rootPath
67+
}
68+
69+
// copied from gopsutil:
70+
// GetEnvWithContext retrieves the environment variable key. If it does not exist it returns the default.
71+
// The context may optionally contain a map superseding os.EnvKey.
72+
func GetEnvWithContext(ctx context.Context, key string, dfault string, combineWith ...string) string {
73+
var value string
74+
if env, ok := ctx.Value(common.EnvKey).(common.EnvMap); ok {
75+
value = env[common.EnvKeyType(key)]
76+
}
77+
if value == "" {
78+
value = os.Getenv(key)
79+
}
80+
if value == "" {
81+
value = dfault
82+
}
83+
84+
return combine(value, combineWith)
85+
}
86+
87+
func combine(value string, combineWith []string) string {
88+
switch len(combineWith) {
89+
case 0:
90+
return value
91+
case 1:
92+
return filepath.Join(value, combineWith[0])
93+
default:
94+
all := make([]string, len(combineWith)+1)
95+
all[0] = value
96+
copy(all[1:], combineWith)
97+
return filepath.Join(all...)
98+
}
99+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build linux
5+
6+
package gopsutilenv
7+
8+
import (
9+
"context"
10+
"testing"
11+
12+
"github.com/shirou/gopsutil/v4/common"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRootPaths(t *testing.T) {
17+
tests := []struct {
18+
testName string
19+
rootPath string
20+
errMsg string
21+
}{
22+
{
23+
testName: "valid path",
24+
rootPath: "testdata",
25+
},
26+
{
27+
testName: "empty path",
28+
rootPath: "",
29+
},
30+
{
31+
testName: "root path",
32+
rootPath: "/",
33+
},
34+
{
35+
testName: "invalid path",
36+
rootPath: "invalidpath",
37+
errMsg: "invalid root_path: stat invalidpath: no such file or directory",
38+
},
39+
}
40+
for _, test := range tests {
41+
t.Run(test.testName, func(t *testing.T) {
42+
err := ValidateRootPath(test.rootPath)
43+
if test.errMsg != "" {
44+
assert.EqualError(t, err, test.errMsg)
45+
} else {
46+
assert.NoError(t, err)
47+
}
48+
t.Cleanup(func() { SetGlobalRootPath("") })
49+
})
50+
}
51+
}
52+
53+
func TestInconsistentRootPaths(t *testing.T) {
54+
SetGlobalRootPath("foo")
55+
err := ValidateRootPath("testdata")
56+
assert.EqualError(t, err, "inconsistent root_path configuration detected among components: `foo` != `testdata`")
57+
58+
t.Cleanup(func() { SetGlobalRootPath("") })
59+
}
60+
61+
func TestSetGoPsutilEnvVars(t *testing.T) {
62+
envMap := SetGoPsutilEnvVars("testdata")
63+
expectedEnvMap := common.EnvMap{
64+
common.HostDevEnvKey: "testdata/dev",
65+
common.HostEtcEnvKey: "testdata/etc",
66+
common.HostRunEnvKey: "testdata/run",
67+
common.HostSysEnvKey: "testdata/sys",
68+
common.HostVarEnvKey: "testdata/var",
69+
common.HostProcEnvKey: "testdata/proc",
70+
}
71+
assert.Equal(t, expectedEnvMap, envMap)
72+
ctx := context.WithValue(context.Background(), common.EnvKey, envMap)
73+
assert.Equal(t, "testdata/proc", GetEnvWithContext(ctx, string(common.HostProcEnvKey), "default"))
74+
assert.Equal(t, "testdata/sys", GetEnvWithContext(ctx, string(common.HostSysEnvKey), "default"))
75+
assert.Equal(t, "testdata/etc", GetEnvWithContext(ctx, string(common.HostEtcEnvKey), "default"))
76+
assert.Equal(t, "testdata/var", GetEnvWithContext(ctx, string(common.HostVarEnvKey), "default"))
77+
assert.Equal(t, "testdata/run", GetEnvWithContext(ctx, string(common.HostRunEnvKey), "default"))
78+
assert.Equal(t, "testdata/dev", GetEnvWithContext(ctx, string(common.HostDevEnvKey), "default"))
79+
80+
t.Cleanup(func() { SetGlobalRootPath("") })
81+
}
82+
83+
func TestGetEnvWithContext(t *testing.T) {
84+
envMap := SetGoPsutilEnvVars("testdata")
85+
86+
ctxEnv := context.WithValue(context.Background(), common.EnvKey, envMap)
87+
assert.Equal(t, "testdata/proc", GetEnvWithContext(ctxEnv, string(common.HostProcEnvKey), "default"))
88+
assert.Equal(t, "testdata/sys", GetEnvWithContext(ctxEnv, string(common.HostSysEnvKey), "default"))
89+
assert.Equal(t, "testdata/etc", GetEnvWithContext(ctxEnv, string(common.HostEtcEnvKey), "default"))
90+
assert.Equal(t, "testdata/var", GetEnvWithContext(ctxEnv, string(common.HostVarEnvKey), "default"))
91+
assert.Equal(t, "testdata/run", GetEnvWithContext(ctxEnv, string(common.HostRunEnvKey), "default"))
92+
assert.Equal(t, "testdata/dev", GetEnvWithContext(ctxEnv, string(common.HostDevEnvKey), "default"))
93+
94+
assert.Equal(t, "/default", GetEnvWithContext(context.Background(), string(common.HostProcEnvKey), "/default"))
95+
assert.Equal(t, "/default/foo", GetEnvWithContext(context.Background(), string(common.HostProcEnvKey), "/default", "foo"))
96+
assert.Equal(t, "/default/foo/bar", GetEnvWithContext(context.Background(), string(common.HostProcEnvKey), "/default", "foo", "bar"))
97+
98+
t.Cleanup(func() { SetGlobalRootPath("") })
99+
}
100+
101+
func TestSetGoPsutilEnvVarsInvalidRootPath(t *testing.T) {
102+
err := ValidateRootPath("invalidpath")
103+
assert.EqualError(t, err, "invalid root_path: stat invalidpath: no such file or directory")
104+
105+
t.Cleanup(func() { SetGlobalRootPath("") })
106+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build !linux
5+
6+
package gopsutilenv // import "github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv"
7+
8+
import (
9+
"context"
10+
"errors"
11+
12+
"github.com/shirou/gopsutil/v4/common"
13+
)
14+
15+
func ValidateRootPath(rootPath string) error {
16+
if rootPath == "" {
17+
return nil
18+
}
19+
return errors.New("root_path is supported on linux only")
20+
}
21+
22+
func SetGoPsutilEnvVars(_ string) (common.EnvMap, error) {
23+
return common.EnvMap{}, nil
24+
}
25+
26+
func GetEnvWithContext(_ context.Context, _ string, dfault string, _ ...string) string {
27+
return dfault
28+
}
29+
30+
func SetGlobalRootPath(_ string) {
31+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright The OpenTelemetry Authors
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
//go:build !linux
5+
6+
package gopsutilenv
7+
8+
import (
9+
"context"
10+
"testing"
11+
12+
"github.com/shirou/gopsutil/v4/common"
13+
"github.com/stretchr/testify/assert"
14+
)
15+
16+
func TestRootPathNotAllowedOnOS(t *testing.T) {
17+
assert.Error(t, ValidateRootPath("testdata"))
18+
}
19+
20+
func TestRootPathUnset(t *testing.T) {
21+
assert.NoError(t, ValidateRootPath(""))
22+
}
23+
24+
func TestGetEnvWithContext(t *testing.T) {
25+
val := GetEnvWithContext(context.Background(), string(common.HostProcEnvKey), "default")
26+
assert.Equal(t, "default", val)
27+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Ignore everything in this directory
2+
*
3+
# Except this .gitignore file
4+
!.gitignore

internal/tidylist/tidylist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ internal/aws/k8s
198198
internal/aws/xray/testdata/sampleapp
199199
internal/aws/xray/testdata/sampleserver
200200
internal/collectd
201+
internal/gopsutilenv
201202
internal/kubelet
202203
internal/sqlquery
203204
internal/tools

versions.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ module-sets:
143143
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/docker
144144
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics
145145
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/filter
146+
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/gopsutilenv
146147
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/grpcutil
147148
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig
148149
- github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka

0 commit comments

Comments
 (0)