Skip to content

Commit 015eb68

Browse files
authored
enable default collector (#1044)
1 parent 8a05e9a commit 015eb68

File tree

9 files changed

+187
-148
lines changed

9 files changed

+187
-148
lines changed

Diff for: internal/config/config.go

+64
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ import (
1616
"slices"
1717
"strconv"
1818
"strings"
19+
"time"
20+
21+
"github.com/nginx/agent/v3/internal/datasource/file"
1922

2023
uuidLibrary "github.com/nginx/agent/v3/pkg/id"
2124
selfsignedcerts "github.com/nginx/agent/v3/pkg/tls"
@@ -115,6 +118,8 @@ func ResolveConfig() (*Config, error) {
115118
Labels: resolveLabels(),
116119
}
117120

121+
checkCollectorConfiguration(collector, config)
122+
118123
slog.Debug("Agent config", "config", config)
119124
slog.Info("Enabled features", "features", config.Features)
120125
slog.Info("Excluded files from being watched for file changes", "exclude_files",
@@ -123,6 +128,60 @@ func ResolveConfig() (*Config, error) {
123128
return config, nil
124129
}
125130

131+
func checkCollectorConfiguration(collector *Collector, config *Config) {
132+
if isOTelExporterConfigured(collector) && config.IsGrpcClientConfigured() && config.IsAuthConfigured() &&
133+
config.IsTLSConfigured() {
134+
slog.Info("No collector configuration found in NGINX Agent config, command server configuration found." +
135+
"Using default collector configuration")
136+
defaultCollector(collector, config)
137+
}
138+
}
139+
140+
func defaultCollector(collector *Collector, config *Config) {
141+
token := config.Command.Auth.Token
142+
if config.Command.Auth.TokenPath != "" {
143+
slog.Debug("Reading token from file", "path", config.Command.Auth.TokenPath)
144+
pathToken, err := file.ReadFromFile(config.Command.Auth.TokenPath)
145+
if err != nil {
146+
slog.Error("Error adding token to default collector, "+
147+
"default collector configuration not started", "error", err)
148+
149+
return
150+
}
151+
token = pathToken
152+
}
153+
154+
collector.Receivers.HostMetrics = &HostMetrics{
155+
Scrapers: &HostMetricsScrapers{
156+
CPU: &CPUScraper{},
157+
Disk: &DiskScraper{},
158+
Filesystem: &FilesystemScraper{},
159+
Memory: &MemoryScraper{},
160+
Network: nil,
161+
},
162+
CollectionInterval: 1 * time.Minute,
163+
InitialDelay: 1 * time.Second,
164+
}
165+
166+
collector.Exporters.OtlpExporters = append(collector.Exporters.OtlpExporters, OtlpExporter{
167+
Server: config.Command.Server,
168+
TLS: config.Command.TLS,
169+
Compression: "",
170+
Authenticator: "headers_setter",
171+
})
172+
173+
header := []Header{
174+
{
175+
Action: "insert",
176+
Key: "authorization",
177+
Value: token,
178+
},
179+
}
180+
collector.Extensions.HeadersSetter = &HeadersSetter{
181+
Headers: header,
182+
}
183+
}
184+
126185
func setVersion(version, commit string) {
127186
RootCommand.Version = version + "-" + commit
128187
viperInstance.SetDefault(VersionKey, version)
@@ -911,3 +970,8 @@ func resolveMapStructure(key string, object any) error {
911970

912971
return nil
913972
}
973+
974+
func isOTelExporterConfigured(collector *Collector) bool {
975+
return len(collector.Exporters.OtlpExporters) == 0 && collector.Exporters.PrometheusExporter == nil &&
976+
collector.Exporters.Debug == nil
977+
}

Diff for: internal/config/types.go

+17
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,23 @@ func (c *Config) IsDirectoryAllowed(directory string) bool {
344344
return isAllowedDir(directory, c.AllowedDirectories)
345345
}
346346

347+
func (c *Config) IsGrpcClientConfigured() bool {
348+
return c.Command != nil &&
349+
c.Command.Server != nil &&
350+
c.Command.Server.Host != "" &&
351+
c.Command.Server.Port != 0 &&
352+
c.Command.Server.Type == Grpc
353+
}
354+
355+
func (c *Config) IsAuthConfigured() bool {
356+
return c.Command.Auth != nil &&
357+
(c.Command.Auth.Token != "" || c.Command.Auth.TokenPath != "")
358+
}
359+
360+
func (c *Config) IsTLSConfigured() bool {
361+
return c.Command.TLS != nil
362+
}
363+
347364
func (c *Config) IsFeatureEnabled(feature string) bool {
348365
for _, enabledFeature := range c.Features {
349366
if enabledFeature == feature {

Diff for: internal/datasource/file/file.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright (c) F5, Inc.
2+
//
3+
// This source code is licensed under the Apache License, Version 2.0 license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package file
7+
8+
import (
9+
"bytes"
10+
"errors"
11+
"fmt"
12+
"os"
13+
)
14+
15+
// ReadFromFile reads the contents from a file, trims the white space, trims newlines
16+
// then returns the contents as a string
17+
func ReadFromFile(path string) (string, error) {
18+
if path == "" {
19+
return "", errors.New("failed to read file since file path is empty")
20+
}
21+
22+
var content string
23+
contentBytes, err := os.ReadFile(path)
24+
if err != nil {
25+
return "", fmt.Errorf("unable to read from file: %w", err)
26+
}
27+
28+
contentBytes = bytes.TrimSpace(contentBytes)
29+
contentBytes = bytes.TrimRight(contentBytes, "\n")
30+
content = string(contentBytes)
31+
32+
if content == "" {
33+
return "", errors.New("failed to read from file, file is empty")
34+
}
35+
36+
return content, nil
37+
}

Diff for: internal/datasource/file/file_test.go

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Copyright (c) F5, Inc.
2+
//
3+
// This source code is licensed under the Apache License, Version 2.0 license found in the
4+
// LICENSE file in the root directory of this source tree.
5+
6+
package file
7+
8+
import (
9+
"os"
10+
"testing"
11+
12+
"github.com/nginx/agent/v3/test/helpers"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
func Test_RetrieveTokenFromFile(t *testing.T) {
18+
dir := t.TempDir()
19+
tokenFile := helpers.CreateFileWithErrorCheck(t, dir, "test-tkn")
20+
defer helpers.RemoveFileWithErrorCheck(t, tokenFile.Name())
21+
tests := []struct {
22+
name string
23+
path string
24+
expected string
25+
expectedErrMsg string
26+
createToken bool
27+
}{
28+
{
29+
name: "Test 1: File exists",
30+
createToken: true,
31+
path: tokenFile.Name(),
32+
expected: "test-tkn",
33+
expectedErrMsg: "",
34+
},
35+
{
36+
name: "Test 2: File does not exist",
37+
createToken: false,
38+
path: "test-tkn",
39+
expected: "",
40+
expectedErrMsg: "unable to read from file: open test-tkn: no such file or directory",
41+
},
42+
{
43+
name: "Test 3: Empty path",
44+
createToken: false,
45+
path: "",
46+
expected: "",
47+
expectedErrMsg: "failed to read file since file path is empty",
48+
},
49+
}
50+
for _, tt := range tests {
51+
t.Run(tt.name, func(t *testing.T) {
52+
if tt.createToken {
53+
writeErr := os.WriteFile(tokenFile.Name(), []byte(" test-tkn\n"), 0o600)
54+
require.NoError(t, writeErr)
55+
}
56+
57+
token, err := ReadFromFile(tt.path)
58+
if err != nil {
59+
assert.Equal(t, tt.expectedErrMsg, err.Error())
60+
}
61+
assert.Equal(t, tt.expected, token)
62+
})
63+
}
64+
}

Diff for: internal/grpc/grpc.go

+4-26
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@
66
package grpc
77

88
import (
9-
"bytes"
109
"context"
1110
"crypto/tls"
1211
"errors"
1312
"fmt"
1413
"log/slog"
1514
"net"
16-
"os"
1715
"strings"
1816
"sync"
1917

18+
"github.com/nginx/agent/v3/internal/datasource/file"
19+
2020
"github.com/cenkalti/backoff/v4"
2121
grpcRetry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
2222

@@ -243,7 +243,8 @@ func addPerRPCCredentials(agentConfig *config.Config, resourceID string, opts []
243243
token := agentConfig.Command.Auth.Token
244244

245245
if agentConfig.Command.Auth.TokenPath != "" {
246-
tk, err := retrieveTokenFromFile(agentConfig.Command.Auth.TokenPath)
246+
slog.Debug("Reading token from file", "path", agentConfig.Command.Auth.TokenPath)
247+
tk, err := file.ReadFromFile(agentConfig.Command.Auth.TokenPath)
247248
if err == nil {
248249
token = tk
249250
} else {
@@ -263,29 +264,6 @@ func addPerRPCCredentials(agentConfig *config.Config, resourceID string, opts []
263264
return opts
264265
}
265266

266-
func retrieveTokenFromFile(path string) (string, error) {
267-
if path == "" {
268-
return "", errors.New("token file path is empty")
269-
}
270-
271-
slog.Debug("Reading token from file", "path", path)
272-
var keyVal string
273-
keyBytes, err := os.ReadFile(path)
274-
if err != nil {
275-
return "", fmt.Errorf("unable to read token from file: %w", err)
276-
}
277-
278-
keyBytes = bytes.TrimSpace(keyBytes)
279-
keyBytes = bytes.TrimRight(keyBytes, "\n")
280-
keyVal = string(keyBytes)
281-
282-
if keyVal == "" {
283-
return "", errors.New("failed to load token, token file is empty")
284-
}
285-
286-
return keyVal, nil
287-
}
288-
289267
// Have to create our own UnaryClientInterceptor function since protovalidate only provides a UnaryServerInterceptor
290268
// https://pkg.go.dev/github.com/grpc-ecosystem/go-grpc-middleware/v2@v2.1.0/interceptors/protovalidate
291269
func ProtoValidatorUnaryClientInterceptor() (grpc.UnaryClientInterceptor, error) {

Diff for: internal/grpc/grpc_test.go

-61
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package grpc
88
import (
99
"context"
1010
"fmt"
11-
"os"
1211
"testing"
1312

1413
"google.golang.org/grpc/credentials"
@@ -356,66 +355,6 @@ func Test_ValidateGrpcError(t *testing.T) {
356355
assert.IsType(t, &backoff.PermanentError{}, result)
357356
}
358357

359-
// nolint:revive,gocognit
360-
func Test_retrieveTokenFromFile(t *testing.T) {
361-
tests := []struct {
362-
name string
363-
path string
364-
want string
365-
wantErrMsg string
366-
createToken bool
367-
}{
368-
{
369-
name: "Test 1: File exists",
370-
createToken: true,
371-
path: "test-tkn",
372-
want: "test-tkn",
373-
wantErrMsg: "",
374-
},
375-
{
376-
name: "Test 2: File does not exist",
377-
createToken: false,
378-
path: "test-tkn",
379-
want: "",
380-
wantErrMsg: "unable to read token from file: open test-tkn: no such file or directory",
381-
},
382-
{
383-
name: "Test 3: Empty path",
384-
createToken: false,
385-
path: "",
386-
want: "",
387-
wantErrMsg: "token file path is empty",
388-
},
389-
}
390-
for _, tt := range tests {
391-
t.Run(tt.name, func(t *testing.T) {
392-
defer func() {
393-
if tt.createToken {
394-
err := os.Remove(tt.path)
395-
if err != nil {
396-
t.Log(err)
397-
}
398-
}
399-
}()
400-
401-
if tt.createToken {
402-
err := os.WriteFile(tt.path, []byte(tt.path), 0o600)
403-
if err != nil {
404-
t.Fatal(err)
405-
}
406-
}
407-
408-
got, err := retrieveTokenFromFile(tt.path)
409-
if err != nil {
410-
if err.Error() != tt.wantErrMsg {
411-
t.Errorf("retrieveTokenFromFile() error = %v, wantErr %v", err, tt.wantErrMsg)
412-
}
413-
}
414-
assert.Equalf(t, tt.want, got, "retrieveTokenFromFile(%v)", tt.path)
415-
})
416-
}
417-
}
418-
419358
func Test_getTransportCredentials(t *testing.T) {
420359
tests := []struct {
421360
want credentials.TransportCredentials

Diff for: internal/plugin/plugin_manager.go

+1-9
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func addResourcePlugin(plugins []bus.Plugin, agentConfig *config.Config) []bus.P
3939
}
4040

4141
func addCommandAndFilePlugins(ctx context.Context, plugins []bus.Plugin, agentConfig *config.Config) []bus.Plugin {
42-
if isGrpcClientConfigured(agentConfig) {
42+
if agentConfig.IsGrpcClientConfigured() {
4343
grpcConnection, err := grpc.NewGrpcConnection(ctx, agentConfig)
4444
if err != nil {
4545
slog.WarnContext(ctx, "Failed to create gRPC connection", "error", err)
@@ -79,11 +79,3 @@ func addWatcherPlugin(plugins []bus.Plugin, agentConfig *config.Config) []bus.Pl
7979

8080
return plugins
8181
}
82-
83-
func isGrpcClientConfigured(agentConfig *config.Config) bool {
84-
return agentConfig.Command != nil &&
85-
agentConfig.Command.Server != nil &&
86-
agentConfig.Command.Server.Host != "" &&
87-
agentConfig.Command.Server.Port != 0 &&
88-
agentConfig.Command.Server.Type == config.Grpc
89-
}

0 commit comments

Comments
 (0)