From 3e334ddb0b81f98276478c977fe679afd7407b88 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 13 Jan 2025 16:01:42 -0500 Subject: [PATCH 01/23] Add otel-agent status --- cmd/otel-agent/command/command.go | 2 + cmd/otel-agent/subcommands/run/command.go | 2 + cmd/otel-agent/subcommands/status/command.go | 240 +++++++++++++++++++ 3 files changed, 244 insertions(+) create mode 100644 cmd/otel-agent/subcommands/status/command.go diff --git a/cmd/otel-agent/command/command.go b/cmd/otel-agent/command/command.go index e3873f9ab800f..88ad7eb76d838 100644 --- a/cmd/otel-agent/command/command.go +++ b/cmd/otel-agent/command/command.go @@ -20,6 +20,7 @@ import ( "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/run" + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/status" "github.com/DataDog/datadog-agent/pkg/cli/subcommands/version" "go.opentelemetry.io/collector/featuregate" ) @@ -48,6 +49,7 @@ func makeCommands(globalParams *subcommands.GlobalParams) *cobra.Command { } commands := []*cobra.Command{ run.MakeCommand(globalConfGetter), + status.MakeCommand(globalConfGetter), version.MakeCommand("otel-agent"), } diff --git a/cmd/otel-agent/subcommands/run/command.go b/cmd/otel-agent/subcommands/run/command.go index 87e8b1b74349a..960fb3d8a47dc 100644 --- a/cmd/otel-agent/subcommands/run/command.go +++ b/cmd/otel-agent/subcommands/run/command.go @@ -28,6 +28,7 @@ import ( logfx "github.com/DataDog/datadog-agent/comp/core/log/fx" logtracefx "github.com/DataDog/datadog-agent/comp/core/log/fx-trace" "github.com/DataDog/datadog-agent/comp/core/secrets" + "github.com/DataDog/datadog-agent/comp/core/status/statusimpl" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" remoteTaggerFx "github.com/DataDog/datadog-agent/comp/core/tagger/fx-remote" taggerTypes "github.com/DataDog/datadog-agent/comp/core/tagger/types" @@ -219,6 +220,7 @@ func runOTelAgentCommand(ctx context.Context, params *subcommands.GlobalParams, PIDFilePath: "", }), traceagentfx.Module(), + statusimpl.Module(), ) } diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go new file mode 100644 index 0000000000000..0ab6edc4be405 --- /dev/null +++ b/cmd/otel-agent/subcommands/status/command.go @@ -0,0 +1,240 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package status implements 'agent status'. +package status + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + "net/url" + "os" + + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/cmd/agent/command" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + log "github.com/DataDog/datadog-agent/comp/core/log/def" + "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + apiutil "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/scrubber" + + "github.com/spf13/cobra" +) + +// cliParams are the command-line arguments for this subcommand +type cliParams struct { + *subcommands.GlobalParams + + // args are the positional command-line arguments + args []string + + jsonStatus bool + prettyPrintJSON bool + statusFilePath string + verbose bool + list bool + logLevelDefaultOff command.LogLevelDefaultOff +} + +// MakeCommand returns a slice of subcommands for the 'agent' command. +func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Command { + globalParams := globalConfGetter() + cliParams := &cliParams{ + GlobalParams: globalParams, + } + cmd := &cobra.Command{ + Use: "status [section]", + Short: "Display the current status", + Long: `Display the current status. +If no section is specified, this command will display all status sections. +If a specific section is provided, such as 'collector', it will only display the status of that section. +The --list flag can be used to list all available status sections.`, + RunE: func(_ *cobra.Command, args []string) error { + cliParams.args = args + + // Prevent autoconfig to run when running status as it logs before logger + // is setup. Cannot rely on config.Override as env detection is run before + // overrides are set. TODO: This should eventually be handled with a + // BundleParams field for AD. + os.Setenv("DD_AUTOCONFIG_FROM_ENVIRONMENT", "false") + + return fxutil.OneShot(statusCmd, + fx.Supply(cliParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewAgentParams(globalParams.CoreConfPath, config.WithExtraConfFiles(globalParams.ConfPaths)), + LogParams: log.ForOneShot(command.LoggerName, cliParams.logLevelDefaultOff.Value(), true)}), + core.Bundle(), + ) + }, + } + cliParams.logLevelDefaultOff.Register(cmd) + cmd.PersistentFlags().BoolVarP(&cliParams.jsonStatus, "json", "j", false, "print out raw json") + cmd.PersistentFlags().BoolVarP(&cliParams.prettyPrintJSON, "pretty-json", "p", false, "pretty print JSON") + cmd.PersistentFlags().StringVarP(&cliParams.statusFilePath, "file", "o", "", "Output the status command to a file") + cmd.PersistentFlags().BoolVarP(&cliParams.verbose, "verbose", "v", false, "print out verbose status") + cmd.PersistentFlags().BoolVarP(&cliParams.list, "list", "l", false, "list all available status sections") + + return cmd +} + +func scrubMessage(message string) string { + msgScrubbed, err := scrubber.ScrubBytes([]byte(message)) + if err == nil { + return string(msgScrubbed) + } + return "[REDACTED] - failure to clean the message" +} + +func redactError(unscrubbedError error) error { + if unscrubbedError == nil { + return unscrubbedError + } + + errMsg := unscrubbedError.Error() + scrubbedMsg, scrubOperationErr := scrubber.ScrubBytes([]byte(errMsg)) + var scrubbedError error + if scrubOperationErr != nil { + scrubbedError = errors.New("[REDACTED] failed to clean error") + } else { + scrubbedError = errors.New(string(scrubbedMsg)) + } + + return scrubbedError +} + +func statusCmd(logger log.Component, config config.Component, _ sysprobeconfig.Component, cliParams *cliParams) error { + if cliParams.list { + return redactError(requestSections(config)) + } + + if len(cliParams.args) < 1 { + return redactError(requestStatus(config, cliParams)) + } + + return componentStatusCmd(logger, config, cliParams) +} + +func setIpcURL(cliParams *cliParams) url.Values { + v := url.Values{} + if cliParams.verbose { + v.Set("verbose", "true") + } + + if cliParams.prettyPrintJSON || cliParams.jsonStatus { + v.Set("format", "json") + } else { + v.Set("format", "text") + } + + return v +} + +func renderResponse(res []byte, cliParams *cliParams) error { + var s string + + // The rendering is done in the client so that the agent has less work to do + if cliParams.prettyPrintJSON { + var prettyJSON bytes.Buffer + json.Indent(&prettyJSON, res, "", " ") //nolint:errcheck + s = prettyJSON.String() + } else if cliParams.jsonStatus { + s = string(res) + } else { + s = scrubMessage(string(res)) + } + + if cliParams.statusFilePath != "" { + return os.WriteFile(cliParams.statusFilePath, []byte(s), 0644) + } + fmt.Println(s) + return nil +} + +func requestStatus(config config.Component, cliParams *cliParams) error { + + if !cliParams.prettyPrintJSON && !cliParams.jsonStatus { + fmt.Printf("Getting the status from the agent.\n\n") + } + + v := setIpcURL(cliParams) + + endpoint, err := apiutil.NewIPCEndpoint(config, "/agent/status") + if err != nil { + return err + } + + res, err := endpoint.DoGet(apiutil.WithValues(v)) + if err != nil { + return err + } + + // The rendering is done in the client so that the agent has less work to do + err = renderResponse(res, cliParams) + if err != nil { + return err + } + + return nil +} + +func componentStatusCmd(_ log.Component, config config.Component, cliParams *cliParams) error { + if len(cliParams.args) > 1 { + return fmt.Errorf("only one section must be specified") + } + + return redactError(componentStatus(config, cliParams, cliParams.args[0])) +} + +func componentStatus(config config.Component, cliParams *cliParams, component string) error { + + v := setIpcURL(cliParams) + + endpoint, err := apiutil.NewIPCEndpoint(config, fmt.Sprintf("/agent/%s/status", component)) + if err != nil { + return err + } + res, err := endpoint.DoGet(apiutil.WithValues(v)) + if err != nil { + return err + } + + // The rendering is done in the client so that the agent has less work to do + err = renderResponse(res, cliParams) + if err != nil { + return err + } + + return nil +} + +func requestSections(config config.Component) error { + endpoint, err := apiutil.NewIPCEndpoint(config, "/agent/status/sections") + if err != nil { + return err + } + + res, err := endpoint.DoGet() + if err != nil { + return err + } + + var sections []string + err = json.Unmarshal(res, §ions) + if err != nil { + return err + } + + for _, section := range sections { + fmt.Printf("- \"%s\"\n", section) + } + + return nil +} From cb9e8bbe42eca44c01f2d226a9cf9be28e2ab92b Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 13 Jan 2025 16:13:16 -0500 Subject: [PATCH 02/23] Add test --- .../subcommands/status/command_test.go | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 cmd/otel-agent/subcommands/status/command_test.go diff --git a/cmd/otel-agent/subcommands/status/command_test.go b/cmd/otel-agent/subcommands/status/command_test.go new file mode 100644 index 0000000000000..21f97b3d911c0 --- /dev/null +++ b/cmd/otel-agent/subcommands/status/command_test.go @@ -0,0 +1,53 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package status + +import ( + "github.com/spf13/cobra" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/secrets" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +func globalConfGetter() *subcommands.GlobalParams { + return &subcommands.GlobalParams{ + ConfigName: "datadog-otel", + LoggerName: "logger", + } +} + +func TestStatusCommand(t *testing.T) { + defer os.Unsetenv("DD_AUTOCONFIG_FROM_ENVIRONMENT") // undo os.Setenv by RunE + + fxutil.TestOneShotSubcommand(t, + []*cobra.Command{MakeCommand(globalConfGetter)}, + []string{"status", "-j"}, + statusCmd, + func(cliParams *cliParams, _ core.BundleParams, secretParams secrets.Params) { + require.Equal(t, []string{}, cliParams.args) + require.Equal(t, true, cliParams.jsonStatus) + require.Equal(t, false, secretParams.Enabled) + }) +} + +func TestComponentStatusCommand(t *testing.T) { + defer os.Unsetenv("DD_AUTOCONFIG_FROM_ENVIRONMENT") // undo os.Setenv by RunE + fxutil.TestOneShotSubcommand(t, + []*cobra.Command{MakeCommand(globalConfGetter)}, + []string{"status", "component", "abc"}, + statusCmd, + func(cliParams *cliParams, _ core.BundleParams, secretParams secrets.Params) { + require.Equal(t, []string{"component", "abc"}, cliParams.args) + require.Equal(t, false, cliParams.jsonStatus) + require.Equal(t, false, secretParams.Enabled) + }) +} From cb462e03303babd6573ac334fca0c2ee0dcaa7c6 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 15 Jan 2025 14:48:19 -0500 Subject: [PATCH 03/23] Remove previous changes --- cmd/otel-agent/command/command.go | 2 - cmd/otel-agent/subcommands/run/command.go | 3 +- cmd/otel-agent/subcommands/status/command.go | 240 ------------------ .../subcommands/status/command_test.go | 53 ---- 4 files changed, 1 insertion(+), 297 deletions(-) delete mode 100644 cmd/otel-agent/subcommands/status/command.go delete mode 100644 cmd/otel-agent/subcommands/status/command_test.go diff --git a/cmd/otel-agent/command/command.go b/cmd/otel-agent/command/command.go index 88ad7eb76d838..e3873f9ab800f 100644 --- a/cmd/otel-agent/command/command.go +++ b/cmd/otel-agent/command/command.go @@ -20,7 +20,6 @@ import ( "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/run" - "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/status" "github.com/DataDog/datadog-agent/pkg/cli/subcommands/version" "go.opentelemetry.io/collector/featuregate" ) @@ -49,7 +48,6 @@ func makeCommands(globalParams *subcommands.GlobalParams) *cobra.Command { } commands := []*cobra.Command{ run.MakeCommand(globalConfGetter), - status.MakeCommand(globalConfGetter), version.MakeCommand("otel-agent"), } diff --git a/cmd/otel-agent/subcommands/run/command.go b/cmd/otel-agent/subcommands/run/command.go index 960fb3d8a47dc..f9d284f761fa8 100644 --- a/cmd/otel-agent/subcommands/run/command.go +++ b/cmd/otel-agent/subcommands/run/command.go @@ -28,7 +28,6 @@ import ( logfx "github.com/DataDog/datadog-agent/comp/core/log/fx" logtracefx "github.com/DataDog/datadog-agent/comp/core/log/fx-trace" "github.com/DataDog/datadog-agent/comp/core/secrets" - "github.com/DataDog/datadog-agent/comp/core/status/statusimpl" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" remoteTaggerFx "github.com/DataDog/datadog-agent/comp/core/tagger/fx-remote" taggerTypes "github.com/DataDog/datadog-agent/comp/core/tagger/types" @@ -220,7 +219,7 @@ func runOTelAgentCommand(ctx context.Context, params *subcommands.GlobalParams, PIDFilePath: "", }), traceagentfx.Module(), - statusimpl.Module(), + //statusimpl.Module(), ) } diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go deleted file mode 100644 index 0ab6edc4be405..0000000000000 --- a/cmd/otel-agent/subcommands/status/command.go +++ /dev/null @@ -1,240 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. - -// Package status implements 'agent status'. -package status - -import ( - "bytes" - "encoding/json" - "errors" - "fmt" - "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" - "net/url" - "os" - - "go.uber.org/fx" - - "github.com/DataDog/datadog-agent/cmd/agent/command" - "github.com/DataDog/datadog-agent/comp/core" - "github.com/DataDog/datadog-agent/comp/core/config" - log "github.com/DataDog/datadog-agent/comp/core/log/def" - "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" - apiutil "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "github.com/DataDog/datadog-agent/pkg/util/scrubber" - - "github.com/spf13/cobra" -) - -// cliParams are the command-line arguments for this subcommand -type cliParams struct { - *subcommands.GlobalParams - - // args are the positional command-line arguments - args []string - - jsonStatus bool - prettyPrintJSON bool - statusFilePath string - verbose bool - list bool - logLevelDefaultOff command.LogLevelDefaultOff -} - -// MakeCommand returns a slice of subcommands for the 'agent' command. -func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Command { - globalParams := globalConfGetter() - cliParams := &cliParams{ - GlobalParams: globalParams, - } - cmd := &cobra.Command{ - Use: "status [section]", - Short: "Display the current status", - Long: `Display the current status. -If no section is specified, this command will display all status sections. -If a specific section is provided, such as 'collector', it will only display the status of that section. -The --list flag can be used to list all available status sections.`, - RunE: func(_ *cobra.Command, args []string) error { - cliParams.args = args - - // Prevent autoconfig to run when running status as it logs before logger - // is setup. Cannot rely on config.Override as env detection is run before - // overrides are set. TODO: This should eventually be handled with a - // BundleParams field for AD. - os.Setenv("DD_AUTOCONFIG_FROM_ENVIRONMENT", "false") - - return fxutil.OneShot(statusCmd, - fx.Supply(cliParams), - fx.Supply(core.BundleParams{ - ConfigParams: config.NewAgentParams(globalParams.CoreConfPath, config.WithExtraConfFiles(globalParams.ConfPaths)), - LogParams: log.ForOneShot(command.LoggerName, cliParams.logLevelDefaultOff.Value(), true)}), - core.Bundle(), - ) - }, - } - cliParams.logLevelDefaultOff.Register(cmd) - cmd.PersistentFlags().BoolVarP(&cliParams.jsonStatus, "json", "j", false, "print out raw json") - cmd.PersistentFlags().BoolVarP(&cliParams.prettyPrintJSON, "pretty-json", "p", false, "pretty print JSON") - cmd.PersistentFlags().StringVarP(&cliParams.statusFilePath, "file", "o", "", "Output the status command to a file") - cmd.PersistentFlags().BoolVarP(&cliParams.verbose, "verbose", "v", false, "print out verbose status") - cmd.PersistentFlags().BoolVarP(&cliParams.list, "list", "l", false, "list all available status sections") - - return cmd -} - -func scrubMessage(message string) string { - msgScrubbed, err := scrubber.ScrubBytes([]byte(message)) - if err == nil { - return string(msgScrubbed) - } - return "[REDACTED] - failure to clean the message" -} - -func redactError(unscrubbedError error) error { - if unscrubbedError == nil { - return unscrubbedError - } - - errMsg := unscrubbedError.Error() - scrubbedMsg, scrubOperationErr := scrubber.ScrubBytes([]byte(errMsg)) - var scrubbedError error - if scrubOperationErr != nil { - scrubbedError = errors.New("[REDACTED] failed to clean error") - } else { - scrubbedError = errors.New(string(scrubbedMsg)) - } - - return scrubbedError -} - -func statusCmd(logger log.Component, config config.Component, _ sysprobeconfig.Component, cliParams *cliParams) error { - if cliParams.list { - return redactError(requestSections(config)) - } - - if len(cliParams.args) < 1 { - return redactError(requestStatus(config, cliParams)) - } - - return componentStatusCmd(logger, config, cliParams) -} - -func setIpcURL(cliParams *cliParams) url.Values { - v := url.Values{} - if cliParams.verbose { - v.Set("verbose", "true") - } - - if cliParams.prettyPrintJSON || cliParams.jsonStatus { - v.Set("format", "json") - } else { - v.Set("format", "text") - } - - return v -} - -func renderResponse(res []byte, cliParams *cliParams) error { - var s string - - // The rendering is done in the client so that the agent has less work to do - if cliParams.prettyPrintJSON { - var prettyJSON bytes.Buffer - json.Indent(&prettyJSON, res, "", " ") //nolint:errcheck - s = prettyJSON.String() - } else if cliParams.jsonStatus { - s = string(res) - } else { - s = scrubMessage(string(res)) - } - - if cliParams.statusFilePath != "" { - return os.WriteFile(cliParams.statusFilePath, []byte(s), 0644) - } - fmt.Println(s) - return nil -} - -func requestStatus(config config.Component, cliParams *cliParams) error { - - if !cliParams.prettyPrintJSON && !cliParams.jsonStatus { - fmt.Printf("Getting the status from the agent.\n\n") - } - - v := setIpcURL(cliParams) - - endpoint, err := apiutil.NewIPCEndpoint(config, "/agent/status") - if err != nil { - return err - } - - res, err := endpoint.DoGet(apiutil.WithValues(v)) - if err != nil { - return err - } - - // The rendering is done in the client so that the agent has less work to do - err = renderResponse(res, cliParams) - if err != nil { - return err - } - - return nil -} - -func componentStatusCmd(_ log.Component, config config.Component, cliParams *cliParams) error { - if len(cliParams.args) > 1 { - return fmt.Errorf("only one section must be specified") - } - - return redactError(componentStatus(config, cliParams, cliParams.args[0])) -} - -func componentStatus(config config.Component, cliParams *cliParams, component string) error { - - v := setIpcURL(cliParams) - - endpoint, err := apiutil.NewIPCEndpoint(config, fmt.Sprintf("/agent/%s/status", component)) - if err != nil { - return err - } - res, err := endpoint.DoGet(apiutil.WithValues(v)) - if err != nil { - return err - } - - // The rendering is done in the client so that the agent has less work to do - err = renderResponse(res, cliParams) - if err != nil { - return err - } - - return nil -} - -func requestSections(config config.Component) error { - endpoint, err := apiutil.NewIPCEndpoint(config, "/agent/status/sections") - if err != nil { - return err - } - - res, err := endpoint.DoGet() - if err != nil { - return err - } - - var sections []string - err = json.Unmarshal(res, §ions) - if err != nil { - return err - } - - for _, section := range sections { - fmt.Printf("- \"%s\"\n", section) - } - - return nil -} diff --git a/cmd/otel-agent/subcommands/status/command_test.go b/cmd/otel-agent/subcommands/status/command_test.go deleted file mode 100644 index 21f97b3d911c0..0000000000000 --- a/cmd/otel-agent/subcommands/status/command_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. - -package status - -import ( - "github.com/spf13/cobra" - "os" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" - "github.com/DataDog/datadog-agent/comp/core" - "github.com/DataDog/datadog-agent/comp/core/secrets" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" -) - -func globalConfGetter() *subcommands.GlobalParams { - return &subcommands.GlobalParams{ - ConfigName: "datadog-otel", - LoggerName: "logger", - } -} - -func TestStatusCommand(t *testing.T) { - defer os.Unsetenv("DD_AUTOCONFIG_FROM_ENVIRONMENT") // undo os.Setenv by RunE - - fxutil.TestOneShotSubcommand(t, - []*cobra.Command{MakeCommand(globalConfGetter)}, - []string{"status", "-j"}, - statusCmd, - func(cliParams *cliParams, _ core.BundleParams, secretParams secrets.Params) { - require.Equal(t, []string{}, cliParams.args) - require.Equal(t, true, cliParams.jsonStatus) - require.Equal(t, false, secretParams.Enabled) - }) -} - -func TestComponentStatusCommand(t *testing.T) { - defer os.Unsetenv("DD_AUTOCONFIG_FROM_ENVIRONMENT") // undo os.Setenv by RunE - fxutil.TestOneShotSubcommand(t, - []*cobra.Command{MakeCommand(globalConfGetter)}, - []string{"status", "component", "abc"}, - statusCmd, - func(cliParams *cliParams, _ core.BundleParams, secretParams secrets.Params) { - require.Equal(t, []string{"component", "abc"}, cliParams.args) - require.Equal(t, false, cliParams.jsonStatus) - require.Equal(t, false, secretParams.Enabled) - }) -} From 0d189a557c701c0c115a82e30a77cfcf880424e1 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 15 Jan 2025 14:50:26 -0500 Subject: [PATCH 04/23] WIP --- cmd/agent/subcommands/run/command.go | 2 + comp/core/status/statusimpl/status.go | 6 + .../status/statusimpl/status_api_endpoints.go | 3 + comp/otelcol/status/component.go | 13 ++ comp/otelcol/status/statusimpl/status.go | 126 ++++++++++++++++++ 5 files changed, 150 insertions(+) create mode 100644 comp/otelcol/status/component.go create mode 100644 comp/otelcol/status/statusimpl/status.go diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index 0cab289f949f9..0f6ecb3ac4a28 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -115,6 +115,7 @@ import ( "github.com/DataDog/datadog-agent/comp/otelcol" otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" + otelagentStatusImpl "github.com/DataDog/datadog-agent/comp/otelcol/status/statusimpl" "github.com/DataDog/datadog-agent/comp/process" processAgent "github.com/DataDog/datadog-agent/comp/process/agent" processagentStatusImpl "github.com/DataDog/datadog-agent/comp/process/status/statusimpl" @@ -372,6 +373,7 @@ func getSharedFxOption() fx.Option { AgentVersion: version.AgentVersion, }, ), + otelagentStatusImpl.Module(), traceagentStatusImpl.Module(), processagentStatusImpl.Module(), dogstatsdStatusimpl.Module(), diff --git a/comp/core/status/statusimpl/status.go b/comp/core/status/statusimpl/status.go index 5f7a783231ae9..b520c7ced4539 100644 --- a/comp/core/status/statusimpl/status.go +++ b/comp/core/status/statusimpl/status.go @@ -78,6 +78,7 @@ func sortByName(providers []status.Provider) []status.Provider { } func newStatus(deps dependencies) provides { + fmt.Println("---- newStatus ----") // Sections are sorted by name // The exception is the collector section. We want that to be the first section to be displayed // We manually insert the collector section in the first place after sorting them alphabetically @@ -87,6 +88,7 @@ func newStatus(deps dependencies) provides { providers := fxutil.GetAndFilterGroup(deps.Providers) for _, provider := range providers { + fmt.Printf("---- provider.Name(): %v ----\n", provider.Name()) if provider.Section() == status.CollectorSection && !collectorSectionPresent { collectorSectionPresent = true } @@ -125,6 +127,7 @@ func newStatus(deps dependencies) provides { sortedHeaderProviders = append([]status.HeaderProvider{newCommonHeaderProvider(deps.Params, deps.Config)}, sortedHeaderProviders...) + fmt.Printf("---- sortedSectionNames: %v ----\n", sortedSectionNames) c := &statusImplementation{ sortedSectionNames: sortedSectionNames, sortedProvidersBySection: sortedProvidersBySection, @@ -154,6 +157,7 @@ func newStatus(deps dependencies) provides { } func (s *statusImplementation) GetStatus(format string, verbose bool, excludeSections ...string) ([]byte, error) { + fmt.Println("---- GetStatus ----") var errs []error switch format { @@ -281,6 +285,7 @@ func (s *statusImplementation) GetStatus(format string, verbose bool, excludeSec } func (s *statusImplementation) GetStatusBySections(sections []string, format string, verbose bool) ([]byte, error) { + fmt.Println("---- GetStatusBySections ----") var errs []error if len(sections) == 1 && sections[0] == "header" { @@ -415,6 +420,7 @@ func (s *statusImplementation) GetStatusBySections(sections []string, format str } func (s *statusImplementation) GetSections() []string { + fmt.Println("---- GetSections ----") return append([]string{"header"}, s.sortedSectionNames...) } diff --git a/comp/core/status/statusimpl/status_api_endpoints.go b/comp/core/status/statusimpl/status_api_endpoints.go index 2c409d6d4b2f9..917bfb4f6c7fc 100644 --- a/comp/core/status/statusimpl/status_api_endpoints.go +++ b/comp/core/status/statusimpl/status_api_endpoints.go @@ -8,6 +8,7 @@ package statusimpl import ( "encoding/json" + "fmt" "net/http" "github.com/gorilla/mux" @@ -65,6 +66,8 @@ func (s *statusImplementation) getSections(w http.ResponseWriter, _ *http.Reques s.log.Info("Got a request for the status sections.") w.Header().Set("Content-Type", "application/json") + fmt.Println("---- getSections ----") + fmt.Printf("---- s.GetSections: %v ----\n", s.GetSections()) res, _ := json.Marshal(s.GetSections()) w.Write(res) } diff --git a/comp/otelcol/status/component.go b/comp/otelcol/status/component.go new file mode 100644 index 0000000000000..ec7f701e74c23 --- /dev/null +++ b/comp/otelcol/status/component.go @@ -0,0 +1,13 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package status implements the core status component information provider interface +package status + +// team: opentelemetry + +// Component is the status interface. +type Component interface { +} diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/statusimpl/status.go new file mode 100644 index 0000000000000..0184e1b28e34d --- /dev/null +++ b/comp/otelcol/status/statusimpl/status.go @@ -0,0 +1,126 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + +// Package statusimpl implements the status component interface +package statusimpl + +import ( + "embed" + "encoding/json" + "fmt" + "io" + "net/http" + "sync" + + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/status" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + + apiutil "github.com/DataDog/datadog-agent/pkg/api/util" +) + +// httpClients should be reused instead of created as needed. They keep cached TCP connections +// that may leak otherwise +var ( + httpClient *http.Client + clientInitOnce sync.Once +) + +func client() *http.Client { + clientInitOnce.Do(func() { + httpClient = apiutil.GetClient(false) + }) + + return httpClient +} + +type dependencies struct { + fx.In + + Config config.Component +} + +type provides struct { + fx.Out + + StatusProvider status.InformationProvider +} + +// Module defines the fx options for the status component. +func Module() fxutil.Module { + return fxutil.Component( + fx.Provide(newStatus)) +} + +type statusProvider struct { + Config config.Component +} + +func newStatus(deps dependencies) provides { + return provides{ + StatusProvider: status.NewInformationProvider(statusProvider{ + Config: deps.Config, + }), + } +} + +//go:embed status_templates +var templatesFS embed.FS + +// Name returns the name +func (s statusProvider) Name() string { + return "OTel Agent" +} + +// Section return the section +func (s statusProvider) Section() string { + return "OTel Agent" +} + +func (s statusProvider) getStatusInfo() map[string]interface{} { + fmt.Println("---- getStatusInfo OTel Agent ----") + + values := s.populateStatus() + fmt.Println(values) + + return values +} + +func (s statusProvider) populateStatus() map[string]interface{} { + extensionUrl := s.Config.GetString("otelcollector.extension_url") + + c := client() + resp, err := apiutil.DoGet(c, extensionUrl, apiutil.CloseConnection) + if err != nil { + return map[string]interface{}{ + "error": err.Error(), + } + } + + status := make(map[string]interface{}) + if err := json.Unmarshal(resp, &status); err != nil { + return map[string]interface{}{ + "error": err.Error(), + } + } + return status +} + +// JSON populates the status map +func (s statusProvider) JSON(_ bool, stats map[string]interface{}) error { + return nil +} + +// Text renders the text output +func (s statusProvider) Text(_ bool, buffer io.Writer) error { + return nil +} + +// HTML renders the html output +func (s statusProvider) HTML(_ bool, buffer io.Writer) error { + return nil +} From 1b08f7226bf4611e799517564f2a0f26e49fa804 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Fri, 24 Jan 2025 15:05:35 -0500 Subject: [PATCH 05/23] Add text status --- comp/otelcol/status/statusimpl/status.go | 172 +++++++++++++++--- .../status_templates/otelagent.tmpl | 29 +++ 2 files changed, 176 insertions(+), 25 deletions(-) create mode 100644 comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/statusimpl/status.go index 0184e1b28e34d..5cd4f10c73509 100644 --- a/comp/otelcol/status/statusimpl/status.go +++ b/comp/otelcol/status/statusimpl/status.go @@ -10,6 +10,7 @@ import ( "embed" "encoding/json" "fmt" + "gopkg.in/yaml.v3" "io" "net/http" "sync" @@ -18,9 +19,10 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/status" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" - + ddflareextension "github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/def" apiutil "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/prometheus" ) // httpClients should be reused instead of created as needed. They keep cached TCP connections @@ -30,13 +32,8 @@ var ( clientInitOnce sync.Once ) -func client() *http.Client { - clientInitOnce.Do(func() { - httpClient = apiutil.GetClient(false) - }) - - return httpClient -} +//go:embed status_templates +var templatesFS embed.FS type dependencies struct { fx.In @@ -50,27 +47,69 @@ type provides struct { StatusProvider status.InformationProvider } +type statusProvider struct { + Config config.Component + receiverStatus map[string]interface{} + exporterStatus map[string]interface{} +} + +type prometheusRuntimeConfig struct { + Service struct { + Telemetry struct { + Metrics struct { + Readers []struct { + Pull struct { + Exporter struct { + Prometheus struct { + Host string + Port int + } + } + } + } + } + } + } +} + +func client() *http.Client { + clientInitOnce.Do(func() { + httpClient = apiutil.GetClient(false) + }) + + return httpClient +} + // Module defines the fx options for the status component. func Module() fxutil.Module { return fxutil.Component( fx.Provide(newStatus)) } -type statusProvider struct { - Config config.Component -} - func newStatus(deps dependencies) provides { return provides{ StatusProvider: status.NewInformationProvider(statusProvider{ Config: deps.Config, + receiverStatus: map[string]interface{}{ + "spans": 0.0, + "metrics": 0.0, + "logs": 0.0, + "refused_spans": 0.0, + "refused_metrics": 0.0, + "refused_logs": 0.0, + }, + exporterStatus: map[string]interface{}{ + "spans": 0.0, + "metrics": 0.0, + "logs": 0.0, + "failed_spans": 0.0, + "failed_metrics": 0.0, + "failed_logs": 0.0, + }, }), } } -//go:embed status_templates -var templatesFS embed.FS - // Name returns the name func (s statusProvider) Name() string { return "OTel Agent" @@ -82,42 +121,125 @@ func (s statusProvider) Section() string { } func (s statusProvider) getStatusInfo() map[string]interface{} { - fmt.Println("---- getStatusInfo OTel Agent ----") + statusInfo := make(map[string]interface{}) values := s.populateStatus() - fmt.Println(values) - return values + statusInfo["otelAgent"] = values + + return statusInfo +} + +func getPrometheusUrl(extensionResp ddflareextension.Response) (string, error) { + var runtimeConfig prometheusRuntimeConfig + if err := yaml.Unmarshal([]byte(extensionResp.RuntimeConfig), &runtimeConfig); err != nil { + return "", err + } + prometheusHost := "localhost" + prometheusPort := 8888 + for _, reader := range runtimeConfig.Service.Telemetry.Metrics.Readers { + prometheusEndpoint := reader.Pull.Exporter.Prometheus + if prometheusEndpoint.Host != "" && prometheusEndpoint.Port != 0 { + prometheusHost = prometheusEndpoint.Host + prometheusPort = prometheusEndpoint.Port + } + } + return fmt.Sprintf("http://%v:%d/metrics", prometheusHost, prometheusPort), nil +} + +func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusUrl string) error { + resp, err := apiutil.DoGet(c, prometheusUrl, apiutil.CloseConnection) + if err != nil { + return err + } + metrics, err := prometheus.ParseMetrics(resp) + if err != nil { + return err + } + + for _, m := range metrics { + value := m.Samples[0].Value + switch m.Name { + case "otelcol_receiver_accepted_spans": + s.receiverStatus["spans"] = value + case "otelcol_receiver_accepted_metric_points": + s.receiverStatus["metrics"] = value + case "otelcol_receiver_accepted_log_records": + s.receiverStatus["logs"] = value + case "otelcol_receiver_refused_spans": + s.receiverStatus["refused_spans"] = value + case "otelcol_receiver_refused_metric_points": + s.receiverStatus["refused_metrics"] = value + case "otelcol_receiver_refused_log_records": + s.receiverStatus["refused_logs"] = value + case "otelcol_exporter_sent_spans": + s.exporterStatus["spans"] = value + case "otelcol_exporter_sent_metric_points": + s.exporterStatus["metrics"] = value + case "otelcol_exporter_sent_log_records": + s.exporterStatus["logs"] = value + case "otelcol_exporter_send_failed_spans": + s.exporterStatus["failed_spans"] = 1.0 + case "otelcol_exporter_send_failed_metric_points": + s.exporterStatus["failed_metrics"] = value + case "otelcol_exporter_send_failed_log_records": + s.exporterStatus["failed_logs"] = value + } + } + return nil } func (s statusProvider) populateStatus() map[string]interface{} { extensionUrl := s.Config.GetString("otelcollector.extension_url") - c := client() resp, err := apiutil.DoGet(c, extensionUrl, apiutil.CloseConnection) if err != nil { return map[string]interface{}{ + "url": extensionUrl, "error": err.Error(), } } - - status := make(map[string]interface{}) - if err := json.Unmarshal(resp, &status); err != nil { + var extensionResp ddflareextension.Response + if err = json.Unmarshal(resp, &extensionResp); err != nil { + return map[string]interface{}{ + "url": extensionUrl, + "error": err.Error(), + } + } + prometheusUrl, err := getPrometheusUrl(extensionResp) + if err != nil { + return map[string]interface{}{ + "url": extensionUrl, + "error": err.Error(), + } + } + err = s.populatePrometheusStatus(c, prometheusUrl) + if err != nil { return map[string]interface{}{ + "url": prometheusUrl, "error": err.Error(), } } - return status + return map[string]interface{}{ + "agentVersion": extensionResp.AgentVersion, + "collectorVersion": extensionResp.ExtensionVersion, + "receiver": s.receiverStatus, + "exporter": s.exporterStatus, + } } // JSON populates the status map func (s statusProvider) JSON(_ bool, stats map[string]interface{}) error { + values := s.populateStatus() + + stats["otelAgent"] = values + return nil } // Text renders the text output func (s statusProvider) Text(_ bool, buffer io.Writer) error { - return nil + return status.RenderText(templatesFS, "otelagent.tmpl", buffer, s.getStatusInfo()) } // HTML renders the html output diff --git a/comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl b/comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl new file mode 100644 index 0000000000000..0d00e7f794557 --- /dev/null +++ b/comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl @@ -0,0 +1,29 @@ +{{- with .otelAgent }} +{{- if .error }} + + Status: Not running or unreachable on {{.url}}. + Error: {{.error}} +{{- else}} + Status: Running + {{if .agentVersion}}Agent Version: {{.agentVersion}} {{end}} + {{if .collectorVersion}}Collector Version: {{.collectorVersion}} {{end}} + + Receiver + ========================== + Spans Accepted: {{.receiver.spans}} + {{- if gt .receiver.refused_spans 0.0}}, WARNING: Refused spans: {{.receiver.refused_spans}}{{end}} + Metric Points Accepted: {{.receiver.metrics}} + {{- if gt .receiver.refused_metrics 0.0}}, WARNING: Refused metric points: {{.receiver.refused_metrics}}{{end}} + Log Records Accepted: {{.receiver.logs}} + {{- if gt .receiver.refused_logs 0.0}}, WARNING: Refused log records: {{.receiver.refused_logs}}{{end}} + + Exporter + ========================== + Spans Sent: {{.exporter.spans}} + {{- if gt .exporter.failed_spans 0.0}}, WARNING: Send failed spans: {{.exporter.failed_spans}}{{end}} + Metric Points Sent: {{.exporter.metrics}} + {{- if gt .exporter.failed_metrics 0.0}}, WARNING: Send failed metrics: {{.exporter.failed_metrics}}{{end}} + Log Records Sent: {{.exporter.logs}} + {{- if gt .exporter.failed_logs 0.0}}, WARNING: Send failed logs: {{.exporter.failed_logs}}{{end}} +{{- end}} +{{- end}} From 9c53985070ac636892148d0f3d32ba8399b82318 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Fri, 24 Jan 2025 15:21:42 -0500 Subject: [PATCH 06/23] Add html status --- comp/otelcol/status/statusimpl/status.go | 2 +- .../status_templates/otelagentHTML.tmpl | 32 ++++++++++ comp/otelcol/status/statusimpl/status_test.go | 61 +++++++++++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl create mode 100644 comp/otelcol/status/statusimpl/status_test.go diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/statusimpl/status.go index 5cd4f10c73509..e9dedf67cef3e 100644 --- a/comp/otelcol/status/statusimpl/status.go +++ b/comp/otelcol/status/statusimpl/status.go @@ -244,5 +244,5 @@ func (s statusProvider) Text(_ bool, buffer io.Writer) error { // HTML renders the html output func (s statusProvider) HTML(_ bool, buffer io.Writer) error { - return nil + return status.RenderHTML(templatesFS, "otelagentHTML.tmpl", buffer, s.getStatusInfo()) } diff --git a/comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl b/comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl new file mode 100644 index 0000000000000..2be1dae02d0d5 --- /dev/null +++ b/comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl @@ -0,0 +1,32 @@ +
+ OTel Agent + + {{- with .otelAgent -}} + {{- if .error }} + Not running or unreachable on {{.url}}. + Error: {{.error}}
+ {{- else}} + Status: Running
+ {{if .agentVersion}}Agent Version: {{.agentVersion}}
{{end}} + {{if .collectorVersion}}Collector Version: {{.collectorVersion}}
{{end}} + Receiver + + Spans Accepted: {{.receiver.spans}} + {{- if gt .receiver.refused_spans 0.0}}, WARNING: Refused spans: {{.receiver.refused_spans}}{{end}} + Metric Points Accepted: {{.receiver.metrics}} + {{- if gt .receiver.refused_metrics 0.0}}, WARNING: Refused metric points: {{.receiver.refused_metrics}}{{end}} + Log Records Accepted: {{.receiver.logs}} + {{- if gt .receiver.refused_logs 0.0}}, WARNING: Refused log records: {{.receiver.refused_logs}}{{end}} + Exporter + + Spans Sent: {{.exporter.spans}} + {{- if gt .exporter.failed_spans 0.0}}, WARNING: Send failed spans: {{.exporter.failed_spans}}{{end}} + Metric Points Sent: {{.exporter.metrics}} + {{- if gt .exporter.failed_metrics 0.0}}, WARNING: Send failed metrics: {{.exporter.failed_metrics}}{{end}} + Log Records Sent: {{.exporter.logs}} + {{- if gt .exporter.failed_logs 0.0}}, WARNING: Send failed logs: {{.exporter.failed_logs}}{{end}} + + {{- end }} + {{ end }} + +
diff --git a/comp/otelcol/status/statusimpl/status_test.go b/comp/otelcol/status/statusimpl/status_test.go new file mode 100644 index 0000000000000..80af331a0784c --- /dev/null +++ b/comp/otelcol/status/statusimpl/status_test.go @@ -0,0 +1,61 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + +package statusimpl + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +func TestStatusOut(t *testing.T) { + deps := fxutil.Test[dependencies](t, fx.Options( + config.MockModule(), + )) + + provides := newStatus(deps) + + headerProvider := provides.StatusProvider.Provider + + tests := []struct { + name string + assertFunc func(t *testing.T) + }{ + {"JSON", func(t *testing.T) { + stats := make(map[string]interface{}) + headerProvider.JSON(false, stats) + + assert.NotEmpty(t, stats) + }}, + {"Text", func(t *testing.T) { + b := new(bytes.Buffer) + err := headerProvider.Text(false, b) + + assert.NoError(t, err) + + assert.NotEmpty(t, b.String()) + }}, + {"HTML", func(t *testing.T) { + b := new(bytes.Buffer) + err := headerProvider.HTML(false, b) + + assert.NoError(t, err) + + assert.NotEmpty(t, b.String()) + }}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.assertFunc(t) + }) + } +} From dc5a823576c194e1a454ce0204fd318335aeac29 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Fri, 24 Jan 2025 15:25:00 -0500 Subject: [PATCH 07/23] Clean up --- cmd/otel-agent/subcommands/run/command.go | 1 - comp/core/status/statusimpl/status.go | 6 ------ comp/core/status/statusimpl/status_api_endpoints.go | 3 --- comp/otelcol/status/statusimpl/status.go | 2 +- 4 files changed, 1 insertion(+), 11 deletions(-) diff --git a/cmd/otel-agent/subcommands/run/command.go b/cmd/otel-agent/subcommands/run/command.go index f9d284f761fa8..87e8b1b74349a 100644 --- a/cmd/otel-agent/subcommands/run/command.go +++ b/cmd/otel-agent/subcommands/run/command.go @@ -219,7 +219,6 @@ func runOTelAgentCommand(ctx context.Context, params *subcommands.GlobalParams, PIDFilePath: "", }), traceagentfx.Module(), - //statusimpl.Module(), ) } diff --git a/comp/core/status/statusimpl/status.go b/comp/core/status/statusimpl/status.go index b520c7ced4539..5f7a783231ae9 100644 --- a/comp/core/status/statusimpl/status.go +++ b/comp/core/status/statusimpl/status.go @@ -78,7 +78,6 @@ func sortByName(providers []status.Provider) []status.Provider { } func newStatus(deps dependencies) provides { - fmt.Println("---- newStatus ----") // Sections are sorted by name // The exception is the collector section. We want that to be the first section to be displayed // We manually insert the collector section in the first place after sorting them alphabetically @@ -88,7 +87,6 @@ func newStatus(deps dependencies) provides { providers := fxutil.GetAndFilterGroup(deps.Providers) for _, provider := range providers { - fmt.Printf("---- provider.Name(): %v ----\n", provider.Name()) if provider.Section() == status.CollectorSection && !collectorSectionPresent { collectorSectionPresent = true } @@ -127,7 +125,6 @@ func newStatus(deps dependencies) provides { sortedHeaderProviders = append([]status.HeaderProvider{newCommonHeaderProvider(deps.Params, deps.Config)}, sortedHeaderProviders...) - fmt.Printf("---- sortedSectionNames: %v ----\n", sortedSectionNames) c := &statusImplementation{ sortedSectionNames: sortedSectionNames, sortedProvidersBySection: sortedProvidersBySection, @@ -157,7 +154,6 @@ func newStatus(deps dependencies) provides { } func (s *statusImplementation) GetStatus(format string, verbose bool, excludeSections ...string) ([]byte, error) { - fmt.Println("---- GetStatus ----") var errs []error switch format { @@ -285,7 +281,6 @@ func (s *statusImplementation) GetStatus(format string, verbose bool, excludeSec } func (s *statusImplementation) GetStatusBySections(sections []string, format string, verbose bool) ([]byte, error) { - fmt.Println("---- GetStatusBySections ----") var errs []error if len(sections) == 1 && sections[0] == "header" { @@ -420,7 +415,6 @@ func (s *statusImplementation) GetStatusBySections(sections []string, format str } func (s *statusImplementation) GetSections() []string { - fmt.Println("---- GetSections ----") return append([]string{"header"}, s.sortedSectionNames...) } diff --git a/comp/core/status/statusimpl/status_api_endpoints.go b/comp/core/status/statusimpl/status_api_endpoints.go index 917bfb4f6c7fc..2c409d6d4b2f9 100644 --- a/comp/core/status/statusimpl/status_api_endpoints.go +++ b/comp/core/status/statusimpl/status_api_endpoints.go @@ -8,7 +8,6 @@ package statusimpl import ( "encoding/json" - "fmt" "net/http" "github.com/gorilla/mux" @@ -66,8 +65,6 @@ func (s *statusImplementation) getSections(w http.ResponseWriter, _ *http.Reques s.log.Info("Got a request for the status sections.") w.Header().Set("Content-Type", "application/json") - fmt.Println("---- getSections ----") - fmt.Printf("---- s.GetSections: %v ----\n", s.GetSections()) res, _ := json.Marshal(s.GetSections()) w.Write(res) } diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/statusimpl/status.go index e9dedf67cef3e..f22f0d179b514 100644 --- a/comp/otelcol/status/statusimpl/status.go +++ b/comp/otelcol/status/statusimpl/status.go @@ -179,7 +179,7 @@ func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusUrl s case "otelcol_exporter_sent_log_records": s.exporterStatus["logs"] = value case "otelcol_exporter_send_failed_spans": - s.exporterStatus["failed_spans"] = 1.0 + s.exporterStatus["failed_spans"] = value case "otelcol_exporter_send_failed_metric_points": s.exporterStatus["failed_metrics"] = value case "otelcol_exporter_send_failed_log_records": From 369e77f2cf5467a836f67c881251db6e6d37bdc3 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Fri, 24 Jan 2025 15:39:16 -0500 Subject: [PATCH 08/23] lint components --- comp/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/comp/README.md b/comp/README.md index f011bc57fe71d..5b5e2a4ed9f98 100644 --- a/comp/README.md +++ b/comp/README.md @@ -390,6 +390,10 @@ Package ddflareextension defines the OpenTelemetry Extension component. Package logsagentpipeline contains logs agent pipeline component +### [comp/otelcol/status](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/otelcol/status) + +Package status implements the core status component information provider interface + ## [comp/process](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process) (Component Bundle) *Datadog Team*: container-intake From cbda996fdd10b0d37799400b79c280eb444357b0 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Fri, 24 Jan 2025 15:40:18 -0500 Subject: [PATCH 09/23] Fix lint --- comp/otelcol/status/statusimpl/status.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/statusimpl/status.go index f22f0d179b514..f8053e854854e 100644 --- a/comp/otelcol/status/statusimpl/status.go +++ b/comp/otelcol/status/statusimpl/status.go @@ -130,7 +130,7 @@ func (s statusProvider) getStatusInfo() map[string]interface{} { return statusInfo } -func getPrometheusUrl(extensionResp ddflareextension.Response) (string, error) { +func getPrometheusURL(extensionResp ddflareextension.Response) (string, error) { var runtimeConfig prometheusRuntimeConfig if err := yaml.Unmarshal([]byte(extensionResp.RuntimeConfig), &runtimeConfig); err != nil { return "", err @@ -147,8 +147,8 @@ func getPrometheusUrl(extensionResp ddflareextension.Response) (string, error) { return fmt.Sprintf("http://%v:%d/metrics", prometheusHost, prometheusPort), nil } -func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusUrl string) error { - resp, err := apiutil.DoGet(c, prometheusUrl, apiutil.CloseConnection) +func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusURL string) error { + resp, err := apiutil.DoGet(c, prometheusURL, apiutil.CloseConnection) if err != nil { return err } @@ -190,33 +190,33 @@ func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusUrl s } func (s statusProvider) populateStatus() map[string]interface{} { - extensionUrl := s.Config.GetString("otelcollector.extension_url") + extensionURL := s.Config.GetString("otelcollector.extension_url") c := client() - resp, err := apiutil.DoGet(c, extensionUrl, apiutil.CloseConnection) + resp, err := apiutil.DoGet(c, extensionURL, apiutil.CloseConnection) if err != nil { return map[string]interface{}{ - "url": extensionUrl, + "url": extensionURL, "error": err.Error(), } } var extensionResp ddflareextension.Response if err = json.Unmarshal(resp, &extensionResp); err != nil { return map[string]interface{}{ - "url": extensionUrl, + "url": extensionURL, "error": err.Error(), } } - prometheusUrl, err := getPrometheusUrl(extensionResp) + prometheusURL, err := getPrometheusURL(extensionResp) if err != nil { return map[string]interface{}{ - "url": extensionUrl, + "url": extensionURL, "error": err.Error(), } } - err = s.populatePrometheusStatus(c, prometheusUrl) + err = s.populatePrometheusStatus(c, prometheusURL) if err != nil { return map[string]interface{}{ - "url": prometheusUrl, + "url": prometheusURL, "error": err.Error(), } } From dcd2f5970b9999a55a105237a4de1251499d43f6 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 27 Jan 2025 14:00:17 -0500 Subject: [PATCH 10/23] PR comments --- cmd/agent/subcommands/run/command.go | 4 +- comp/otelcol/status/{ => def}/component.go | 2 +- comp/otelcol/status/fx/fx.go | 23 +++++++++ .../status/{statusimpl => impl}/status.go | 50 ++++--------------- .../status_templates/otelagent.tmpl | 0 .../status_templates/otelagentHTML.tmpl | 0 .../{statusimpl => impl}/status_test.go | 9 ++-- 7 files changed, 40 insertions(+), 48 deletions(-) rename comp/otelcol/status/{ => def}/component.go (90%) create mode 100644 comp/otelcol/status/fx/fx.go rename comp/otelcol/status/{statusimpl => impl}/status.go (84%) rename comp/otelcol/status/{statusimpl => impl}/status_templates/otelagent.tmpl (100%) rename comp/otelcol/status/{statusimpl => impl}/status_templates/otelagentHTML.tmpl (100%) rename comp/otelcol/status/{statusimpl => impl}/status_test.go (87%) diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index 0f6ecb3ac4a28..aa7430d06281b 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -115,7 +115,7 @@ import ( "github.com/DataDog/datadog-agent/comp/otelcol" otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" - otelagentStatusImpl "github.com/DataDog/datadog-agent/comp/otelcol/status/statusimpl" + otelagentStatusfx "github.com/DataDog/datadog-agent/comp/otelcol/status/fx" "github.com/DataDog/datadog-agent/comp/process" processAgent "github.com/DataDog/datadog-agent/comp/process/agent" processagentStatusImpl "github.com/DataDog/datadog-agent/comp/process/status/statusimpl" @@ -373,7 +373,7 @@ func getSharedFxOption() fx.Option { AgentVersion: version.AgentVersion, }, ), - otelagentStatusImpl.Module(), + otelagentStatusfx.Module(), traceagentStatusImpl.Module(), processagentStatusImpl.Module(), dogstatsdStatusimpl.Module(), diff --git a/comp/otelcol/status/component.go b/comp/otelcol/status/def/component.go similarity index 90% rename from comp/otelcol/status/component.go rename to comp/otelcol/status/def/component.go index ec7f701e74c23..deaff5f0518f8 100644 --- a/comp/otelcol/status/component.go +++ b/comp/otelcol/status/def/component.go @@ -1,7 +1,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. +// Copyright 2025-present Datadog, Inc. // Package status implements the core status component information provider interface package status diff --git a/comp/otelcol/status/fx/fx.go b/comp/otelcol/status/fx/fx.go new file mode 100644 index 0000000000000..b25adf9945109 --- /dev/null +++ b/comp/otelcol/status/fx/fx.go @@ -0,0 +1,23 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025-present Datadog, Inc. + +// Package fx provides the fx module for the status component +package fx + +import ( + status "github.com/DataDog/datadog-agent/comp/otelcol/status/def" + statusimpl "github.com/DataDog/datadog-agent/comp/otelcol/status/impl" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +// Module defines the fx options for the status component. +func Module() fxutil.Module { + return fxutil.Component( + fxutil.ProvideComponentConstructor( + statusimpl.NewComponent, + ), + fxutil.ProvideOptional[status.Component](), + ) +} diff --git a/comp/otelcol/status/statusimpl/status.go b/comp/otelcol/status/impl/status.go similarity index 84% rename from comp/otelcol/status/statusimpl/status.go rename to comp/otelcol/status/impl/status.go index f8053e854854e..d9eff12869dff 100644 --- a/comp/otelcol/status/statusimpl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -1,7 +1,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2023-present Datadog, Inc. +// Copyright 2025-present Datadog, Inc. // Package statusimpl implements the status component interface package statusimpl @@ -13,42 +13,28 @@ import ( "gopkg.in/yaml.v3" "io" "net/http" - "sync" - - "go.uber.org/fx" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/status" ddflareextension "github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/def" apiutil "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/prometheus" ) -// httpClients should be reused instead of created as needed. They keep cached TCP connections -// that may leak otherwise -var ( - httpClient *http.Client - clientInitOnce sync.Once -) - //go:embed status_templates var templatesFS embed.FS -type dependencies struct { - fx.In - +type Dependencies struct { Config config.Component } -type provides struct { - fx.Out - +type Provides struct { StatusProvider status.InformationProvider } type statusProvider struct { Config config.Component + client *http.Client receiverStatus map[string]interface{} exporterStatus map[string]interface{} } @@ -72,24 +58,11 @@ type prometheusRuntimeConfig struct { } } -func client() *http.Client { - clientInitOnce.Do(func() { - httpClient = apiutil.GetClient(false) - }) - - return httpClient -} - -// Module defines the fx options for the status component. -func Module() fxutil.Module { - return fxutil.Component( - fx.Provide(newStatus)) -} - -func newStatus(deps dependencies) provides { - return provides{ +func NewComponent(deps Dependencies) Provides { + return Provides{ StatusProvider: status.NewInformationProvider(statusProvider{ Config: deps.Config, + client: apiutil.GetClient(false), receiverStatus: map[string]interface{}{ "spans": 0.0, "metrics": 0.0, @@ -147,8 +120,8 @@ func getPrometheusURL(extensionResp ddflareextension.Response) (string, error) { return fmt.Sprintf("http://%v:%d/metrics", prometheusHost, prometheusPort), nil } -func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusURL string) error { - resp, err := apiutil.DoGet(c, prometheusURL, apiutil.CloseConnection) +func (s statusProvider) populatePrometheusStatus(prometheusURL string) error { + resp, err := apiutil.DoGet(s.client, prometheusURL, apiutil.CloseConnection) if err != nil { return err } @@ -191,8 +164,7 @@ func (s statusProvider) populatePrometheusStatus(c *http.Client, prometheusURL s func (s statusProvider) populateStatus() map[string]interface{} { extensionURL := s.Config.GetString("otelcollector.extension_url") - c := client() - resp, err := apiutil.DoGet(c, extensionURL, apiutil.CloseConnection) + resp, err := apiutil.DoGet(s.client, extensionURL, apiutil.CloseConnection) if err != nil { return map[string]interface{}{ "url": extensionURL, @@ -213,7 +185,7 @@ func (s statusProvider) populateStatus() map[string]interface{} { "error": err.Error(), } } - err = s.populatePrometheusStatus(c, prometheusURL) + err = s.populatePrometheusStatus(prometheusURL) if err != nil { return map[string]interface{}{ "url": prometheusURL, diff --git a/comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl b/comp/otelcol/status/impl/status_templates/otelagent.tmpl similarity index 100% rename from comp/otelcol/status/statusimpl/status_templates/otelagent.tmpl rename to comp/otelcol/status/impl/status_templates/otelagent.tmpl diff --git a/comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl b/comp/otelcol/status/impl/status_templates/otelagentHTML.tmpl similarity index 100% rename from comp/otelcol/status/statusimpl/status_templates/otelagentHTML.tmpl rename to comp/otelcol/status/impl/status_templates/otelagentHTML.tmpl diff --git a/comp/otelcol/status/statusimpl/status_test.go b/comp/otelcol/status/impl/status_test.go similarity index 87% rename from comp/otelcol/status/statusimpl/status_test.go rename to comp/otelcol/status/impl/status_test.go index 80af331a0784c..c893f3a730050 100644 --- a/comp/otelcol/status/statusimpl/status_test.go +++ b/comp/otelcol/status/impl/status_test.go @@ -1,7 +1,7 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2023-present Datadog, Inc. +// Copyright 2025-present Datadog, Inc. package statusimpl @@ -10,18 +10,15 @@ import ( "testing" "github.com/stretchr/testify/assert" - "go.uber.org/fx" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) func TestStatusOut(t *testing.T) { - deps := fxutil.Test[dependencies](t, fx.Options( - config.MockModule(), - )) + deps := fxutil.Test[Dependencies](t, config.MockModule()) - provides := newStatus(deps) + provides := NewComponent(deps) headerProvider := provides.StatusProvider.Provider From fe8295ba3e3ce7939f282fe48c7d05b506f6c457 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 27 Jan 2025 14:31:55 -0500 Subject: [PATCH 11/23] Fix test --- comp/otelcol/status/impl/status_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/comp/otelcol/status/impl/status_test.go b/comp/otelcol/status/impl/status_test.go index c893f3a730050..fa8ecc4145ed2 100644 --- a/comp/otelcol/status/impl/status_test.go +++ b/comp/otelcol/status/impl/status_test.go @@ -7,6 +7,7 @@ package statusimpl import ( "bytes" + "go.uber.org/fx" "testing" "github.com/stretchr/testify/assert" @@ -15,9 +16,16 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) -func TestStatusOut(t *testing.T) { - deps := fxutil.Test[Dependencies](t, config.MockModule()) +type testDependencies struct { + fx.In + Config config.Component +} +func TestStatusOut(t *testing.T) { + testDeps := fxutil.Test[testDependencies](t, config.MockModule()) + deps := Dependencies{ + Config: testDeps.Config, + } provides := NewComponent(deps) headerProvider := provides.StatusProvider.Provider From 4d6b5d0a55de442c37ae9889ee8a68babee63423 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 27 Jan 2025 14:47:43 -0500 Subject: [PATCH 12/23] Fix lint --- comp/otelcol/status/impl/status.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index d9eff12869dff..f46245118c775 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -24,10 +24,12 @@ import ( //go:embed status_templates var templatesFS embed.FS +// Dependencies defines the dependencies of the status component. type Dependencies struct { Config config.Component } +// Provider contains components provided by status constructor. type Provides struct { StatusProvider status.InformationProvider } @@ -58,6 +60,7 @@ type prometheusRuntimeConfig struct { } } +// NewComponent creates a new status component. func NewComponent(deps Dependencies) Provides { return Provides{ StatusProvider: status.NewInformationProvider(statusProvider{ From df8a95056f9791f0c32062b81d0753cc092982c2 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 27 Jan 2025 15:01:57 -0500 Subject: [PATCH 13/23] Fix lint --- comp/otelcol/status/impl/status.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index f46245118c775..7716d061bc2c3 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -29,7 +29,7 @@ type Dependencies struct { Config config.Component } -// Provider contains components provided by status constructor. +// Provides contains components provided by status constructor. type Provides struct { StatusProvider status.InformationProvider } From 7048dee157ed9bc6ead22f9d1cf95b67e1689a91 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Tue, 28 Jan 2025 11:20:08 -0500 Subject: [PATCH 14/23] PR comments --- comp/otelcol/status/fx/fx.go | 2 - comp/otelcol/status/impl/status.go | 58 +++++++++++++------------ comp/otelcol/status/impl/status_test.go | 30 +++++-------- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/comp/otelcol/status/fx/fx.go b/comp/otelcol/status/fx/fx.go index b25adf9945109..491e7d3d21bcd 100644 --- a/comp/otelcol/status/fx/fx.go +++ b/comp/otelcol/status/fx/fx.go @@ -7,7 +7,6 @@ package fx import ( - status "github.com/DataDog/datadog-agent/comp/otelcol/status/def" statusimpl "github.com/DataDog/datadog-agent/comp/otelcol/status/impl" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -18,6 +17,5 @@ func Module() fxutil.Module { fxutil.ProvideComponentConstructor( statusimpl.NewComponent, ), - fxutil.ProvideOptional[status.Component](), ) } diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index 7716d061bc2c3..1673432db858e 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -15,8 +15,9 @@ import ( "net/http" "github.com/DataDog/datadog-agent/comp/core/config" - "github.com/DataDog/datadog-agent/comp/core/status" + statusComponent "github.com/DataDog/datadog-agent/comp/core/status" ddflareextension "github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/def" + status "github.com/DataDog/datadog-agent/comp/otelcol/status/def" apiutil "github.com/DataDog/datadog-agent/pkg/api/util" "github.com/DataDog/datadog-agent/pkg/util/prometheus" ) @@ -24,14 +25,15 @@ import ( //go:embed status_templates var templatesFS embed.FS -// Dependencies defines the dependencies of the status component. -type Dependencies struct { +// Requires defines the dependencies of the status component. +type Requires struct { Config config.Component } // Provides contains components provided by status constructor. type Provides struct { - StatusProvider status.InformationProvider + Comp status.Component + StatusProvider statusComponent.InformationProvider } type statusProvider struct { @@ -61,28 +63,30 @@ type prometheusRuntimeConfig struct { } // NewComponent creates a new status component. -func NewComponent(deps Dependencies) Provides { +func NewComponent(reqs Requires) Provides { + comp := statusProvider{ + Config: reqs.Config, + client: apiutil.GetClient(false), + receiverStatus: map[string]interface{}{ + "spans": 0.0, + "metrics": 0.0, + "logs": 0.0, + "refused_spans": 0.0, + "refused_metrics": 0.0, + "refused_logs": 0.0, + }, + exporterStatus: map[string]interface{}{ + "spans": 0.0, + "metrics": 0.0, + "logs": 0.0, + "failed_spans": 0.0, + "failed_metrics": 0.0, + "failed_logs": 0.0, + }, + } return Provides{ - StatusProvider: status.NewInformationProvider(statusProvider{ - Config: deps.Config, - client: apiutil.GetClient(false), - receiverStatus: map[string]interface{}{ - "spans": 0.0, - "metrics": 0.0, - "logs": 0.0, - "refused_spans": 0.0, - "refused_metrics": 0.0, - "refused_logs": 0.0, - }, - exporterStatus: map[string]interface{}{ - "spans": 0.0, - "metrics": 0.0, - "logs": 0.0, - "failed_spans": 0.0, - "failed_metrics": 0.0, - "failed_logs": 0.0, - }, - }), + Comp: comp, + StatusProvider: statusComponent.NewInformationProvider(comp), } } @@ -214,10 +218,10 @@ func (s statusProvider) JSON(_ bool, stats map[string]interface{}) error { // Text renders the text output func (s statusProvider) Text(_ bool, buffer io.Writer) error { - return status.RenderText(templatesFS, "otelagent.tmpl", buffer, s.getStatusInfo()) + return statusComponent.RenderText(templatesFS, "otelagent.tmpl", buffer, s.getStatusInfo()) } // HTML renders the html output func (s statusProvider) HTML(_ bool, buffer io.Writer) error { - return status.RenderHTML(templatesFS, "otelagentHTML.tmpl", buffer, s.getStatusInfo()) + return statusComponent.RenderHTML(templatesFS, "otelagentHTML.tmpl", buffer, s.getStatusInfo()) } diff --git a/comp/otelcol/status/impl/status_test.go b/comp/otelcol/status/impl/status_test.go index fa8ecc4145ed2..f11cefd7eed97 100644 --- a/comp/otelcol/status/impl/status_test.go +++ b/comp/otelcol/status/impl/status_test.go @@ -7,40 +7,26 @@ package statusimpl import ( "bytes" - "go.uber.org/fx" "testing" "github.com/stretchr/testify/assert" "github.com/DataDog/datadog-agent/comp/core/config" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/comp/core/status" ) -type testDependencies struct { - fx.In - Config config.Component -} - func TestStatusOut(t *testing.T) { - testDeps := fxutil.Test[testDependencies](t, config.MockModule()) - deps := Dependencies{ - Config: testDeps.Config, - } - provides := NewComponent(deps) - - headerProvider := provides.StatusProvider.Provider - tests := []struct { name string - assertFunc func(t *testing.T) + assertFunc func(t *testing.T, headerProvider status.Provider) }{ - {"JSON", func(t *testing.T) { + {"JSON", func(t *testing.T, headerProvider status.Provider) { stats := make(map[string]interface{}) headerProvider.JSON(false, stats) assert.NotEmpty(t, stats) }}, - {"Text", func(t *testing.T) { + {"Text", func(t *testing.T, headerProvider status.Provider) { b := new(bytes.Buffer) err := headerProvider.Text(false, b) @@ -48,7 +34,7 @@ func TestStatusOut(t *testing.T) { assert.NotEmpty(t, b.String()) }}, - {"HTML", func(t *testing.T) { + {"HTML", func(t *testing.T, headerProvider status.Provider) { b := new(bytes.Buffer) err := headerProvider.HTML(false, b) @@ -60,7 +46,11 @@ func TestStatusOut(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - test.assertFunc(t) + provides := NewComponent(Requires{ + Config: config.NewMock(t), + }) + headerProvider := provides.StatusProvider.Provider + test.assertFunc(t, headerProvider) }) } } From 55fb36bb00311c1672d8b7b976bf63cb87594960 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 29 Jan 2025 12:57:02 -0500 Subject: [PATCH 15/23] Add break --- comp/otelcol/status/impl/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index 1673432db858e..b0bc3c8cb4298 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -122,6 +122,7 @@ func getPrometheusURL(extensionResp ddflareextension.Response) (string, error) { if prometheusEndpoint.Host != "" && prometheusEndpoint.Port != 0 { prometheusHost = prometheusEndpoint.Host prometheusPort = prometheusEndpoint.Port + break } } return fmt.Sprintf("http://%v:%d/metrics", prometheusHost, prometheusPort), nil From 9a8d8f1af28f66f123c743454095b19faedaf240 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 29 Jan 2025 14:48:17 -0500 Subject: [PATCH 16/23] Add otel-agent status subcommand --- cmd/otel-agent/command/command.go | 2 + cmd/otel-agent/subcommands/status/command.go | 61 ++++++++++++++++++++ comp/otelcol/status/def/component.go | 2 + comp/otelcol/status/impl/status.go | 10 ++++ 4 files changed, 75 insertions(+) create mode 100644 cmd/otel-agent/subcommands/status/command.go diff --git a/cmd/otel-agent/command/command.go b/cmd/otel-agent/command/command.go index 2a4ea3d6bac0a..6a2c95c61f71d 100644 --- a/cmd/otel-agent/command/command.go +++ b/cmd/otel-agent/command/command.go @@ -20,6 +20,7 @@ import ( "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/run" + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands/status" "github.com/DataDog/datadog-agent/pkg/cli/subcommands/version" "go.opentelemetry.io/collector/featuregate" ) @@ -49,6 +50,7 @@ func makeCommands(globalParams *subcommands.GlobalParams) *cobra.Command { commands := []*cobra.Command{ run.MakeCommand(globalConfGetter), version.MakeCommand("otel-agent"), + status.MakeCommand(globalConfGetter), } otelAgentCmd := *commands[0] // root cmd is `run()`; indexed at 0 diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go new file mode 100644 index 0000000000000..04c56fabec2b6 --- /dev/null +++ b/cmd/otel-agent/subcommands/status/command.go @@ -0,0 +1,61 @@ +package status + +import ( + "context" + agentConfig "github.com/DataDog/datadog-agent/cmd/otel-agent/config" + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + coreconfig "github.com/DataDog/datadog-agent/comp/core/config" + status "github.com/DataDog/datadog-agent/comp/otelcol/status/def" + otelagentStatusfx "github.com/DataDog/datadog-agent/comp/otelcol/status/fx" + pkgconfigenv "github.com/DataDog/datadog-agent/pkg/config/env" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" + "go.uber.org/fx" + "os" +) + +type dependencies struct { + fx.In + + Status status.Component +} + +// MakeCommand returns a `version` command to be used by agent binaries. +func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Command { + cmd := &cobra.Command{ + Use: "status", + Short: "Print the current status", + Long: ``, + RunE: func(*cobra.Command, []string) error { + globalParams := globalConfGetter() + acfg, err := agentConfig.NewConfigComponent(context.Background(), globalParams.CoreConfPath, globalParams.ConfPaths) + if err != nil { + return err + } + uris := append(globalParams.ConfPaths, globalParams.Sets...) + return fxutil.OneShot( + runStatus, + fx.Supply(uris), + fx.Provide(func() (coreconfig.Component, error) { + pkgconfigenv.DetectFeatures(acfg) + return acfg, nil + }), + otelagentStatusfx.Module(), + ) + }, + } + + return cmd +} + +func runStatus(deps dependencies) error { + statusText, err := deps.Status.GetStatus() + if err != nil { + return err + } + _, err = os.Stdout.Write([]byte(statusText)) + if err != nil { + return err + } + return nil +} diff --git a/comp/otelcol/status/def/component.go b/comp/otelcol/status/def/component.go index deaff5f0518f8..a4f6994638311 100644 --- a/comp/otelcol/status/def/component.go +++ b/comp/otelcol/status/def/component.go @@ -10,4 +10,6 @@ package status // Component is the status interface. type Component interface { + // GetStatus returns the OTel Agent status in string form + GetStatus() (string, error) } diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index b0bc3c8cb4298..b166cb1a52de4 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -7,6 +7,7 @@ package statusimpl import ( + "bytes" "embed" "encoding/json" "fmt" @@ -100,6 +101,15 @@ func (s statusProvider) Section() string { return "OTel Agent" } +func (s statusProvider) GetStatus() (string, error) { + buf := new(bytes.Buffer) + err := s.Text(false, buf) + if err != nil { + return "", err + } + return buf.String(), nil +} + func (s statusProvider) getStatusInfo() map[string]interface{} { statusInfo := make(map[string]interface{}) From c81680d1bd730c4371522daee8db92c0128ba196 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 29 Jan 2025 15:35:55 -0500 Subject: [PATCH 17/23] Fix lint --- cmd/otel-agent/subcommands/status/command.go | 6 +++ .../subcommands/status/command_test.go | 43 +++++++++++++++++++ .../subcommands/status/test_config.yaml | 21 +++++++++ 3 files changed, 70 insertions(+) create mode 100644 cmd/otel-agent/subcommands/status/command_test.go create mode 100644 cmd/otel-agent/subcommands/status/test_config.yaml diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go index 04c56fabec2b6..fac2dda71efec 100644 --- a/cmd/otel-agent/subcommands/status/command.go +++ b/cmd/otel-agent/subcommands/status/command.go @@ -1,3 +1,9 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package status implements the core status component information provider interface package status import ( diff --git a/cmd/otel-agent/subcommands/status/command_test.go b/cmd/otel-agent/subcommands/status/command_test.go new file mode 100644 index 0000000000000..5f22796dc37d6 --- /dev/null +++ b/cmd/otel-agent/subcommands/status/command_test.go @@ -0,0 +1,43 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package status + +import ( + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + "os" + "path" + "testing" +) + +func newGlobalParamsTest(t *testing.T) *subcommands.GlobalParams { + // Because getSharedFxOption uses fx.Invoke, demultiplexer component is built + // which lead to build: + // - config.Component which requires a valid datadog.yaml + // - hostname.Component which requires a valid hostname + config := path.Join(t.TempDir(), "datadog.yaml") + err := os.WriteFile(config, []byte("hostname: test"), 0644) + require.NoError(t, err) + + return &subcommands.GlobalParams{ + CoreConfPath: config, + ConfPaths: []string{"test_config.yaml"}, + } +} + +func TestStatusCommand(t *testing.T) { + globalConfGetter := func() *subcommands.GlobalParams { + return newGlobalParamsTest(t) + } + fxutil.TestOneShotSubcommand(t, + []*cobra.Command{MakeCommand(globalConfGetter)}, + []string{"status"}, + runStatus, + func() {}, + ) +} diff --git a/cmd/otel-agent/subcommands/status/test_config.yaml b/cmd/otel-agent/subcommands/status/test_config.yaml new file mode 100644 index 0000000000000..d24e4565e93cf --- /dev/null +++ b/cmd/otel-agent/subcommands/status/test_config.yaml @@ -0,0 +1,21 @@ +receivers: + otlp: + protocols: + http: + endpoint: "localhost:4318" + grpc: + endpoint: "localhost:4317" + +exporters: + datadog: + api: + key: "abc" + +service: + pipelines: + traces: + receivers: [otlp] + exporters: [datadog] + telemetry: + metrics: + address: 127.0.0.1:8888 From 37b6bce1a7f58dd2b809805e49854b475c1a10b1 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 29 Jan 2025 15:39:05 -0500 Subject: [PATCH 18/23] Fix lint --- comp/otelcol/status/impl/status.go | 1 + 1 file changed, 1 insertion(+) diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index b166cb1a52de4..255f5d67fbc0a 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -101,6 +101,7 @@ func (s statusProvider) Section() string { return "OTel Agent" } +// GetStatus returns the status func (s statusProvider) GetStatus() (string, error) { buf := new(bytes.Buffer) err := s.Text(false, buf) From a43ecee55bd4f523ce7361010fd13bf4849aa26b Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Mon, 3 Feb 2025 14:49:32 -0500 Subject: [PATCH 19/23] Clean up PR --- cmd/otel-agent/subcommands/status/command.go | 20 ++++++++++++------- .../subcommands/status/command_test.go | 17 +++++++--------- comp/otelcol/status/impl/status.go | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go index fac2dda71efec..aa4b42a25c25f 100644 --- a/cmd/otel-agent/subcommands/status/command.go +++ b/cmd/otel-agent/subcommands/status/command.go @@ -1,13 +1,18 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. +// Copyright 2025-present Datadog, Inc. // Package status implements the core status component information provider interface package status import ( "context" + "os" + + "github.com/spf13/cobra" + "go.uber.org/fx" + agentConfig "github.com/DataDog/datadog-agent/cmd/otel-agent/config" "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" coreconfig "github.com/DataDog/datadog-agent/comp/core/config" @@ -15,9 +20,6 @@ import ( otelagentStatusfx "github.com/DataDog/datadog-agent/comp/otelcol/status/fx" pkgconfigenv "github.com/DataDog/datadog-agent/pkg/config/env" "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "github.com/spf13/cobra" - "go.uber.org/fx" - "os" ) type dependencies struct { @@ -26,6 +28,8 @@ type dependencies struct { Status status.Component } +const headerText = "==========\nOTel Agent\n==========\n" + // MakeCommand returns a `version` command to be used by agent binaries. func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Command { cmd := &cobra.Command{ @@ -35,7 +39,7 @@ func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Comma RunE: func(*cobra.Command, []string) error { globalParams := globalConfGetter() acfg, err := agentConfig.NewConfigComponent(context.Background(), globalParams.CoreConfPath, globalParams.ConfPaths) - if err != nil { + if err != nil && err != agentConfig.ErrNoDDExporter { return err } uris := append(globalParams.ConfPaths, globalParams.Sets...) @@ -43,7 +47,9 @@ func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Comma runStatus, fx.Supply(uris), fx.Provide(func() (coreconfig.Component, error) { - pkgconfigenv.DetectFeatures(acfg) + if err != agentConfig.ErrNoDDExporter { + pkgconfigenv.DetectFeatures(acfg) + } return acfg, nil }), otelagentStatusfx.Module(), @@ -59,7 +65,7 @@ func runStatus(deps dependencies) error { if err != nil { return err } - _, err = os.Stdout.Write([]byte(statusText)) + _, err = os.Stdout.Write([]byte(headerText + statusText)) if err != nil { return err } diff --git a/cmd/otel-agent/subcommands/status/command_test.go b/cmd/otel-agent/subcommands/status/command_test.go index 5f22796dc37d6..d8c3b6e3098cd 100644 --- a/cmd/otel-agent/subcommands/status/command_test.go +++ b/cmd/otel-agent/subcommands/status/command_test.go @@ -1,29 +1,26 @@ // Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. // This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. +// Copyright 2025-present Datadog, Inc. package status import ( - "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" - "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "github.com/spf13/cobra" - "github.com/stretchr/testify/require" "os" "path" "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/require" + + "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) func newGlobalParamsTest(t *testing.T) *subcommands.GlobalParams { - // Because getSharedFxOption uses fx.Invoke, demultiplexer component is built - // which lead to build: - // - config.Component which requires a valid datadog.yaml - // - hostname.Component which requires a valid hostname config := path.Join(t.TempDir(), "datadog.yaml") err := os.WriteFile(config, []byte("hostname: test"), 0644) require.NoError(t, err) - return &subcommands.GlobalParams{ CoreConfPath: config, ConfPaths: []string{"test_config.yaml"}, diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index 255f5d67fbc0a..d0bb9bb9457e2 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -101,7 +101,7 @@ func (s statusProvider) Section() string { return "OTel Agent" } -// GetStatus returns the status +// GetStatus returns the OTel Agent status in string form func (s statusProvider) GetStatus() (string, error) { buf := new(bytes.Buffer) err := s.Text(false, buf) From 6d64d8dc3ca223ea3199c24c880f93bdfb93fb18 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Thu, 6 Feb 2025 14:52:25 -0500 Subject: [PATCH 20/23] Add authtoken component and fix fx --- cmd/otel-agent/subcommands/status/command.go | 31 ++++++++++---------- comp/otelcol/status/impl/status.go | 16 +++++++--- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/cmd/otel-agent/subcommands/status/command.go b/cmd/otel-agent/subcommands/status/command.go index aa4b42a25c25f..f60d7191ba98b 100644 --- a/cmd/otel-agent/subcommands/status/command.go +++ b/cmd/otel-agent/subcommands/status/command.go @@ -7,19 +7,22 @@ package status import ( - "context" "os" "github.com/spf13/cobra" "go.uber.org/fx" - agentConfig "github.com/DataDog/datadog-agent/cmd/otel-agent/config" "github.com/DataDog/datadog-agent/cmd/otel-agent/subcommands" + "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" coreconfig "github.com/DataDog/datadog-agent/comp/core/config" + log "github.com/DataDog/datadog-agent/comp/core/log/def" + logfx "github.com/DataDog/datadog-agent/comp/core/log/fx" + "github.com/DataDog/datadog-agent/comp/core/secrets" + "github.com/DataDog/datadog-agent/comp/core/secrets/secretsimpl" status "github.com/DataDog/datadog-agent/comp/otelcol/status/def" otelagentStatusfx "github.com/DataDog/datadog-agent/comp/otelcol/status/fx" - pkgconfigenv "github.com/DataDog/datadog-agent/pkg/config/env" "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/option" ) type dependencies struct { @@ -30,7 +33,7 @@ type dependencies struct { const headerText = "==========\nOTel Agent\n==========\n" -// MakeCommand returns a `version` command to be used by agent binaries. +// MakeCommand returns a `status` command to be used by agent binaries. func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Command { cmd := &cobra.Command{ Use: "status", @@ -38,20 +41,16 @@ func MakeCommand(globalConfGetter func() *subcommands.GlobalParams) *cobra.Comma Long: ``, RunE: func(*cobra.Command, []string) error { globalParams := globalConfGetter() - acfg, err := agentConfig.NewConfigComponent(context.Background(), globalParams.CoreConfPath, globalParams.ConfPaths) - if err != nil && err != agentConfig.ErrNoDDExporter { - return err - } - uris := append(globalParams.ConfPaths, globalParams.Sets...) return fxutil.OneShot( runStatus, - fx.Supply(uris), - fx.Provide(func() (coreconfig.Component, error) { - if err != agentConfig.ErrNoDDExporter { - pkgconfigenv.DetectFeatures(acfg) - } - return acfg, nil - }), + fx.Supply(coreconfig.NewAgentParams(globalParams.CoreConfPath, coreconfig.WithExtraConfFiles(globalParams.ConfPaths))), + fx.Supply(option.None[secrets.Component]()), + fx.Supply(secrets.NewEnabledParams()), + fx.Supply(log.ForOneShot(globalParams.LoggerName, "off", true)), + coreconfig.Module(), + secretsimpl.Module(), + logfx.Module(), + fetchonlyimpl.Module(), otelagentStatusfx.Module(), ) }, diff --git a/comp/otelcol/status/impl/status.go b/comp/otelcol/status/impl/status.go index d0bb9bb9457e2..5b174114096d1 100644 --- a/comp/otelcol/status/impl/status.go +++ b/comp/otelcol/status/impl/status.go @@ -15,6 +15,7 @@ import ( "io" "net/http" + "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/core/config" statusComponent "github.com/DataDog/datadog-agent/comp/core/status" ddflareextension "github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/def" @@ -28,7 +29,8 @@ var templatesFS embed.FS // Requires defines the dependencies of the status component. type Requires struct { - Config config.Component + Config config.Component + Authtoken authtoken.Component } // Provides contains components provided by status constructor. @@ -40,6 +42,7 @@ type Provides struct { type statusProvider struct { Config config.Component client *http.Client + authToken authtoken.Component receiverStatus map[string]interface{} exporterStatus map[string]interface{} } @@ -66,8 +69,9 @@ type prometheusRuntimeConfig struct { // NewComponent creates a new status component. func NewComponent(reqs Requires) Provides { comp := statusProvider{ - Config: reqs.Config, - client: apiutil.GetClient(false), + Config: reqs.Config, + client: apiutil.GetClient(false), + authToken: reqs.Authtoken, receiverStatus: map[string]interface{}{ "spans": 0.0, "metrics": 0.0, @@ -183,7 +187,11 @@ func (s statusProvider) populatePrometheusStatus(prometheusURL string) error { func (s statusProvider) populateStatus() map[string]interface{} { extensionURL := s.Config.GetString("otelcollector.extension_url") - resp, err := apiutil.DoGet(s.client, extensionURL, apiutil.CloseConnection) + options := apiutil.ReqOptions{ + Conn: apiutil.CloseConnection, + Authtoken: s.authToken.Get(), + } + resp, err := apiutil.DoGetWithOptions(s.client, extensionURL, &options) if err != nil { return map[string]interface{}{ "url": extensionURL, From ea57d6cfb4b915936c05aa52ac4104ca1406cd25 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Thu, 6 Feb 2025 15:10:49 -0500 Subject: [PATCH 21/23] Fix test --- comp/otelcol/status/impl/status_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/comp/otelcol/status/impl/status_test.go b/comp/otelcol/status/impl/status_test.go index f11cefd7eed97..de379c8f60e32 100644 --- a/comp/otelcol/status/impl/status_test.go +++ b/comp/otelcol/status/impl/status_test.go @@ -7,6 +7,8 @@ package statusimpl import ( "bytes" + "github.com/DataDog/datadog-agent/comp/api/authtoken" + "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "testing" "github.com/stretchr/testify/assert" @@ -47,7 +49,8 @@ func TestStatusOut(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { provides := NewComponent(Requires{ - Config: config.NewMock(t), + Config: config.NewMock(t), + Authtoken: authtoken.Component(&fetchonlyimpl.MockFetchOnly{}), }) headerProvider := provides.StatusProvider.Provider test.assertFunc(t, headerProvider) From eb256081172aa665341c7a9000de677ac905cd72 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Thu, 6 Feb 2025 19:22:25 -0500 Subject: [PATCH 22/23] Add e2e tests --- .../tests/otel/otel-agent/minimal_test.go | 8 ++++ test/new-e2e/tests/otel/utils/config_utils.go | 40 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/test/new-e2e/tests/otel/otel-agent/minimal_test.go b/test/new-e2e/tests/otel/otel-agent/minimal_test.go index ee33ec9d6cbac..1e0c5bc8630cb 100644 --- a/test/new-e2e/tests/otel/otel-agent/minimal_test.go +++ b/test/new-e2e/tests/otel/otel-agent/minimal_test.go @@ -92,3 +92,11 @@ func (s *minimalTestSuite) TestOTelFlareFiles() { func (s *minimalTestSuite) TestOTelRemoteConfigPayload() { utils.TestOTelRemoteConfigPayload(s, minimalProvidedConfig, minimalFullConfig) } + +func (s *minimalTestSuite) TestCoreAgentStatus() { + utils.TestCoreAgentStatusCmd(s) +} + +func (s *minimalTestSuite) TestOTelAgentStatus() { + utils.TestOTelAgentStatusCmd(s) +} diff --git a/test/new-e2e/tests/otel/utils/config_utils.go b/test/new-e2e/tests/otel/utils/config_utils.go index 3d10d5267bc2e..c054a966e31b1 100644 --- a/test/new-e2e/tests/otel/utils/config_utils.go +++ b/test/new-e2e/tests/otel/utils/config_utils.go @@ -227,3 +227,43 @@ func validateConfigs(t *testing.T, expectedCfg string, actualCfg string) { assert.YAMLEq(t, expectedCfg, actualCfg) } + +// TestCoreAgentStatusCmd tests the core agent status command contains the OTel Agent status as expected +func TestCoreAgentStatusCmd(s OTelTestSuite) { + err := s.Env().FakeIntake.Client().FlushServerAndResetAggregators() + require.NoError(s.T(), err) + agent := getAgentPod(s) + + s.T().Log("Calling status command in core agent") + stdout, stderr, err := s.Env().KubernetesCluster.KubernetesClient.PodExec("datadog", agent.Name, "agent", []string{"agent", "status", "otel agent"}) + require.NoError(s.T(), err, "Failed to execute config") + require.Empty(s.T(), stderr) + validateStatus(s.T(), stdout) +} + +// TestOTelAgentStatusCmd tests the OTel Agent status subcommand returns as expected +func TestOTelAgentStatusCmd(s OTelTestSuite) { + err := s.Env().FakeIntake.Client().FlushServerAndResetAggregators() + require.NoError(s.T(), err) + agent := getAgentPod(s) + + s.T().Log("Calling status command in otel agent") + stdout, stderr, err := s.Env().KubernetesCluster.KubernetesClient.PodExec("datadog", agent.Name, "otel-agent", []string{"otel-agent", "status"}) + require.NoError(s.T(), err, "Failed to execute config") + require.Empty(s.T(), stderr) + validateStatus(s.T(), stdout) +} + +func validateStatus(t *testing.T, status string) { + require.NotNil(t, status) + require.Contains(t, status, "OTel Agent") + require.Contains(t, status, "Status: Running") + require.Contains(t, status, "Agent Version:") + require.Contains(t, status, "Collector Version:") + require.Contains(t, status, "Spans Accepted:") + require.Contains(t, status, "Metric Points Accepted:") + require.Contains(t, status, "Log Records Accepted:") + require.Contains(t, status, "Spans Sent:") + require.Contains(t, status, "Metric Points Sent:") + require.Contains(t, status, "Log Records Sent:") +} From 9c4ba04558deee740658b7353f447424b899b292 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Thu, 6 Feb 2025 19:30:35 -0500 Subject: [PATCH 23/23] Fix imports --- comp/otelcol/status/impl/status_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/comp/otelcol/status/impl/status_test.go b/comp/otelcol/status/impl/status_test.go index de379c8f60e32..e55f3a71fbb32 100644 --- a/comp/otelcol/status/impl/status_test.go +++ b/comp/otelcol/status/impl/status_test.go @@ -7,12 +7,12 @@ package statusimpl import ( "bytes" - "github.com/DataDog/datadog-agent/comp/api/authtoken" - "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "testing" "github.com/stretchr/testify/assert" + "github.com/DataDog/datadog-agent/comp/api/authtoken" + "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/status" )