From 89cc97e64f8c81ad498603d6b4de5d4c969e289d Mon Sep 17 00:00:00 2001 From: Gustavo Caso Date: Mon, 14 Oct 2024 18:15:31 +0200 Subject: [PATCH] create checks agent 59M --- cmd/checks-agent/command/command.go | 36 ++++ cmd/checks-agent/main.go | 27 +++ cmd/checks-agent/subcommands/start/command.go | 182 ++++++++++++++++++ .../subcommands/version/command.go | 58 ++++++ pkg/util/flavor/flavor.go | 3 + tasks/agent.py | 2 + tasks/build_tags.py | 8 + tasks/flavor.py | 4 + 8 files changed, 320 insertions(+) create mode 100644 cmd/checks-agent/command/command.go create mode 100644 cmd/checks-agent/main.go create mode 100644 cmd/checks-agent/subcommands/start/command.go create mode 100644 cmd/checks-agent/subcommands/version/command.go diff --git a/cmd/checks-agent/command/command.go b/cmd/checks-agent/command/command.go new file mode 100644 index 0000000000000..303907fa660d4 --- /dev/null +++ b/cmd/checks-agent/command/command.go @@ -0,0 +1,36 @@ +// 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. + +//nolint:revive // TODO(AML) Fix revive linter +package command + +import ( + //nolint:revive // TODO(AML) Fix revive linter + _ "expvar" + _ "net/http/pprof" + + "github.com/spf13/cobra" + + "github.com/DataDog/datadog-agent/cmd/checks-agent/subcommands/start" + "github.com/DataDog/datadog-agent/cmd/checks-agent/subcommands/version" +) + +func MakeRootCommand() *cobra.Command { + // checksAgentCmd is the root command + checksAgentCmd := &cobra.Command{ + Use: "checks-agent [command]", + Short: "Checks Agent at your service.", + } + + for _, cmd := range makeCommands() { + checksAgentCmd.AddCommand(cmd) + } + + return checksAgentCmd +} + +func makeCommands() []*cobra.Command { + return []*cobra.Command{start.MakeCommand(), version.MakeCommand("Checks Agent")} +} diff --git a/cmd/checks-agent/main.go b/cmd/checks-agent/main.go new file mode 100644 index 0000000000000..abd778534575f --- /dev/null +++ b/cmd/checks-agent/main.go @@ -0,0 +1,27 @@ +// 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. + +//go:build !windows + +package main + +import ( + _ "net/http/pprof" + "os" + + "log" + + "github.com/DataDog/datadog-agent/cmd/checks-agent/command" + "github.com/DataDog/datadog-agent/pkg/util/flavor" +) + +func main() { + flavor.SetFlavor(flavor.ChecksAgent) + + if err := command.MakeRootCommand().Execute(); err != nil { + log.Println(err) + os.Exit(-1) + } +} diff --git a/cmd/checks-agent/subcommands/start/command.go b/cmd/checks-agent/subcommands/start/command.go new file mode 100644 index 0000000000000..8fe9fa2b2c52a --- /dev/null +++ b/cmd/checks-agent/subcommands/start/command.go @@ -0,0 +1,182 @@ +// 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. + +//nolint:revive // TODO Fix revive linter +package start + +import ( + "context" + "os" + "os/signal" + "syscall" + + "github.com/spf13/cobra" + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/comp/aggregator/diagnosesendermanager" + "github.com/DataDog/datadog-agent/comp/aggregator/diagnosesendermanager/diagnosesendermanagerimpl" + "github.com/DataDog/datadog-agent/comp/collector/collector" + "github.com/DataDog/datadog-agent/comp/collector/collector/collectorimpl" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/hostname/hostnameimpl" + 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" + noopTelemetry "github.com/DataDog/datadog-agent/comp/core/telemetry/noopsimpl" + integrations "github.com/DataDog/datadog-agent/comp/logs/integrations/def" + "github.com/DataDog/datadog-agent/comp/serializer/compression/compressionimpl" + "github.com/DataDog/datadog-agent/pkg/aggregator/sender" + pkgcollector "github.com/DataDog/datadog-agent/pkg/collector" + "github.com/DataDog/datadog-agent/pkg/serializer" + "github.com/DataDog/datadog-agent/pkg/status/health" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/optional" +) + +type CLIParams struct { + confPath string +} + +// MakeCommand returns the start subcommand for the 'dogstatsd' command. +func MakeCommand() *cobra.Command { + cliParams := &CLIParams{} + startCmd := &cobra.Command{ + Use: "start", + Short: "Start Checks Agent", + Long: `Runs Checks Agent in the foreground`, + RunE: func(*cobra.Command, []string) error { + return RunChecksAgent(cliParams, "", start) + }, + } + + // local flags + startCmd.PersistentFlags().StringVarP(&cliParams.confPath, "cfgpath", "c", "", "path to directory containing datadog.yaml") + + return startCmd +} + +func RunChecksAgent(cliParams *CLIParams, defaultConfPath string, fct interface{}) error { + return fxutil.OneShot(fct, + fx.Supply(cliParams), + + // Configuration + fx.Supply(config.NewParams( + defaultConfPath, + config.WithConfFilePath(cliParams.confPath), + config.WithConfigMissingOK(true), + config.WithConfigName("datadog")), + ), + config.Module(), + + // Logging + logfx.Module(), + fx.Supply(log.ForDaemon("CA", "log_file", "/var/log/datadog/checks-agent.log")), + + // Secrets management + fx.Provide(func(comp secrets.Component) optional.Option[secrets.Component] { + return optional.NewOption[secrets.Component](comp) + }), + fx.Supply(secrets.NewEnabledParams()), + secretsimpl.Module(), + noopTelemetry.Module(), + collectorimpl.Module(), + fx.Provide(func() optional.Option[serializer.MetricSerializer] { + return optional.NewNoneOption[serializer.MetricSerializer]() + }), + diagnosesendermanagerimpl.Module(), + fx.Provide(func(diagnoseSenderManager diagnosesendermanager.Component) (sender.SenderManager, error) { + return diagnoseSenderManager.LazyGetSenderManager() + }), + compressionimpl.Module(), + hostnameimpl.Module(), + ) +} + +func start( + cliParams *CLIParams, + config config.Component, + log log.Component, + _ diagnosesendermanager.Component, + collector collector.Component, + sender sender.SenderManager, +) error { + + // Main context passed to components + ctx, cancel := context.WithCancel(context.Background()) + + defer StopAgent(cancel, log) + + // TODO: figure out how to initial.ize checks context + // check.InitializeInventoryChecksContext(invChecks) + + pkgcollector.InitCheckScheduler(optional.NewOption(collector), sender, optional.NewNoneOption[integrations.Component]()) + + stopCh := make(chan struct{}) + go handleSignals(stopCh, log) + + err := Run(ctx, cliParams, config, log) + if err != nil { + return err + } + + // Block here until we receive a stop signal + <-stopCh + + return nil +} + +// Run starts the Logs agent server +func Run(ctx context.Context, cliParams *CLIParams, config config.Component, log log.Component) (err error) { + if len(cliParams.confPath) == 0 { + log.Infof("Config will be read from env variables") + } + + if !config.IsSet("api_key") { + err = log.Critical("no API key configured, exiting") + return + } + + return nil +} + +// handleSignals handles OS signals, and sends a message on stopCh when an interrupt +// signal is received. +func handleSignals(stopCh chan struct{}, log log.Component) { + // Setup a channel to catch OS signals + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGPIPE) + + // Block here until we receive the interrupt signal + for signo := range signalCh { + switch signo { + case syscall.SIGPIPE: + // By default systemd redirects the stdout to journald. When journald is stopped or crashes we receive a SIGPIPE signal. + // Go ignores SIGPIPE signals unless it is when stdout or stdout is closed, in this case the agent is stopped. + // We never want dogstatsd to stop upon receiving SIGPIPE, so we intercept the SIGPIPE signals and just discard them. + default: + log.Infof("Received signal '%s', shutting down...", signo) + stopCh <- struct{}{} + return + } + } +} + +func StopAgent(cancel context.CancelFunc, log log.Component) { + // retrieve the agent health before stopping the components + // GetReadyNonBlocking has a 100ms timeout to avoid blocking + health, err := health.GetReadyNonBlocking() + if err != nil { + log.Warnf("Logs Agent health unknown: %s", err) + } else if len(health.Unhealthy) > 0 { + log.Warnf("Some components were unhealthy: %v", health.Unhealthy) + } + + // gracefully shut down any component + cancel() + + log.Info("See ya!") + log.Flush() +} diff --git a/cmd/checks-agent/subcommands/version/command.go b/cmd/checks-agent/subcommands/version/command.go new file mode 100644 index 0000000000000..0d9c318b92c12 --- /dev/null +++ b/cmd/checks-agent/subcommands/version/command.go @@ -0,0 +1,58 @@ +// 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. + +//nolint:revive // TODO Fix revive linter +package version + +import ( + "fmt" + "os" + "runtime" + + "github.com/fatih/color" + "github.com/spf13/cobra" + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/version" +) + +type params struct { + binary string +} + +// MakeCommand returns a `version` command to be used by agent binaries. +func MakeCommand(binary string) *cobra.Command { + cmd := &cobra.Command{ + Use: "version", + Short: "Print the version info", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return fxutil.OneShot(run, fx.Supply(¶ms{binary})) + }, + } + + return cmd +} + +func run(params *params) error { + av, _ := version.Agent() + meta := "" + + if av.Meta != "" { + meta = fmt.Sprintf("- Meta: %s ", color.YellowString(av.Meta)) + } + + fmt.Fprintf(os.Stdout, + "%s %s %s- Commit: %s - Go version: %s\n", + params.binary, + av.GetNumberAndPre(), + meta, + version.Commit, + runtime.Version(), + ) + + return nil +} diff --git a/pkg/util/flavor/flavor.go b/pkg/util/flavor/flavor.go index 902c05a843a05..efe5e10c97db6 100644 --- a/pkg/util/flavor/flavor.go +++ b/pkg/util/flavor/flavor.go @@ -33,6 +33,8 @@ const ( KernelAgent = "kernel_agent" // LogsAgent is the Logs Agent flavor LogsAgent = "logs_agent" + // ChecksAgent is the Checks Agent flavor + ChecksAgent = "checks_agent" ) var agentFlavors = map[string]string{ @@ -48,6 +50,7 @@ var agentFlavors = map[string]string{ OTelAgent: "OpenTelemetry Collector", KernelAgent: "Kernel Agent", LogsAgent: "Logs Agent", + ChecksAgent: "Checks Agent", } const unknownAgent = "Unknown Agent" diff --git a/tasks/agent.py b/tasks/agent.py index 74db882fd6649..feb64867fbc4a 100644 --- a/tasks/agent.py +++ b/tasks/agent.py @@ -221,6 +221,8 @@ def build( command_flavor = "kernel-agent" if flavor.is_logs(): command_flavor = "logs-agent" + if flavor.is_checks(): + command_flavor = "checks-agent" args = { "go_mod": go_mod, diff --git a/tasks/build_tags.py b/tasks/build_tags.py index 4062e4476ee87..d8698ed20f3dc 100644 --- a/tasks/build_tags.py +++ b/tasks/build_tags.py @@ -118,6 +118,9 @@ # LOGS_AGENT_TAGS lists the tags needed when building the Logs agent LOGS_AGENT_TAGS = {"docker", "kubelet"} +# CHECKS_AGENT_TAGS lists the tags needed when building the Logs agent +CHECKS_AGENT_TAGS = {"zlib", "zstd", "python"} + # PROCESS_AGENT_TAGS lists the tags necessary to build the process-agent PROCESS_AGENT_TAGS = AGENT_TAGS.union({"fargateprocess"}).difference({"otlp", "python", "trivy"}) @@ -230,6 +233,11 @@ "lint": LOGS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), "unit-tests": LOGS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), }, + AgentFlavor.checks: { + "agent": CHECKS_AGENT_TAGS, + "lint": CHECKS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), + "unit-tests": CHECKS_AGENT_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), + }, } diff --git a/tasks/flavor.py b/tasks/flavor.py index bda09d83ad4ff..a4f8f5d2608ed 100644 --- a/tasks/flavor.py +++ b/tasks/flavor.py @@ -10,6 +10,7 @@ class AgentFlavor(enum.Enum): ot = 5 ka = 6 logs = 7 + checks = 8 def is_iot(self): return self == type(self).iot @@ -22,3 +23,6 @@ def is_ka(self): def is_logs(self): return self == type(self).logs + + def is_checks(self): + return self == type(self).checks