diff --git a/cmd/cluster-agent-cloudfoundry/app/app.go b/cmd/cluster-agent-cloudfoundry/app/app.go index a1c5d2865d858..f9204d34d0cef 100644 --- a/cmd/cluster-agent-cloudfoundry/app/app.go +++ b/cmd/cluster-agent-cloudfoundry/app/app.go @@ -24,9 +24,10 @@ import ( "github.com/DataDog/datadog-agent/cmd/agent/common" "github.com/DataDog/datadog-agent/cmd/cluster-agent/api" dcav1 "github.com/DataDog/datadog-agent/cmd/cluster-agent/api/v1" - "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands" "github.com/DataDog/datadog-agent/pkg/aggregator" "github.com/DataDog/datadog-agent/pkg/api/healthprobe" + clustercheckscmd "github.com/DataDog/datadog-agent/pkg/cli/subcommands/clusterchecks" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/dcaconfigcheck" "github.com/DataDog/datadog-agent/pkg/clusteragent" "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks" "github.com/DataDog/datadog-agent/pkg/collector" @@ -98,8 +99,18 @@ func init() { // attach the commands to the root ClusterAgentCmd.AddCommand(runCmd) ClusterAgentCmd.AddCommand(versionCmd) - ClusterAgentCmd.AddCommand(commands.GetClusterChecksCobraCmd(&flagNoColor, &confPath, loggerName)) - ClusterAgentCmd.AddCommand(commands.GetConfigCheckCobraCmd(&flagNoColor, &confPath, loggerName)) + + ClusterAgentCmd.AddCommand(clustercheckscmd.MakeCommand(func() clustercheckscmd.GlobalParams { + return clustercheckscmd.GlobalParams{ + ConfFilePath: confPath, + } + })) + + ClusterAgentCmd.AddCommand(dcaconfigcheck.MakeCommand(func() dcaconfigcheck.GlobalParams { + return dcaconfigcheck.GlobalParams{ + ConfFilePath: confPath, + } + })) ClusterAgentCmd.PersistentFlags().StringVarP(&confPath, "cfgpath", "c", "", "path to directory containing datadog.yaml") ClusterAgentCmd.PersistentFlags().BoolVarP(&flagNoColor, "no-color", "n", false, "disable color output") diff --git a/cmd/cluster-agent/app/check.go b/cmd/cluster-agent/app/check.go deleted file mode 100644 index 5a19319bad99c..0000000000000 --- a/cmd/cluster-agent/app/check.go +++ /dev/null @@ -1,17 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands/check" -) - -func init() { - ClusterAgentCmd.AddCommand(check.Check(loggerName, &confPath, &flagNoColor)) -} diff --git a/cmd/cluster-agent/app/clusterchecks.go b/cmd/cluster-agent/app/clusterchecks.go deleted file mode 100644 index 00e52ece4297d..0000000000000 --- a/cmd/cluster-agent/app/clusterchecks.go +++ /dev/null @@ -1,18 +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. - -//go:build kubeapiserver && clusterchecks -// +build kubeapiserver,clusterchecks - -package app - -import "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands" - -func init() { - clusterChecksCmd := commands.GetClusterChecksCobraCmd(&flagNoColor, &confPath, loggerName) - clusterChecksCmd.AddCommand(commands.RebalanceClusterChecksCobraCmd(&flagNoColor, &confPath, loggerName)) - - ClusterAgentCmd.AddCommand(clusterChecksCmd) -} diff --git a/cmd/cluster-agent/app/config.go b/cmd/cluster-agent/app/config.go deleted file mode 100644 index fb8679e888fff..0000000000000 --- a/cmd/cluster-agent/app/config.go +++ /dev/null @@ -1,81 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "fmt" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - cmdconfig "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands/config" - "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" - commonsettings "github.com/DataDog/datadog-agent/pkg/config/settings" - settingshttp "github.com/DataDog/datadog-agent/pkg/config/settings/http" - - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -func init() { - ClusterAgentCmd.AddCommand(cmdconfig.Config(getSettingsClient)) -} - -func setupConfig() error { - if flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - return util.SetAuthToken() -} - -func getSettingsClient(_ *cobra.Command, _ []string) (commonsettings.Client, error) { - err := setupConfig() - if err != nil { - return nil, err - } - - c := util.GetClient(false) - apiConfigURL := fmt.Sprintf("https://localhost:%v/config", config.Datadog.GetInt("cluster_agent.cmd_port")) - - return settingshttp.NewClient(c, apiConfigURL, "datadog-cluster-agent"), nil -} - -// initRuntimeSettings builds the map of runtime Cluster Agent settings configurable at runtime. -func initRuntimeSettings() error { - if err := commonsettings.RegisterRuntimeSetting(commonsettings.LogLevelRuntimeSetting{}); err != nil { - return err - } - - if err := commonsettings.RegisterRuntimeSetting(commonsettings.RuntimeMutexProfileFraction("runtime_mutex_profile_fraction")); err != nil { - return err - } - - if err := commonsettings.RegisterRuntimeSetting(commonsettings.RuntimeBlockProfileRate("runtime_block_profile_rate")); err != nil { - return err - } - - if err := commonsettings.RegisterRuntimeSetting(commonsettings.ProfilingGoroutines("internal_profiling_goroutines")); err != nil { - return err - } - - return commonsettings.RegisterRuntimeSetting(commonsettings.ProfilingRuntimeSetting{SettingName: "internal_profiling", Service: "datadog-cluster-agent"}) -} diff --git a/cmd/cluster-agent/app/config_check.go b/cmd/cluster-agent/app/config_check.go deleted file mode 100644 index e966d275804ab..0000000000000 --- a/cmd/cluster-agent/app/config_check.go +++ /dev/null @@ -1,15 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands" - -func init() { - ClusterAgentCmd.AddCommand(commands.GetConfigCheckCobraCmd(&flagNoColor, &confPath, loggerName)) -} diff --git a/cmd/cluster-agent/app/diagnose.go b/cmd/cluster-agent/app/diagnose.go deleted file mode 100644 index cf72e3cbea0d4..0000000000000 --- a/cmd/cluster-agent/app/diagnose.go +++ /dev/null @@ -1,58 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "fmt" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/diagnose" - - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -func init() { - ClusterAgentCmd.AddCommand(diagnoseCommand) -} - -var diagnoseCommand = &cobra.Command{ - Use: "diagnose", - Short: "Execute some connectivity diagnosis on your system", - Long: ``, - RunE: doDiagnose, -} - -func doDiagnose(cmd *cobra.Command, args []string) error { - // Global config setup - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global agent configuration: %v", err) - } - - if flagNoColor { - color.NoColor = true - } - - err = config.SetupLogger( - loggerName, - config.Datadog.GetString("log_level"), - common.DefaultLogFile, - config.GetSyslogURI(), - config.Datadog.GetBool("syslog_rfc"), - config.Datadog.GetBool("log_to_console"), - config.Datadog.GetBool("log_format_json"), - ) - if err != nil { - return fmt.Errorf("Error while setting up logging, exiting: %v", err) - } - - return diagnose.RunAll(color.Output) -} diff --git a/cmd/cluster-agent/app/dummy.go b/cmd/cluster-agent/app/dummy.go deleted file mode 100644 index 125c4446fa14c..0000000000000 --- a/cmd/cluster-agent/app/dummy.go +++ /dev/null @@ -1,15 +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. - -//go:build !kubeapiserver -// +build !kubeapiserver - -package app - -// this file is to allow go vet to look at something in this directory, as everything -// else currently is excluded (on windows) by build flags -func init() { - -} diff --git a/cmd/cluster-agent/app/health.go b/cmd/cluster-agent/app/health.go deleted file mode 100644 index 8d4b852e6062a..0000000000000 --- a/cmd/cluster-agent/app/health.go +++ /dev/null @@ -1,17 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "github.com/DataDog/datadog-agent/cmd/cluster-agent/commands" -) - -func init() { - ClusterAgentCmd.AddCommand(commands.Health(loggerName, &confPath, &flagNoColor)) -} diff --git a/cmd/cluster-agent/app/metadata_mapper_digest.go b/cmd/cluster-agent/app/metadata_mapper_digest.go deleted file mode 100644 index 28628d9acf031..0000000000000 --- a/cmd/cluster-agent/app/metadata_mapper_digest.go +++ /dev/null @@ -1,111 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/status" -) - -func init() { - ClusterAgentCmd.AddCommand(metaMapperCmd) -} - -var metaMapperCmd = &cobra.Command{ - Use: "metamap [nodeName]", - Short: "Print the map between the metadata and the pods associated", - Long: `The metamap command is mostly designed for troubleshooting purposes. -One can easily identify which pods are running on which nodes, -as well as which services are serving the pods. Or the deployment name for the pod`, - Example: "datadog-cluster-agent metamap ip-10-0-115-123", - RunE: func(cmd *cobra.Command, args []string) error { - - if flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - nodeName := "" - if len(args) > 0 { - nodeName = args[0] - } - return getMetadataMap(nodeName) // if nodeName == "", call all. - }, -} - -func getMetadataMap(nodeName string) error { - var e error - var s string - c := util.GetClient(false) // FIX: get certificates right then make this true - var urlstr string - if nodeName == "" { - urlstr = fmt.Sprintf("https://localhost:%v/api/v1/tags/pod", config.Datadog.GetInt("cluster_agent.cmd_port")) - } else { - urlstr = fmt.Sprintf("https://localhost:%v/api/v1/tags/pod/%s", config.Datadog.GetInt("cluster_agent.cmd_port"), nodeName) - } - - // Set session token - e = util.SetAuthToken() - if e != nil { - return e - } - - r, e := util.DoGet(c, urlstr, util.LeaveConnectionOpen) - if e != nil { - fmt.Printf(` - Could not reach agent: %v - Make sure the agent is properly running before requesting the map of services to pods. - Contact support if you continue having issues.`, e) - return e - } - - // The rendering is done in the client so that the agent has less work to do - if prettyPrintJSON { - var prettyJSON bytes.Buffer - json.Indent(&prettyJSON, r, "", " ") //nolint:errcheck - s = prettyJSON.String() - } else if jsonStatus { - s = string(r) - } else { - formattedMetadataMap, err := status.FormatMetadataMapCLI(r) - if err != nil { - return err - } - s = formattedMetadataMap - } - - if statusFilePath != "" { - os.WriteFile(statusFilePath, []byte(s), 0644) //nolint:errcheck - } else { - fmt.Println(s) - } - return nil -} diff --git a/cmd/cluster-agent/app/secret_helper.go b/cmd/cluster-agent/app/secret_helper.go deleted file mode 100644 index 16be17ce4e1e1..0000000000000 --- a/cmd/cluster-agent/app/secret_helper.go +++ /dev/null @@ -1,19 +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. - -//go:build kubeapiserver && secrets -// +build kubeapiserver,secrets - -package app - -import ( - "github.com/DataDog/datadog-agent/cmd/secrethelper" -) - -func init() { - for _, cmd := range secrethelper.Commands() { - ClusterAgentCmd.AddCommand(cmd) - } -} diff --git a/cmd/cluster-agent/app/status.go b/cmd/cluster-agent/app/status.go deleted file mode 100644 index 547ca9d5ff8a2..0000000000000 --- a/cmd/cluster-agent/app/status.go +++ /dev/null @@ -1,122 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/status" -) - -var ( - jsonStatus bool - prettyPrintJSON bool - statusFilePath string -) - -func init() { - ClusterAgentCmd.AddCommand(statusCmd) - statusCmd.Flags().BoolVarP(&jsonStatus, "json", "j", false, "print out raw json") - statusCmd.Flags().BoolVarP(&prettyPrintJSON, "pretty-json", "p", false, "pretty print JSON") - statusCmd.Flags().StringVarP(&statusFilePath, "file", "o", "", "Output the status command to a file") -} - -var statusCmd = &cobra.Command{ - Use: "status", - Short: "Print the current status", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - - if flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - err = requestStatus() - if err != nil { - return err - } - return nil - }, -} - -func requestStatus() error { - fmt.Printf("Getting the status from the agent.\n") - var e error - var s string - c := util.GetClient(false) // FIX: get certificates right then make this true - // TODO use https - urlstr := fmt.Sprintf("https://localhost:%v/status", config.Datadog.GetInt("cluster_agent.cmd_port")) - - // Set session token - e = util.SetAuthToken() - if e != nil { - return e - } - - r, e := util.DoGet(c, urlstr, util.LeaveConnectionOpen) - if e != nil { - var errMap = make(map[string]string) - json.Unmarshal(r, &errMap) //nolint:errcheck - // If the error has been marshalled into a json object, check it and return it properly - if err, found := errMap["error"]; found { - e = fmt.Errorf(err) - } - - fmt.Printf(` - Could not reach agent: %v - Make sure the agent is running before requesting the status. - Contact support if you continue having issues.`, e) - return e - } - - // The rendering is done in the client so that the agent has less work to do - if prettyPrintJSON { - var prettyJSON bytes.Buffer - json.Indent(&prettyJSON, r, "", " ") //nolint:errcheck - s = prettyJSON.String() - } else if jsonStatus { - s = string(r) - } else { - formattedStatus, err := status.FormatDCAStatus(r) - if err != nil { - return err - } - s = formattedStatus - } - - if statusFilePath != "" { - os.WriteFile(statusFilePath, []byte(s), 0644) //nolint:errcheck - } else { - fmt.Println(s) - } - - return nil -} diff --git a/cmd/cluster-agent/app/telemetry.go b/cmd/cluster-agent/app/telemetry.go deleted file mode 100644 index d1e0f02fe8fbc..0000000000000 --- a/cmd/cluster-agent/app/telemetry.go +++ /dev/null @@ -1,35 +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. - -//go:build kubeapiserver -// +build kubeapiserver - -package app - -import ( - "fmt" - - "github.com/spf13/cobra" - - "github.com/DataDog/datadog-agent/pkg/flare" -) - -func init() { - ClusterAgentCmd.AddCommand(telemetryCmd) -} - -var telemetryCmd = &cobra.Command{ - Use: "telemetry", - Short: "Print the telemetry metrics exposed by the cluster agent", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - payload, err := flare.QueryDCAMetrics() - if err != nil { - return err - } - fmt.Print(string(payload)) - return nil - }, -} diff --git a/cmd/cluster-agent/command/command.go b/cmd/cluster-agent/command/command.go new file mode 100644 index 0000000000000..dd2c43b3e7b37 --- /dev/null +++ b/cmd/cluster-agent/command/command.go @@ -0,0 +1,69 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package command implements the top-level `cluster-agent` binary, including its subcommands. +package command + +import ( + "fmt" + "os" + + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// GlobalParams contains the values of agent-global Cobra flags. +// +// A pointer to this type is passed to SubcommandFactory's, but its contents +// are not valid until Cobra calls the subcommand's Run or RunE function. +type GlobalParams struct { + // ConfFilePath holds the path to the folder containing the configuration + // file, to allow overrides from the command line + ConfFilePath string +} + +// SubcommandFactory is a callable that will return a slice of subcommands. +type SubcommandFactory func(globalParams *GlobalParams) []*cobra.Command + +// MakeCommand makes the top-level Cobra command for this app. +func MakeCommand(subcommandFactories []SubcommandFactory) *cobra.Command { + globalParams := GlobalParams{} + + // AgentCmd is the root command + agentCmd := &cobra.Command{ + Use: fmt.Sprintf("%s [command]", os.Args[0]), + Short: "Datadog Cluster Agent at your service.", + Long: ` +Datadog Cluster Agent takes care of running checks that need run only once per cluster. +It also exposes an API for other Datadog agents that provides them with cluster-level +metadata for their metrics.`, + SilenceUsage: true, + } + + agentCmd.PersistentFlags().StringVarP(&globalParams.ConfFilePath, "cfgpath", "c", "", "path to directory containing datadog-agent.yaml") + + // github.com/fatih/color sets its global color.NoColor to a default value based on + // whether the process is running in a tty. So, we only want to override that when + // the value is true. + var noColorFlag bool + agentCmd.PersistentFlags().BoolVarP(&noColorFlag, "no-color", "n", false, "disable color output") + agentCmd.PersistentPreRun = func(*cobra.Command, []string) { + if noColorFlag { + color.NoColor = true + } + } + + for _, sf := range subcommandFactories { + subcommands := sf(&globalParams) + for _, cmd := range subcommands { + agentCmd.AddCommand(cmd) + } + } + + return agentCmd +} diff --git a/cmd/cluster-agent/commands/check/check.go b/cmd/cluster-agent/commands/check/check.go deleted file mode 100644 index b53c0594ba4fb..0000000000000 --- a/cmd/cluster-agent/commands/check/check.go +++ /dev/null @@ -1,603 +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 check - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "os" - "path/filepath" - "runtime" - "strconv" - "strings" - "time" - - "github.com/fatih/color" - "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/aggregator" - "github.com/DataDog/datadog-agent/pkg/autodiscovery" - "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - "github.com/DataDog/datadog-agent/pkg/cli/standalone" - "github.com/DataDog/datadog-agent/pkg/collector" - "github.com/DataDog/datadog-agent/pkg/collector/check" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/metadata/inventories" - "github.com/DataDog/datadog-agent/pkg/status" - "github.com/DataDog/datadog-agent/pkg/util/flavor" - "github.com/DataDog/datadog-agent/pkg/util/hostname" - "github.com/DataDog/datadog-agent/pkg/util/scrubber" -) - -var ( - checkRate bool - checkTimes int - checkPause int - checkName string - checkDelay int - instanceFilter string - logLevel string - formatJSON bool - formatTable bool - breakPoint string - fullSketches bool - saveFlare bool - profileMemory bool - profileMemoryDir string - profileMemoryFrames string - profileMemoryGC string - profileMemoryCombine string - profileMemorySort string - profileMemoryLimit string - profileMemoryDiff string - profileMemoryFilters string - profileMemoryUnit string - profileMemoryVerbose string - discoveryTimeout uint - discoveryRetryInterval uint - discoveryMinInstances uint - generateIntegrationTraces bool -) - -func setupCmd(cmd *cobra.Command) { - cmd.Flags().BoolVarP(&checkRate, "check-rate", "r", false, "check rates by running the check twice with a 1sec-pause between the 2 runs") - cmd.Flags().IntVarP(&checkTimes, "check-times", "t", 1, "number of times to run the check") - cmd.Flags().IntVar(&checkPause, "pause", 0, "pause between multiple runs of the check, in milliseconds") - cmd.Flags().StringVarP(&logLevel, "log-level", "l", "", "set the log level (default 'off') (deprecated, use the env var DD_LOG_LEVEL instead)") - cmd.Flags().IntVarP(&checkDelay, "delay", "d", 100, "delay between running the check and grabbing the metrics in milliseconds") - cmd.Flags().StringVarP(&instanceFilter, "instance-filter", "", "", "filter instances using jq style syntax, example: --instance-filter '.ip_address == \"127.0.0.51\"'") - cmd.Flags().BoolVarP(&formatJSON, "json", "", false, "format aggregator and check runner output as json") - cmd.Flags().BoolVarP(&formatTable, "table", "", false, "format aggregator and check runner output as an ascii table") - cmd.Flags().StringVarP(&breakPoint, "breakpoint", "b", "", "set a breakpoint at a particular line number (Python checks only)") - cmd.Flags().BoolVarP(&profileMemory, "profile-memory", "m", false, "run the memory profiler (Python checks only)") - cmd.Flags().BoolVar(&fullSketches, "full-sketches", false, "output sketches with bins information") - cmd.Flags().BoolVarP(&saveFlare, "flare", "", false, "save check results to the log dir so it may be reported in a flare") - cmd.Flags().UintVarP(&discoveryTimeout, "discovery-timeout", "", 5, "max retry duration until Autodiscovery resolves the check template (in seconds)") - cmd.Flags().UintVarP(&discoveryRetryInterval, "discovery-retry-interval", "", 1, "(unused)") - cmd.Flags().UintVarP(&discoveryMinInstances, "discovery-min-instances", "", 1, "minimum number of config instances to be discovered before running the check(s)") - config.Datadog.BindPFlag("cmd.check.fullsketches", cmd.Flags().Lookup("full-sketches")) //nolint:errcheck - - // Power user flags - mark as hidden - createHiddenStringFlag(cmd, &profileMemoryDir, "m-dir", "", "an existing directory in which to store memory profiling data, ignoring clean-up") - createHiddenStringFlag(cmd, &profileMemoryFrames, "m-frames", "", "the number of stack frames to consider") - createHiddenStringFlag(cmd, &profileMemoryGC, "m-gc", "", "whether or not to run the garbage collector to remove noise") - createHiddenStringFlag(cmd, &profileMemoryCombine, "m-combine", "", "whether or not to aggregate over all traceback frames") - createHiddenStringFlag(cmd, &profileMemorySort, "m-sort", "", "what to sort by between: lineno | filename | traceback") - createHiddenStringFlag(cmd, &profileMemoryLimit, "m-limit", "", "the maximum number of sorted results to show") - createHiddenStringFlag(cmd, &profileMemoryDiff, "m-diff", "", "how to order diff results between: absolute | positive") - createHiddenStringFlag(cmd, &profileMemoryFilters, "m-filters", "", "comma-separated list of file path glob patterns to filter by") - createHiddenStringFlag(cmd, &profileMemoryUnit, "m-unit", "", "the binary unit to represent memory usage (kib, mb, etc.). the default is dynamic") - createHiddenStringFlag(cmd, &profileMemoryVerbose, "m-verbose", "", "whether or not to include potentially noisy sources") - createHiddenBooleanFlag(cmd, &generateIntegrationTraces, "m-trace", false, "send the integration traces") - - cmd.SetArgs([]string{"checkName"}) -} - -// Check returns a cobra command to run checks -func Check(loggerName config.LoggerName, confFilePath *string, flagNoColor *bool) *cobra.Command { - cmd := &cobra.Command{ - Use: "check ", - Short: "Run the specified check", - Long: `Use this to run a specific check with a specific rate`, - RunE: func(cmd *cobra.Command, args []string) error { - configName := "" - if flavor.GetFlavor() == flavor.ClusterAgent { - // we'll search for a config file named `datadog-cluster.yaml` - configName = "datadog-cluster" - } - resolvedLogLevel, warnings, err := setupCLI(loggerName, *confFilePath, configName, "", logLevel, "off") - if err != nil { - fmt.Printf("Cannot initialize command: %v\n", err) - return err - } - - if *flagNoColor { - color.NoColor = true - } - - previousIntegrationTracing := false - previousIntegrationTracingExhaustive := false - if generateIntegrationTraces { - if config.Datadog.IsSet("integration_tracing") { - previousIntegrationTracing = config.Datadog.GetBool("integration_tracing") - } - if config.Datadog.IsSet("integration_tracing_exhaustive") { - previousIntegrationTracingExhaustive = config.Datadog.GetBool("integration_tracing_exhaustive") - } - config.Datadog.Set("integration_tracing", true) - config.Datadog.Set("integration_tracing_exhaustive", true) - } - - if len(args) != 0 { - checkName = args[0] - } else { - cmd.Help() //nolint:errcheck - return nil - } - - hostnameDetected, err := hostname.Get(context.TODO()) - if err != nil { - fmt.Printf("Cannot get hostname, exiting: %v\n", err) - return err - } - - // Initializing the aggregator with a flush interval of 0 (to disable the flush goroutines) - opts := aggregator.DefaultAgentDemultiplexerOptions(nil) - opts.FlushInterval = 0 - opts.UseNoopForwarder = true - opts.UseNoopEventPlatformForwarder = true - opts.UseNoopOrchestratorForwarder = true - demux := aggregator.InitAndStartAgentDemultiplexer(opts, hostnameDetected) - - common.LoadComponents(context.Background(), config.Datadog.GetString("confd_path")) - common.AC.LoadAndRun(context.Background()) - - // Create the CheckScheduler, but do not attach it to - // AutoDiscovery. NOTE: we do not start common.Coll, either. - collector.InitCheckScheduler(common.Coll) - - waitCtx, cancelTimeout := context.WithTimeout( - context.Background(), time.Duration(discoveryTimeout)*time.Second) - allConfigs, err := common.WaitForConfigsFromAD(waitCtx, []string{checkName}, int(discoveryMinInstances), instanceFilter) - cancelTimeout() - if err != nil { - return err - } - - // make sure the checks in cs are not JMX checks - for idx := range allConfigs { - conf := &allConfigs[idx] - if conf.Name != checkName { - continue - } - - if check.IsJMXConfig(*conf) { - // we'll mimic the check command behavior with JMXFetch by running - // it with the JSON reporter and the list_with_metrics command. - fmt.Println("Please consider using the 'jmx' command instead of 'check jmx'") - selectedChecks := []string{checkName} - if checkRate { - if err := standalone.ExecJmxListWithRateMetricsJSON(selectedChecks, resolvedLogLevel, allConfigs); err != nil { - return fmt.Errorf("while running the jmx check: %v", err) - } - } else { - if err := standalone.ExecJmxListWithMetricsJSON(selectedChecks, resolvedLogLevel, allConfigs); err != nil { - return fmt.Errorf("while running the jmx check: %v", err) - } - } - - instances := []integration.Data{} - - // Retain only non-JMX instances for later - for _, instance := range conf.Instances { - if check.IsJMXInstance(conf.Name, instance, conf.InitConfig) { - continue - } - instances = append(instances, instance) - } - - if len(instances) == 0 { - fmt.Printf("All instances of '%s' are JMXFetch instances, and have completed running\n", checkName) - return nil - } - - conf.Instances = instances - } - } - - if profileMemory { - // If no directory is specified, make a temporary one - if profileMemoryDir == "" { - profileMemoryDir, err = os.MkdirTemp("", "datadog-agent-memory-profiler") - if err != nil { - return err - } - - defer func() { - cleanupErr := os.RemoveAll(profileMemoryDir) - if cleanupErr != nil { - fmt.Printf("%s\n", cleanupErr) - } - }() - } - - for idx := range allConfigs { - conf := &allConfigs[idx] - if conf.Name != checkName { - continue - } - - var data map[string]interface{} - - err = yaml.Unmarshal(conf.InitConfig, &data) - if err != nil { - return err - } - - if data == nil { - data = make(map[string]interface{}) - } - - data["profile_memory"] = profileMemoryDir - err = populateMemoryProfileConfig(data) - if err != nil { - return err - } - - y, _ := yaml.Marshal(data) - conf.InitConfig = y - - break - } - } else if breakPoint != "" { - breakPointLine, err := strconv.Atoi(breakPoint) - if err != nil { - fmt.Printf("breakpoint must be an integer\n") - return err - } - - for idx := range allConfigs { - conf := &allConfigs[idx] - if conf.Name != checkName { - continue - } - - var data map[string]interface{} - - err = yaml.Unmarshal(conf.InitConfig, &data) - if err != nil { - return err - } - - if data == nil { - data = make(map[string]interface{}) - } - - data["set_breakpoint"] = breakPointLine - - y, _ := yaml.Marshal(data) - conf.InitConfig = y - - break - } - } - - cs := collector.GetChecksByNameForConfigs(checkName, allConfigs) - - // something happened while getting the check(s), display some info. - if len(cs) == 0 { - for check, error := range autodiscovery.GetConfigErrors() { - if checkName == check { - fmt.Fprintln(color.Output, fmt.Sprintf("\n%s: invalid config for %s: %s", color.RedString("Error"), color.YellowString(check), error)) - } - } - for check, errors := range collector.GetLoaderErrors() { - if checkName == check { - fmt.Fprintln(color.Output, fmt.Sprintf("\n%s: could not load %s:", color.RedString("Error"), color.YellowString(checkName))) - for loader, error := range errors { - fmt.Fprintln(color.Output, fmt.Sprintf("* %s: %s", color.YellowString(loader), error)) - } - } - } - for check, warnings := range autodiscovery.GetResolveWarnings() { - if checkName == check { - fmt.Fprintln(color.Output, fmt.Sprintf("\n%s: could not resolve %s config:", color.YellowString("Warning"), color.YellowString(check))) - for _, warning := range warnings { - fmt.Fprintln(color.Output, fmt.Sprintf("* %s", warning)) - } - } - } - return fmt.Errorf("no valid check found") - } - - if len(cs) > 1 { - fmt.Println("Multiple check instances found, running each of them") - } - - var checkFileOutput bytes.Buffer - var instancesData []interface{} - printer := aggregator.AgentDemultiplexerPrinter{AgentDemultiplexer: demux} - for _, c := range cs { - s := runCheck(c, printer) - - // Sleep for a while to allow the aggregator to finish ingesting all the metrics/events/sc - time.Sleep(time.Duration(checkDelay) * time.Millisecond) - - if formatJSON { - aggregatorData := printer.GetMetricsDataForPrint() - var collectorData map[string]interface{} - - collectorJSON, _ := status.GetCheckStatusJSON(c, s) - err = json.Unmarshal(collectorJSON, &collectorData) - if err != nil { - return err - } - - checkRuns := collectorData["runnerStats"].(map[string]interface{})["Checks"].(map[string]interface{})[checkName].(map[string]interface{}) - - // There is only one checkID per run so we'll just access that - var runnerData map[string]interface{} - for _, checkIDData := range checkRuns { - runnerData = checkIDData.(map[string]interface{}) - break - } - - instanceData := map[string]interface{}{ - "aggregator": aggregatorData, - "runner": runnerData, - "inventories": collectorData["inventories"], - } - instancesData = append(instancesData, instanceData) - } else if profileMemory { - // Every instance will create its own directory - instanceID := strings.SplitN(string(c.ID()), ":", 2)[1] - // Colons can't be part of Windows file paths - instanceID = strings.Replace(instanceID, ":", "_", -1) - profileDataDir := filepath.Join(profileMemoryDir, checkName, instanceID) - - snapshotDir := filepath.Join(profileDataDir, "snapshots") - if _, err := os.Stat(snapshotDir); !os.IsNotExist(err) { - snapshots, err := os.ReadDir(snapshotDir) - if err != nil { - return err - } - - numSnapshots := len(snapshots) - if numSnapshots > 0 { - lastSnapshot := snapshots[numSnapshots-1] - snapshotContents, err := os.ReadFile(filepath.Join(snapshotDir, lastSnapshot.Name())) - if err != nil { - return err - } - - color.HiWhite(string(snapshotContents)) - } else { - return fmt.Errorf("no snapshots found in %s", snapshotDir) - } - } else { - return fmt.Errorf("no snapshot data found in %s", profileDataDir) - } - - diffDir := filepath.Join(profileDataDir, "diffs") - if _, err := os.Stat(diffDir); !os.IsNotExist(err) { - diffs, err := os.ReadDir(diffDir) - if err != nil { - return err - } - - numDiffs := len(diffs) - if numDiffs > 0 { - lastDiff := diffs[numDiffs-1] - diffContents, err := os.ReadFile(filepath.Join(diffDir, lastDiff.Name())) - if err != nil { - return err - } - - color.HiCyan(fmt.Sprintf("\n%s\n\n", strings.Repeat("=", 50))) - color.HiWhite(string(diffContents)) - } else { - return fmt.Errorf("no diffs found in %s", diffDir) - } - } else if !singleCheckRun() { - return fmt.Errorf("no diff data found in %s", profileDataDir) - } - } else { - printer.PrintMetrics(&checkFileOutput, formatTable) - - p := func(data string) { - fmt.Println(data) - checkFileOutput.WriteString(data + "\n") - } - - checkStatus, _ := status.GetCheckStatus(c, s) - p(string(checkStatus)) - - metadata := inventories.GetCheckMetadata(c) - if metadata != nil { - p(" Metadata\n ========") - for k, v := range *metadata { - p(fmt.Sprintf(" %s: %v", k, v)) - } - } - } - } - - if runtime.GOOS == "windows" { - standalone.PrintWindowsUserWarning("check") - } - - if formatJSON { - instancesJSON, _ := json.MarshalIndent(instancesData, "", " ") - instanceJSONString := string(instancesJSON) - - fmt.Println(instanceJSONString) - checkFileOutput.WriteString(instanceJSONString + "\n") - } else if singleCheckRun() { - if profileMemory { - color.Yellow("Check has run only once, to collect diff data run the check multiple times with the -t/--check-times flag.") - } else { - color.Yellow("Check has run only once, if some metrics are missing you can try again with --check-rate to see any other metric if available.") - } - } - - if warnings != nil && warnings.TraceMallocEnabledWithPy2 { - return errors.New("tracemalloc is enabled but unavailable with python version 2") - } - - if saveFlare { - writeCheckToFile(checkName, &checkFileOutput) - } - - if generateIntegrationTraces { - config.Datadog.Set("integration_tracing", previousIntegrationTracing) - config.Datadog.Set("integration_tracing_exhaustive", previousIntegrationTracingExhaustive) - } - - return nil - }, - } - setupCmd(cmd) - return cmd -} - -func runCheck(c check.Check, demux aggregator.Demultiplexer) *check.Stats { - s := check.NewStats(c) - times := checkTimes - pause := checkPause - if checkRate { - if checkTimes > 2 { - color.Yellow("The check-rate option is overriding check-times to 2") - } - if pause > 0 { - color.Yellow("The check-rate option is overriding pause to 1000ms") - } - times = 2 - pause = 1000 - } - for i := 0; i < times; i++ { - t0 := time.Now() - err := c.Run() - warnings := c.GetWarnings() - sStats, _ := c.GetSenderStats() - s.Add(time.Since(t0), err, warnings, sStats) - if pause > 0 && i < times-1 { - time.Sleep(time.Duration(pause) * time.Millisecond) - } - } - - return s -} - -func writeCheckToFile(checkName string, checkFileOutput *bytes.Buffer) { - _ = os.Mkdir(common.DefaultCheckFlareDirectory, os.ModeDir) - - // Windows cannot accept ":" in file names - filenameSafeTimeStamp := strings.ReplaceAll(time.Now().UTC().Format(time.RFC3339), ":", "-") - flarePath := filepath.Join(common.DefaultCheckFlareDirectory, "check_"+checkName+"_"+filenameSafeTimeStamp+".log") - - scrubbed, err := scrubber.ScrubBytes(checkFileOutput.Bytes()) - if err != nil { - fmt.Println("Error while scrubbing the check file:", err) - } - err = os.WriteFile(flarePath, scrubbed, os.ModePerm) - - if err != nil { - fmt.Println("Error while writing the check file (is the location writable by the dd-agent user?):", err) - } else { - fmt.Println("check written to:", flarePath) - } -} - -func singleCheckRun() bool { - return checkRate == false && checkTimes < 2 -} - -func createHiddenStringFlag(cmd *cobra.Command, p *string, name string, value string, usage string) { - cmd.Flags().StringVar(p, name, value, usage) - cmd.Flags().MarkHidden(name) //nolint:errcheck -} - -func createHiddenBooleanFlag(cmd *cobra.Command, p *bool, name string, value bool, usage string) { - cmd.Flags().BoolVar(p, name, value, usage) - cmd.Flags().MarkHidden(name) //nolint:errcheck -} - -func populateMemoryProfileConfig(initConfig map[string]interface{}) error { - if profileMemoryFrames != "" { - profileMemoryFrames, err := strconv.Atoi(profileMemoryFrames) - if err != nil { - return fmt.Errorf("--m-frames must be an integer") - } - initConfig["profile_memory_frames"] = profileMemoryFrames - } - - if profileMemoryGC != "" { - profileMemoryGC, err := strconv.Atoi(profileMemoryGC) - if err != nil { - return fmt.Errorf("--m-gc must be an integer") - } - - initConfig["profile_memory_gc"] = profileMemoryGC - } - - if profileMemoryCombine != "" { - profileMemoryCombine, err := strconv.Atoi(profileMemoryCombine) - if err != nil { - return fmt.Errorf("--m-combine must be an integer") - } - - if profileMemoryCombine != 0 && profileMemorySort == "traceback" { - return fmt.Errorf("--m-combine cannot be sorted (--m-sort) by traceback") - } - - initConfig["profile_memory_combine"] = profileMemoryCombine - } - - if profileMemorySort != "" { - if profileMemorySort != "lineno" && profileMemorySort != "filename" && profileMemorySort != "traceback" { - return fmt.Errorf("--m-sort must one of: lineno | filename | traceback") - } - initConfig["profile_memory_sort"] = profileMemorySort - } - - if profileMemoryLimit != "" { - profileMemoryLimit, err := strconv.Atoi(profileMemoryLimit) - if err != nil { - return fmt.Errorf("--m-limit must be an integer") - } - initConfig["profile_memory_limit"] = profileMemoryLimit - } - - if profileMemoryDiff != "" { - if profileMemoryDiff != "absolute" && profileMemoryDiff != "positive" { - return fmt.Errorf("--m-diff must one of: absolute | positive") - } - initConfig["profile_memory_diff"] = profileMemoryDiff - } - - if profileMemoryFilters != "" { - initConfig["profile_memory_filters"] = profileMemoryFilters - } - - if profileMemoryUnit != "" { - initConfig["profile_memory_unit"] = profileMemoryUnit - } - - if profileMemoryVerbose != "" { - profileMemoryVerbose, err := strconv.Atoi(profileMemoryVerbose) - if err != nil { - return fmt.Errorf("--m-verbose must be an integer") - } - initConfig["profile_memory_verbose"] = profileMemoryVerbose - } - - return nil -} diff --git a/cmd/cluster-agent/commands/check/setup.go b/cmd/cluster-agent/commands/check/setup.go deleted file mode 100644 index dcf1288823dfa..0000000000000 --- a/cmd/cluster-agent/commands/check/setup.go +++ /dev/null @@ -1,47 +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 check - -import ( - "fmt" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/config" -) - -// setupCLI sets up the shared utilities for a standalone CLI command: -// - config, with defaults to avoid conflicting with an agent process running in parallel -// - logger -// and returns the log level resolved from cliLogLevel and defaultLogLevel -func setupCLI(loggerName config.LoggerName, confFilePath, configName string, cliLogFile string, cliLogLevel string, defaultLogLevel string) (string, *config.Warnings, error) { - var resolvedLogLevel string - - if cliLogLevel != "" { - // Honour the deprecated --log-level argument - overrides := make(map[string]interface{}) - overrides["log_level"] = cliLogLevel - config.AddOverrides(overrides) - resolvedLogLevel = cliLogLevel - } else { - resolvedLogLevel = config.GetEnvDefault("DD_LOG_LEVEL", defaultLogLevel) - } - - overrides := make(map[string]interface{}) - overrides["cmd_port"] = 0 // let the OS assign an available port for the HTTP server - config.AddOverrides(overrides) - - warnings, err := common.SetupConfigWithWarnings(confFilePath, configName) - if err != nil { - return resolvedLogLevel, warnings, fmt.Errorf("unable to set up global agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, resolvedLogLevel, cliLogFile, "", false, true, false) - if err != nil { - return resolvedLogLevel, warnings, fmt.Errorf("unable to set up logger: %v", err) - } - - return resolvedLogLevel, warnings, nil -} diff --git a/cmd/cluster-agent/commands/clusterchecks.go b/cmd/cluster-agent/commands/clusterchecks.go deleted file mode 100644 index 3d5baad7897e5..0000000000000 --- a/cmd/cluster-agent/commands/clusterchecks.go +++ /dev/null @@ -1,135 +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. - -//go:build kubeapiserver || clusterchecks -// +build kubeapiserver clusterchecks - -package commands - -import ( - "bytes" - "encoding/json" - "fmt" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks/types" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/flare" - - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -var ( - checkName string -) - -// GetClusterChecksCobraCmd TODO -func GetClusterChecksCobraCmd(flagNoColor *bool, confPath *string, loggerName config.LoggerName) *cobra.Command { - clusterChecksCmd := &cobra.Command{ - Use: "clusterchecks", - Short: "Prints the active cluster check configurations", - RunE: func(cmd *cobra.Command, args []string) error { - if *flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(*confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - if err = flare.GetClusterChecks(color.Output, checkName); err != nil { - return err - } - - return flare.GetEndpointsChecks(color.Output, checkName) - }, - } - clusterChecksCmd.PersistentFlags().StringVarP(&checkName, "check", "", "", "the check name to filter for") - - return clusterChecksCmd -} - -// RebalanceClusterChecksCobraCmd TODO -func RebalanceClusterChecksCobraCmd(flagNoColor *bool, confPath *string, loggerName config.LoggerName) *cobra.Command { - clusterChecksCmd := &cobra.Command{ - Use: "rebalance", - Short: "Rebalances cluster checks", - RunE: func(cmd *cobra.Command, args []string) error { - - if *flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(*confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - return rebalanceChecks() - }, - } - - return clusterChecksCmd -} - -func rebalanceChecks() error { - fmt.Println("Requesting a cluster check rebalance...") - c := util.GetClient(false) // FIX: get certificates right then make this true - urlstr := fmt.Sprintf("https://localhost:%v/api/v1/clusterchecks/rebalance", config.Datadog.GetInt("cluster_agent.cmd_port")) - - // Set session token - err := util.SetAuthToken() - if err != nil { - return err - } - - r, err := util.DoPost(c, urlstr, "application/json", bytes.NewBuffer([]byte{})) - if err != nil { - var errMap = make(map[string]string) - json.Unmarshal(r, &errMap) //nolint:errcheck - // If the error has been marshalled into a json object, check it and return it properly - if e, found := errMap["error"]; found { - err = fmt.Errorf(e) - } - - fmt.Printf(` - Could not reach agent: %v - Make sure the agent is running before requesting the cluster checks rebalancing. - Contact support if you continue having issues.`, err) - - return err - } - - checksMoved := make([]types.RebalanceResponse, 0) - json.Unmarshal(r, &checksMoved) //nolint:errcheck - - fmt.Printf("%d cluster checks rebalanced successfully\n", len(checksMoved)) - - for _, check := range checksMoved { - fmt.Printf("Check %s with weight %d moved from node %s to %s. source diff: %d, dest diff: %d\n", - check.CheckID, check.CheckWeight, check.SourceNodeName, check.DestNodeName, check.SourceDiff, check.DestDiff) - } - - return nil -} diff --git a/cmd/cluster-agent/commands/config/config.go b/cmd/cluster-agent/commands/config/config.go deleted file mode 100644 index 3bc6dc10d66ee..0000000000000 --- a/cmd/cluster-agent/commands/config/config.go +++ /dev/null @@ -1,143 +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 config - -import ( - "fmt" - - "github.com/DataDog/datadog-agent/pkg/config/settings" - - "github.com/spf13/cobra" -) - -// Config returns the main cobra config command. -func Config(getClient settings.ClientBuilder) *cobra.Command { - cmd := &cobra.Command{ - Use: "config", - Short: "Print the runtime configuration of a running agent", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { return showRuntimeConfiguration(getClient, cmd, args) }, - } - - cmd.AddCommand(listRuntime(getClient)) - cmd.AddCommand(set(getClient)) - cmd.AddCommand(get(getClient)) - - return cmd -} - -// listRuntime returns a cobra command to list the settings that can be changed at runtime. -func listRuntime(getClient settings.ClientBuilder) *cobra.Command { - return &cobra.Command{ - Use: "list-runtime", - Short: "List settings that can be changed at runtime", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - return listRuntimeConfigurableValue(getClient, cmd, args) - }, - } -} - -// set returns a cobra command to set a config value at runtime. -func set(getClient settings.ClientBuilder) *cobra.Command { - return &cobra.Command{ - Use: "set [setting] [value]", - Short: "Set, for the current runtime, the value of a given configuration setting", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { return setConfigValue(getClient, cmd, args) }, - } -} - -// get returns a cobra command to get a runtime config value. -func get(getClient settings.ClientBuilder) *cobra.Command { - return &cobra.Command{ - Use: "get [setting]", - Short: "Get, for the current runtime, the value of a given configuration setting", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { return getConfigValue(getClient, cmd, args) }, - } -} - -func showRuntimeConfiguration(getClient settings.ClientBuilder, cmd *cobra.Command, args []string) error { - c, err := getClient(cmd, args) - if err != nil { - return err - } - - runtimeConfig, err := c.FullConfig() - if err != nil { - return err - } - - fmt.Println(runtimeConfig) - - return nil -} - -func listRuntimeConfigurableValue(getClient settings.ClientBuilder, cmd *cobra.Command, args []string) error { - c, err := getClient(cmd, args) - if err != nil { - return err - } - - settingsList, err := c.List() - if err != nil { - return err - } - - fmt.Println("=== Settings that can be changed at runtime ===") - for setting, details := range settingsList { - if !details.Hidden { - fmt.Printf("%-30s %s\n", setting, details.Description) - } - } - - return nil -} - -func setConfigValue(getClient settings.ClientBuilder, cmd *cobra.Command, args []string) error { - if len(args) != 2 { - return fmt.Errorf("exactly two parameters are required: the setting name and its value") - } - - c, err := getClient(cmd, args) - if err != nil { - return err - } - - hidden, err := c.Set(args[0], args[1]) - if err != nil { - return err - } - - if hidden { - fmt.Printf("IMPORTANT: you have modified a hidden option, this may incur billing charges or have other unexpected side-effects.\n") - } - - fmt.Printf("Configuration setting %s is now set to: %s\n", args[0], args[1]) - - return nil -} - -func getConfigValue(getClient settings.ClientBuilder, cmd *cobra.Command, args []string) error { - if len(args) != 1 { - return fmt.Errorf("a single setting name must be specified") - } - - c, err := getClient(cmd, args) - if err != nil { - return err - } - - value, err := c.Get(args[0]) - if err != nil { - return err - } - - fmt.Printf("%s is set to: %v\n", args[0], value) - - return nil -} diff --git a/cmd/cluster-agent/commands/configcheck.go b/cmd/cluster-agent/commands/configcheck.go deleted file mode 100644 index b3ca87dd4f56f..0000000000000 --- a/cmd/cluster-agent/commands/configcheck.go +++ /dev/null @@ -1,58 +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. - -//go:build kubeapiserver || clusterchecks -// +build kubeapiserver clusterchecks - -package commands - -import ( - "fmt" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/flare" - - "github.com/fatih/color" - "github.com/spf13/cobra" -) - -// GetConfigCheckCobraCmd TODO -func GetConfigCheckCobraCmd(flagNoColor *bool, confPath *string, loggerName config.LoggerName) *cobra.Command { - var withDebug bool - configCheckCommand := &cobra.Command{ - Use: "configcheck", - Aliases: []string{"checkconfig"}, - Short: "Print all configurations loaded & resolved of a running cluster agent", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - - if *flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(*confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - err = flare.GetClusterAgentConfigCheck(color.Output, withDebug) - if err != nil { - return err - } - return nil - }, - } - configCheckCommand.Flags().BoolVarP(&withDebug, "verbose", "v", false, "print additional debug info") - return configCheckCommand -} diff --git a/cmd/cluster-agent/commands/health.go b/cmd/cluster-agent/commands/health.go deleted file mode 100644 index 321c112e6d4fc..0000000000000 --- a/cmd/cluster-agent/commands/health.go +++ /dev/null @@ -1,118 +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 commands - -import ( - "encoding/json" - "fmt" - "sort" - "strconv" - "strings" - - "github.com/fatih/color" - "github.com/spf13/cobra" - - "github.com/DataDog/datadog-agent/cmd/agent/common" - "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" - "github.com/DataDog/datadog-agent/pkg/status/health" - "github.com/DataDog/datadog-agent/pkg/util/flavor" -) - -// Health returns a cobra command to report on the agent's health -func Health(loggerName config.LoggerName, confPath *string, flagNoColor *bool) *cobra.Command { - return &cobra.Command{ - Use: "health", - Short: "Print the current agent health", - Long: ``, - SilenceUsage: true, - RunE: func(cmd *cobra.Command, args []string) error { - - if *flagNoColor { - color.NoColor = true - } - - if flavor.GetFlavor() == flavor.ClusterAgent { - config.Datadog.SetConfigName("datadog-cluster") - } - - // Set up config without secrets so that running the health command (e.g. from container - // liveness probe script) does not trigger a secret backend command call. - err := common.SetupConfigWithoutSecrets(*confPath, "") - if err != nil { - return fmt.Errorf("unable to set up global agent configuration: %v", err) - } - - err = config.SetupLogger(loggerName, config.GetEnvDefault("DD_LOG_LEVEL", "off"), "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } - - return requestHealth() - }, - } -} -func requestHealth() error { - c := util.GetClient(false) // FIX: get certificates right then make this true - - ipcAddress, err := config.GetIPCAddress() - if err != nil { - return err - } - - var urlstr string - if flavor.GetFlavor() == flavor.ClusterAgent { - urlstr = fmt.Sprintf("https://%v:%v/status/health", ipcAddress, config.Datadog.GetInt("cluster_agent.cmd_port")) - } else { - urlstr = fmt.Sprintf("https://%v:%v/agent/status/health", ipcAddress, config.Datadog.GetInt("cmd_port")) - } - - // Set session token - err = util.SetAuthToken() - if err != nil { - return err - } - - r, err := util.DoGet(c, urlstr, util.LeaveConnectionOpen) - if err != nil { - var errMap = make(map[string]string) - json.Unmarshal(r, &errMap) //nolint:errcheck - // If the error has been marshalled into a json object, check it and return it properly - if e, found := errMap["error"]; found { - err = fmt.Errorf(e) - } - - fmt.Printf("Could not reach agent: %v \nMake sure the agent is running before requesting the status and contact support if you continue having issues. \n", err) - return err - } - - s := new(health.Status) - if err = json.Unmarshal(r, s); err != nil { - return fmt.Errorf("Error unmarshalling json: %s", err) - } - - sort.Strings(s.Unhealthy) - sort.Strings(s.Healthy) - - statusString := color.GreenString("PASS") - if len(s.Unhealthy) > 0 { - statusString = color.RedString("FAIL") - } - fmt.Fprintln(color.Output, fmt.Sprintf("Agent health: %s", statusString)) - - if len(s.Healthy) > 0 { - fmt.Fprintln(color.Output, fmt.Sprintf("=== %s healthy components ===", color.GreenString(strconv.Itoa(len(s.Healthy))))) - fmt.Fprintln(color.Output, strings.Join(s.Healthy, ", ")) - } - if len(s.Unhealthy) > 0 { - fmt.Fprintln(color.Output, fmt.Sprintf("=== %s unhealthy components ===", color.RedString(strconv.Itoa(len(s.Unhealthy))))) - fmt.Fprintln(color.Output, strings.Join(s.Unhealthy, ", ")) - return fmt.Errorf("found %d unhealthy components", len(s.Unhealthy)) - } - - return nil -} diff --git a/cmd/cluster-agent/main.go b/cmd/cluster-agent/main.go index cc049926d3dc3..1a6c6b3afe43c 100644 --- a/cmd/cluster-agent/main.go +++ b/cmd/cluster-agent/main.go @@ -16,6 +16,8 @@ import ( _ "expvar" // Blank import used because this isn't directly used in this file _ "net/http/pprof" // Blank import used because this isn't directly used in this file + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands" _ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/cluster/helm" _ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/cluster/ksm" _ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/cluster/kubernetesapiserver" @@ -29,14 +31,15 @@ import ( _ "github.com/DataDog/datadog-agent/pkg/collector/corechecks/system/winproc" "github.com/DataDog/datadog-agent/pkg/util/flavor" "github.com/DataDog/datadog-agent/pkg/util/log" - - "github.com/DataDog/datadog-agent/cmd/cluster-agent/app" ) func main() { // set the Agent flavor flavor.SetFlavor(flavor.ClusterAgent) - if err := app.ClusterAgentCmd.Execute(); err != nil { + + ClusterAgentCmd := command.MakeCommand(subcommands.ClusterAgentSubcommands()) + + if err := ClusterAgentCmd.Execute(); err != nil { log.Error(err) os.Exit(-1) } diff --git a/cmd/cluster-agent/subcommands/check/command.go b/cmd/cluster-agent/subcommands/check/command.go new file mode 100644 index 0000000000000..d7a7ad75aed84 --- /dev/null +++ b/cmd/cluster-agent/subcommands/check/command.go @@ -0,0 +1,32 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package check implements 'cluster-agent check'. +package check + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/check" + + "github.com/spf13/cobra" +) + +const loggerName = "CLUSTER" + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := check.MakeCommand(func() check.GlobalParams { + return check.GlobalParams{ + ConfFilePath: globalParams.ConfFilePath, + ConfigName: "datadog-cluster", + LoggerName: loggerName, + } + }) + + return []*cobra.Command{cmd} +} diff --git a/cmd/cluster-agent/subcommands/clusterchecks/command.go b/cmd/cluster-agent/subcommands/clusterchecks/command.go new file mode 100644 index 0000000000000..9bc2c18b31596 --- /dev/null +++ b/cmd/cluster-agent/subcommands/clusterchecks/command.go @@ -0,0 +1,28 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package clusterchecks implements 'cluster-agent clusterchecks'. +package clusterchecks + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/clusterchecks" + + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := clusterchecks.MakeCommand(func() clusterchecks.GlobalParams { + return clusterchecks.GlobalParams{ + ConfFilePath: globalParams.ConfFilePath, + } + }) + + return []*cobra.Command{cmd} +} diff --git a/cmd/cluster-agent/app/compliance_cmd.go b/cmd/cluster-agent/subcommands/compliance/command.go similarity index 60% rename from cmd/cluster-agent/app/compliance_cmd.go rename to cmd/cluster-agent/subcommands/compliance/command.go index 32c1ff7beceb3..20cf603f82dfa 100644 --- a/cmd/cluster-agent/app/compliance_cmd.go +++ b/cmd/cluster-agent/subcommands/compliance/command.go @@ -6,35 +6,33 @@ //go:build !windows && kubeapiserver // +build !windows,kubeapiserver -package app +// Package compliance implements 'cluster-agent compliance'. +package version import ( "github.com/spf13/cobra" + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands/check" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log" ) -var ( - complianceCmd = &cobra.Command{ +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + complianceCmd := &cobra.Command{ Use: "compliance", - Short: "Compliance utility commands", + Short: "compliance utility commands", } -) -func init() { bundleParams := core.BundleParams{ - ConfigParams: config.NewParams( - "", - config.WithConfFilePath(confPath), - config.WithConfigName("datadog-cluster"), - ), - LogParams: log.LogForOneShot(string(loggerName), "off", true), + ConfigParams: config.NewClusterAgentParams(""), + LogParams: log.LogForOneShot("CLUSTER", "off", true), } // TODO: The SecAgent Check package should be a component complianceCmd.AddCommand(check.CommandsWrapped(bundleParams)...) - ClusterAgentCmd.AddCommand(complianceCmd) + + return []*cobra.Command{complianceCmd} } diff --git a/cmd/cluster-agent/subcommands/config/command.go b/cmd/cluster-agent/subcommands/config/command.go new file mode 100644 index 0000000000000..cf877493cc8da --- /dev/null +++ b/cmd/cluster-agent/subcommands/config/command.go @@ -0,0 +1,50 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package config implements 'cluster-agent config'. +package config + +import ( + "fmt" + + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/config" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/config/settings" + settingshttp "github.com/DataDog/datadog-agent/pkg/config/settings/http" + + "github.com/spf13/cobra" +) + +const loggerName = "CLUSTER" + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := config.MakeCommand(func() config.GlobalParams { + return config.GlobalParams{ + ConfFilePath: globalParams.ConfFilePath, + ConfigName: "datadog-cluster", + LoggerName: loggerName, + SettingsClient: newSettingsClient, + } + }) + + return []*cobra.Command{cmd} +} + +func newSettingsClient() (settings.Client, error) { + c := util.GetClient(false) + + apiConfigURL := fmt.Sprintf( + "https://localhost:%v/config", + pkgconfig.Datadog.GetInt("cluster_agent.cmd_port"), + ) + + return settingshttp.NewClient(c, apiConfigURL, "datadog-cluster-agent"), nil +} diff --git a/cmd/cluster-agent/subcommands/configcheck/command.go b/cmd/cluster-agent/subcommands/configcheck/command.go new file mode 100644 index 0000000000000..0d0035e54434c --- /dev/null +++ b/cmd/cluster-agent/subcommands/configcheck/command.go @@ -0,0 +1,28 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package configcheck implements 'cluster-agent configcheck'. +package configcheck + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/dcaconfigcheck" + + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := dcaconfigcheck.MakeCommand(func() dcaconfigcheck.GlobalParams { + return dcaconfigcheck.GlobalParams{ + ConfFilePath: globalParams.ConfFilePath, + } + }) + + return []*cobra.Command{cmd} +} diff --git a/cmd/cluster-agent/subcommands/diagnose/command.go b/cmd/cluster-agent/subcommands/diagnose/command.go new file mode 100644 index 0000000000000..c62d20dfd818c --- /dev/null +++ b/cmd/cluster-agent/subcommands/diagnose/command.go @@ -0,0 +1,46 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package diagnose implements 'cluster-agent diagnose'. +package diagnose + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/pkg/diagnose" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/fatih/color" + "github.com/spf13/cobra" + "go.uber.org/fx" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := &cobra.Command{ + Use: "diagnose", + Short: "Execute some connectivity diagnosis on your system", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return fxutil.OneShot(run, + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForOneShot(command.LoggerName, command.DefaultLogLevel, true), + }), + core.Bundle, + ) + }, + } + + return []*cobra.Command{cmd} +} + +func run(log log.Component, config config.Component) error { + return diagnose.RunAll(color.Output) +} diff --git a/cmd/cluster-agent/app/flare.go b/cmd/cluster-agent/subcommands/flare/command.go similarity index 50% rename from cmd/cluster-agent/app/flare.go rename to cmd/cluster-agent/subcommands/flare/command.go index cc1447d6cabcb..634c386b8fd79 100644 --- a/cmd/cluster-agent/app/flare.go +++ b/cmd/cluster-agent/subcommands/flare/command.go @@ -3,87 +3,84 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build kubeapiserver -// +build kubeapiserver +//go:build !windows && kubeapiserver +// +build !windows,kubeapiserver -package app +// Package flare implements 'cluster-agent flare'. +package flare import ( "bytes" "fmt" - "github.com/fatih/color" - "github.com/spf13/cobra" - "github.com/DataDog/datadog-agent/cmd/agent/common" + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/pkg/api/util" - "github.com/DataDog/datadog-agent/pkg/config" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/flare" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/input" + "github.com/fatih/color" + "github.com/spf13/cobra" + "go.uber.org/fx" ) -var ( - customerEmail string - autoconfirm bool -) - -func init() { - ClusterAgentCmd.AddCommand(flareCmd) - - flareCmd.Flags().StringVarP(&customerEmail, "email", "e", "", "Your email") - flareCmd.Flags().BoolVarP(&autoconfirm, "send", "s", false, "Automatically send flare (don't prompt for confirmation)") - flareCmd.SetArgs([]string{"caseID"}) +type cliParams struct { + caseID string + email string + send bool } -var flareCmd = &cobra.Command{ - Use: "flare [caseID]", - Short: "Collect a flare and send it to Datadog", - Long: ``, - RunE: func(cmd *cobra.Command, args []string) error { - - if flagNoColor { - color.NoColor = true - } - - // we'll search for a config file named `datadog-cluster.yaml` - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global cluster agent configuration: %v", err) - } +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cliParams := &cliParams{} + + cmd := &cobra.Command{ + Use: "flare [caseID]", + Short: "Collect a flare and send it to Datadog", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) > 0 { + cliParams.caseID = args[0] + } - // The flare command should not log anything, all errors should be reported directly to the console without the log format - err = config.SetupLogger(loggerName, "off", "", "", false, true, false) - if err != nil { - fmt.Printf("Cannot setup logger, exiting: %v\n", err) - return err - } + if cliParams.email == "" { + var err error + cliParams.email, err = input.AskForEmail() + if err != nil { + fmt.Println("Error reading email, please retry or contact support") + return err + } + } - caseID := "" - if len(args) > 0 { - caseID = args[0] - } + return fxutil.OneShot(run, + fx.Supply(cliParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForOneShot("CLUSTER", "off", true), + }), + core.Bundle, + ) + }, + } - if customerEmail == "" { - var err error - customerEmail, err = input.AskForEmail() - if err != nil { - fmt.Println("Error reading email, please retry or contact support") - return err - } - } + cmd.Flags().StringVarP(&cliParams.email, "email", "e", "", "Your email") + cmd.Flags().BoolVarP(&cliParams.send, "send", "s", false, "Automatically send flare (don't prompt for confirmation)") + cmd.SetArgs([]string{"caseID"}) - return requestFlare(caseID) - }, + return []*cobra.Command{cmd} } -func requestFlare(caseID string) error { +func run(log log.Component, config config.Component, cliParams *cliParams) error { fmt.Fprintln(color.Output, color.BlueString("Asking the Cluster Agent to build the flare archive.")) var e error c := util.GetClient(false) // FIX: get certificates right then make this true - urlstr := fmt.Sprintf("https://localhost:%v/flare", config.Datadog.GetInt("cluster_agent.cmd_port")) + urlstr := fmt.Sprintf("https://localhost:%v/flare", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port")) - logFile := config.Datadog.GetString("log_file") + logFile := pkgconfig.Datadog.GetString("log_file") if logFile == "" { logFile = common.DefaultDCALogFile } @@ -113,7 +110,7 @@ func requestFlare(caseID string) error { } fmt.Fprintln(color.Output, fmt.Sprintf("%s is going to be uploaded to Datadog", color.YellowString(filePath))) - if !autoconfirm { + if !cliParams.send { confirmation := input.AskForConfirmation("Are you sure you want to upload a flare? [Y/N]") if !confirmation { fmt.Fprintln(color.Output, fmt.Sprintf("Aborting. (You can still use %s)", color.YellowString(filePath))) @@ -121,7 +118,7 @@ func requestFlare(caseID string) error { } } - response, e := flare.SendFlare(filePath, caseID, customerEmail) + response, e := flare.SendFlare(filePath, cliParams.caseID, cliParams.email) fmt.Println(response) if e != nil { return e diff --git a/cmd/cluster-agent/subcommands/health/command.go b/cmd/cluster-agent/subcommands/health/command.go new file mode 100644 index 0000000000000..676f23f798006 --- /dev/null +++ b/cmd/cluster-agent/subcommands/health/command.go @@ -0,0 +1,30 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package health implements 'cluster-agent health'. +package health + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/health" + + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := health.MakeCommand(func() health.GlobalParams { + return health.GlobalParams{ + ConfFilePath: globalParams.ConfFilePath, + ConfigName: "datadog-cluster", + LoggerName: "CLUSTER", + } + }) + + return []*cobra.Command{cmd} +} diff --git a/cmd/cluster-agent/subcommands/metamap/command.go b/cmd/cluster-agent/subcommands/metamap/command.go new file mode 100644 index 0000000000000..355630ddbfe22 --- /dev/null +++ b/cmd/cluster-agent/subcommands/metamap/command.go @@ -0,0 +1,99 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package metamap implements 'cluster-agent metamap'. +package metamap + +import ( + "fmt" + + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/pkg/api/util" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/status" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" + "go.uber.org/fx" +) + +type cliParams struct { + args []string +} + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cliParams := &cliParams{} + + cmd := &cobra.Command{ + Use: "metamap [nodeName]", + Short: "Print the map between the metadata and the pods associated", + Long: `The metamap command is mostly designed for troubleshooting purposes. +One can easily identify which pods are running on which nodes, +as well as which services are serving the pods. Or the deployment name for the pod`, + Example: "datadog-cluster-agent metamap ip-10-0-115-123", + RunE: func(cmd *cobra.Command, args []string) error { + cliParams.args = args + return fxutil.OneShot(run, + fx.Supply(cliParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForOneShot(command.LoggerName, command.DefaultLogLevel, true), + }), + core.Bundle, + ) + }, + } + + return []*cobra.Command{cmd} +} + +func run(log log.Component, config config.Component, cliParams *cliParams) error { + nodeName := "" + if len(cliParams.args) > 0 { + nodeName = cliParams.args[0] + } + return getMetadataMap(nodeName) // if nodeName == "", call all. +} + +func getMetadataMap(nodeName string) error { + var e error + c := util.GetClient(false) // FIX: get certificates right then make this true + var urlstr string + if nodeName == "" { + urlstr = fmt.Sprintf("https://localhost:%v/api/v1/tags/pod", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port")) + } else { + urlstr = fmt.Sprintf("https://localhost:%v/api/v1/tags/pod/%s", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port"), nodeName) + } + + // Set session token + e = util.SetAuthToken() + if e != nil { + return e + } + + r, e := util.DoGet(c, urlstr, util.LeaveConnectionOpen) + if e != nil { + fmt.Printf(` + Could not reach agent: %v + Make sure the agent is properly running before requesting the map of services to pods. + Contact support if you continue having issues.`, e) + return e + } + + formattedMetadataMap, err := status.FormatMetadataMapCLI(r) + if err != nil { + return err + } + + fmt.Println(formattedMetadataMap) + + return nil +} diff --git a/cmd/cluster-agent/subcommands/secrethelper/command.go b/cmd/cluster-agent/subcommands/secrethelper/command.go new file mode 100644 index 0000000000000..12b93800e285b --- /dev/null +++ b/cmd/cluster-agent/subcommands/secrethelper/command.go @@ -0,0 +1,21 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package secrethelper implements 'cluster-agent secret-helper'. +package diagnose + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/cmd/secrethelper" + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + return secrethelper.Commands() +} diff --git a/cmd/cluster-agent/app/app.go b/cmd/cluster-agent/subcommands/start/command.go similarity index 60% rename from cmd/cluster-agent/app/app.go rename to cmd/cluster-agent/subcommands/start/command.go index a0f6e7e3eea37..20da0bfcd0fe0 100644 --- a/cmd/cluster-agent/app/app.go +++ b/cmd/cluster-agent/subcommands/start/command.go @@ -3,10 +3,11 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build kubeapiserver -// +build kubeapiserver +//go:build !windows && kubeapiserver +// +build !windows,kubeapiserver -package app +// Package start implements 'cluster-agent start'. +package start import ( "context" @@ -18,20 +19,15 @@ import ( "syscall" "time" - "github.com/fatih/color" - "github.com/gorilla/mux" - "github.com/spf13/cobra" - - v1 "k8s.io/api/core/v1" - "k8s.io/client-go/kubernetes/scheme" - corev1 "k8s.io/client-go/kubernetes/typed/core/v1" - "k8s.io/client-go/tools/record" - "github.com/DataDog/datadog-agent/cmd/agent/common" admissioncmd "github.com/DataDog/datadog-agent/cmd/cluster-agent/admission" "github.com/DataDog/datadog-agent/cmd/cluster-agent/api" dcav1 "github.com/DataDog/datadog-agent/cmd/cluster-agent/api/v1" + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" "github.com/DataDog/datadog-agent/cmd/cluster-agent/custommetrics" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/pkg/aggregator" "github.com/DataDog/datadog-agent/pkg/api/healthprobe" "github.com/DataDog/datadog-agent/pkg/clusteragent" @@ -40,169 +36,118 @@ import ( admissionpatch "github.com/DataDog/datadog-agent/pkg/clusteragent/admission/patch" "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks" "github.com/DataDog/datadog-agent/pkg/collector" - "github.com/DataDog/datadog-agent/pkg/config" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/config/remote" "github.com/DataDog/datadog-agent/pkg/config/remote/data" remoteconfig "github.com/DataDog/datadog-agent/pkg/config/remote/service" "github.com/DataDog/datadog-agent/pkg/config/resolver" + commonsettings "github.com/DataDog/datadog-agent/pkg/config/settings" "github.com/DataDog/datadog-agent/pkg/forwarder" - "github.com/DataDog/datadog-agent/pkg/serializer" "github.com/DataDog/datadog-agent/pkg/status/health" "github.com/DataDog/datadog-agent/pkg/telemetry" "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/hostname" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver" apicommon "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver/common" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver/leaderelection" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/clustername" - "github.com/DataDog/datadog-agent/pkg/util/log" + pkglog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/version" + + "github.com/gorilla/mux" + "github.com/spf13/cobra" + "go.uber.org/fx" + v1 "k8s.io/api/core/v1" + "k8s.io/client-go/kubernetes/scheme" + corev1 "k8s.io/client-go/kubernetes/typed/core/v1" + "k8s.io/client-go/tools/record" ) -// loggerName is the name of the cluster agent logger -const loggerName config.LoggerName = "CLUSTER" - -// FIXME: move LoadComponents and AC.LoadAndRun in their own package so we don't import cmd/agent -var ( - ClusterAgentCmd = &cobra.Command{ - Use: "datadog-cluster-agent [command]", - Short: "Datadog Cluster Agent at your service.", - Long: ` -Datadog Cluster Agent takes care of running checks that need run only once per cluster. -It also exposes an API for other Datadog agents that provides them with cluster-level -metadata for their metrics.`, - } +const loggerName = "CLUSTER" - startCmd = &cobra.Command{ +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + startCmd := &cobra.Command{ Use: "start", Short: "Start the Cluster Agent", Long: `Runs Datadog Cluster agent in the foreground`, - RunE: start, - } - - versionCmd = &cobra.Command{ - Use: "version", - Short: "Print the version info", - Long: ``, - Run: func(cmd *cobra.Command, args []string) { - if flagNoColor { - color.NoColor = true - } - av, _ := version.Agent() - meta := "" - if av.Meta != "" { - meta = fmt.Sprintf("- Meta: %s ", color.YellowString(av.Meta)) - } - fmt.Fprintln( - color.Output, - fmt.Sprintf("Cluster agent %s %s- Commit: '%s' - Serialization version: %s", - color.BlueString(av.GetNumberAndPre()), - meta, - color.GreenString(version.Commit), - color.MagentaString(serializer.AgentPayloadVersion), - ), + RunE: func(cmd *cobra.Command, args []string) error { + // TODO: once the cluster-agent is represented as a component, and + // not a function (start), this will use `fxutil.Run` instead of + // `fxutil.OneShot`. + return fxutil.OneShot(start, + fx.Supply(globalParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForDaemon(loggerName, "log_file", common.DefaultDCALogFile), + }), + core.Bundle, ) }, } - confPath string - flagNoColor bool - stopCh chan struct{} -) - -func init() { - // attach the command to the root - ClusterAgentCmd.AddCommand(startCmd) - ClusterAgentCmd.AddCommand(versionCmd) - - ClusterAgentCmd.PersistentFlags().StringVarP(&confPath, "cfgpath", "c", "", "path to directory containing datadog.yaml") - ClusterAgentCmd.PersistentFlags().BoolVarP(&flagNoColor, "no-color", "n", false, "disable color output") + return []*cobra.Command{startCmd} } -func start(cmd *cobra.Command, args []string) error { - // Starting Cluster Agent sequence - // Initialization order is important for multiple reasons, see comments +func start(log log.Component, config config.Component, cliParams *command.GlobalParams) error { + stopCh := make(chan struct{}) - // Reading configuration as mostly everything can depend on config variables - config.Datadog.SetConfigName("datadog-cluster") - err := common.SetupConfig(confPath) - if err != nil { - return fmt.Errorf("unable to set up global agent configuration: %v", err) - } + mainCtx, mainCtxCancel := context.WithCancel(context.Background()) + defer mainCtxCancel() - // Setup logger - syslogURI := config.GetSyslogURI() - logFile := config.Datadog.GetString("log_file") - if logFile == "" { - logFile = common.DefaultDCALogFile - } - if config.Datadog.GetBool("disable_file_logging") { - // this will prevent any logging on file - logFile = "" - } + signalCh := make(chan os.Signal, 1) + signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM) - err = config.SetupLogger( - loggerName, - config.Datadog.GetString("log_level"), - logFile, - syslogURI, - config.Datadog.GetBool("syslog_rfc"), - config.Datadog.GetBool("log_to_console"), - config.Datadog.GetBool("log_format_json"), - ) - if err != nil { - log.Criticalf("Unable to setup logger: %s", err) - return nil - } + // Starting Cluster Agent sequence + // Initialization order is important for multiple reasons, see comments if err := util.SetupCoreDump(); err != nil { - log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) + pkglog.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } // Init settings that can be changed at runtime if err := initRuntimeSettings(); err != nil { - log.Warnf("Can't initiliaze the runtime settings: %v", err) + pkglog.Warnf("Can't initiliaze the runtime settings: %v", err) } // Setup Internal Profiling common.SetupInternalProfiling() - if !config.Datadog.IsSet("api_key") { - log.Critical("no API key configured, exiting") - return nil + if !pkgconfig.Datadog.IsSet("api_key") { + return fmt.Errorf("no API key configured, exiting") } - mainCtx, mainCtxCancel := context.WithCancel(context.Background()) - defer mainCtxCancel() // Calling cancel twice is safe - // Expose the registered metrics via HTTP. http.Handle("/metrics", telemetry.Handler()) - metricsPort := config.Datadog.GetInt("metrics_port") + metricsPort := pkgconfig.Datadog.GetInt("metrics_port") metricsServer := &http.Server{ Addr: fmt.Sprintf("0.0.0.0:%d", metricsPort), Handler: http.DefaultServeMux, } + go func() { err := metricsServer.ListenAndServe() if err != nil && err != http.ErrServerClosed { - log.Errorf("Error creating expvar server on port %v: %v", metricsPort, err) + pkglog.Errorf("Error creating expvar server on port %v: %v", metricsPort, err) } }() // Setup healthcheck port - var healthPort = config.Datadog.GetInt("health_port") + var healthPort = pkgconfig.Datadog.GetInt("health_port") if healthPort > 0 { err := healthprobe.Serve(mainCtx, healthPort) if err != nil { - return log.Errorf("Error starting health port, exiting: %v", err) + return fmt.Errorf("Error starting health port, exiting: %v", err) } - log.Debugf("Health check listening on port %d", healthPort) + + pkglog.Debugf("Health check listening on port %d", healthPort) } // Initialize remote configuration var rcClient *remote.Client - if config.Datadog.GetBool("remote_configuration.enabled") { - rcClient, err = initializeRemoteConfig(mainCtx) + if pkgconfig.Datadog.GetBool("remote_configuration.enabled") { + rcClient, err := initializeRemoteConfig(mainCtx) if err != nil { log.Errorf("Failed to start remote-configuration: %v", err) } else { @@ -212,30 +157,31 @@ func start(cmd *cobra.Command, args []string) error { } // Starting server early to ease investigations - if err = api.StartServer(); err != nil { - return log.Errorf("Error while starting agent API, exiting: %v", err) + if err := api.StartServer(); err != nil { + return fmt.Errorf("Error while starting agent API, exiting: %v", err) } // Getting connection to APIServer, it's done before Hostname resolution // as hostname resolution may call APIServer - log.Info("Waiting to obtain APIClient connection") + pkglog.Info("Waiting to obtain APIClient connection") apiCl, err := apiserver.WaitForAPIClient(context.Background()) // make sure we can connect to the apiserver if err != nil { - return log.Errorf("Fatal error: Cannot connect to the apiserver: %v", err) + return fmt.Errorf("Fatal error: Cannot connect to the apiserver: %v", err) } - log.Infof("Got APIClient connection") + pkglog.Infof("Got APIClient connection") // Get hostname as aggregator requires hostname - hname, err := hostname.Get(context.TODO()) + hname, err := hostname.Get(mainCtx) if err != nil { - return log.Errorf("Error while getting hostname, exiting: %v", err) + return fmt.Errorf("Error while getting hostname, exiting: %v", err) } - log.Infof("Hostname is: %s", hname) + + pkglog.Infof("Hostname is: %s", hname) // setup the forwarder - keysPerDomain, err := config.GetMultipleEndpoints() + keysPerDomain, err := pkgconfig.GetMultipleEndpoints() if err != nil { - log.Error("Misconfiguration of agent endpoints: ", err) + pkglog.Error("Misconfiguration of agent endpoints: ", err) } // If a cluster-agent looses the connectivity to DataDog, we still want it to remain ready so that its endpoint remains in the service because: @@ -257,7 +203,7 @@ func start(cmd *cobra.Command, args []string) error { // Create event recorder eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(log.Infof) + eventBroadcaster.StartLogging(pkglog.Infof) eventBroadcaster.StartRecordingToSink(&corev1.EventSinkImpl{Interface: apiCl.Cl.CoreV1().Events("")}) eventRecorder := eventBroadcaster.NewRecorder(scheme.Scheme, v1.EventSource{Component: "datadog-cluster-agent"}) @@ -275,33 +221,33 @@ func start(cmd *cobra.Command, args []string) error { if aggErr := apiserver.StartControllers(ctx); aggErr != nil { for _, err := range aggErr.Errors() { - log.Warnf("Error while starting controller: %v", err) + pkglog.Warnf("Error while starting controller: %v", err) } } clusterName := clustername.GetClusterName(context.TODO(), hname) - if config.Datadog.GetBool("orchestrator_explorer.enabled") { + if pkgconfig.Datadog.GetBool("orchestrator_explorer.enabled") { // Generate and persist a cluster ID // this must be a UUID, and ideally be stable for the lifetime of a cluster, // so we store it in a configmap that we try and read before generating a new one. coreClient := apiCl.Cl.CoreV1().(*corev1.CoreV1Client) _, err = apicommon.GetOrCreateClusterID(coreClient) if err != nil { - log.Errorf("Failed to generate or retrieve the cluster ID") + pkglog.Errorf("Failed to generate or retrieve the cluster ID") } if clusterName == "" { - log.Warn("Failed to auto-detect a Kubernetes cluster name. We recommend you set it manually via the cluster_name config option") + pkglog.Warn("Failed to auto-detect a Kubernetes cluster name. We recommend you set it manually via the cluster_name config option") } } else { - log.Info("Orchestrator explorer is disabled") + pkglog.Info("Orchestrator explorer is disabled") } - // Setup a channel to catch OS signals - signalCh := make(chan os.Signal, 1) - signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM) + // FIXME: move LoadComponents and AC.LoadAndRun in their own package so we + // don't import cmd/agent + // create and setup the Autoconfig instance - common.LoadComponents(mainCtx, config.Datadog.GetString("confd_path")) + common.LoadComponents(mainCtx, pkgconfig.Datadog.GetString("confd_path")) // Set up check collector common.AC.AddScheduler("check", collector.InitCheckScheduler(common.Coll), true) @@ -310,7 +256,7 @@ func start(cmd *cobra.Command, args []string) error { // start the autoconfig, this will immediately run any configured check common.AC.LoadAndRun(mainCtx) - if config.Datadog.GetBool("cluster_checks.enabled") { + if pkgconfig.Datadog.GetBool("cluster_checks.enabled") { // Start the cluster check Autodiscovery clusterCheckHandler, err := setupClusterCheck(mainCtx) if err == nil { @@ -318,15 +264,15 @@ func start(cmd *cobra.Command, args []string) error { dcav1.InstallChecksEndpoints(r, clusteragent.ServerContext{ClusterCheckHandler: clusterCheckHandler}) }) } else { - log.Errorf("Error while setting up cluster check Autodiscovery, CLC API endpoints won't be available, err: %v", err) + pkglog.Errorf("Error while setting up cluster check Autodiscovery, CLC API endpoints won't be available, err: %v", err) } } else { - log.Debug("Cluster check Autodiscovery disabled") + pkglog.Debug("Cluster check Autodiscovery disabled") } wg := sync.WaitGroup{} // Autoscaler Controller Goroutine - if config.Datadog.GetBool("external_metrics_provider.enabled") { + if pkgconfig.Datadog.GetBool("external_metrics_provider.enabled") { // Start the k8s custom metrics server. This is a blocking call wg.Add(1) go func() { @@ -334,25 +280,25 @@ func start(cmd *cobra.Command, args []string) error { errServ := custommetrics.RunServer(mainCtx, apiCl) if errServ != nil { - log.Errorf("Error in the External Metrics API Server: %v", errServ) + pkglog.Errorf("Error in the External Metrics API Server: %v", errServ) } }() } // Compliance - if config.Datadog.GetBool("compliance_config.enabled") { + if pkgconfig.Datadog.GetBool("compliance_config.enabled") { wg.Add(1) go func() { defer wg.Done() if err := runCompliance(mainCtx, apiCl, le.IsLeader); err != nil { - log.Errorf("Error while running compliance agent: %v", err) + pkglog.Errorf("Error while running compliance agent: %v", err) } }() } - if config.Datadog.GetBool("admission_controller.enabled") { - if config.Datadog.GetBool("admission_controller.auto_instrumentation.patcher.enabled") { + if pkgconfig.Datadog.GetBool("admission_controller.enabled") { + if pkgconfig.Datadog.GetBool("admission_controller.auto_instrumentation.patcher.enabled") { patchCtx := admissionpatch.ControllerContext{ IsLeaderFunc: le.IsLeader, K8sClient: apiCl.Cl, @@ -379,14 +325,14 @@ func start(cmd *cobra.Command, args []string) error { err = admissionpkg.StartControllers(admissionCtx) if err != nil { - log.Errorf("Could not start admission controller: %v", err) + pkglog.Errorf("Could not start admission controller: %v", err) } else { // Webhook and secret controllers are started successfully // Setup the the k8s admission webhook server server := admissioncmd.NewServer() - server.Register(config.Datadog.GetString("admission_controller.inject_config.endpoint"), mutate.InjectConfig, apiCl.DynamicCl) - server.Register(config.Datadog.GetString("admission_controller.inject_tags.endpoint"), mutate.InjectTags, apiCl.DynamicCl) - server.Register(config.Datadog.GetString("admission_controller.auto_instrumentation.endpoint"), mutate.InjectAutoInstrumentation, apiCl.DynamicCl) + server.Register(pkgconfig.Datadog.GetString("admission_controller.inject_config.endpoint"), mutate.InjectConfig, apiCl.DynamicCl) + server.Register(pkgconfig.Datadog.GetString("admission_controller.inject_tags.endpoint"), mutate.InjectTags, apiCl.DynamicCl) + server.Register(pkgconfig.Datadog.GetString("admission_controller.auto_instrumentation.endpoint"), mutate.InjectAutoInstrumentation, apiCl.DynamicCl) // Start the k8s admission webhook server wg.Add(1) @@ -395,15 +341,15 @@ func start(cmd *cobra.Command, args []string) error { errServ := server.Run(mainCtx, apiCl.Cl) if errServ != nil { - log.Errorf("Error in the Admission Controller Webhook Server: %v", errServ) + pkglog.Errorf("Error in the Admission Controller Webhook Server: %v", errServ) } }() } } else { - log.Info("Admission controller is disabled") + pkglog.Info("Admission controller is disabled") } - log.Infof("All components started. Cluster Agent now running.") + pkglog.Infof("All components started. Cluster Agent now running.") // Block here until we receive the interrupt signal <-signalCh @@ -412,32 +358,52 @@ func start(cmd *cobra.Command, args []string) error { // GetReadyNonBlocking has a 100ms timeout to avoid blocking health, err := health.GetReadyNonBlocking() if err != nil { - log.Warnf("Cluster Agent health unknown: %s", err) + pkglog.Warnf("Cluster Agent health unknown: %s", err) } else if len(health.Unhealthy) > 0 { - log.Warnf("Some components were unhealthy: %v", health.Unhealthy) + pkglog.Warnf("Some components were unhealthy: %v", health.Unhealthy) } // Cancel the main context to stop components mainCtxCancel() - // wait for the External Metrics Server and - // the Admission Webhook Server to stop properly + // wait for the External Metrics Server and the Admission Webhook Server to + // stop properly wg.Wait() - if stopCh != nil { - close(stopCh) - } + close(stopCh) demux.Stop(true) if err := metricsServer.Shutdown(context.Background()); err != nil { - log.Errorf("Error shutdowning metrics server on port %d: %v", metricsPort, err) + pkglog.Errorf("Error shutdowning metrics server on port %d: %v", metricsPort, err) } - log.Info("See ya!") - log.Flush() + pkglog.Info("See ya!") + pkglog.Flush() + return nil } +// initRuntimeSettings builds the map of runtime Cluster Agent settings configurable at runtime. +func initRuntimeSettings() error { + if err := commonsettings.RegisterRuntimeSetting(commonsettings.LogLevelRuntimeSetting{}); err != nil { + return err + } + + if err := commonsettings.RegisterRuntimeSetting(commonsettings.RuntimeMutexProfileFraction("runtime_mutex_profile_fraction")); err != nil { + return err + } + + if err := commonsettings.RegisterRuntimeSetting(commonsettings.RuntimeBlockProfileRate("runtime_block_profile_rate")); err != nil { + return err + } + + if err := commonsettings.RegisterRuntimeSetting(commonsettings.ProfilingGoroutines("internal_profiling_goroutines")); err != nil { + return err + } + + return commonsettings.RegisterRuntimeSetting(commonsettings.ProfilingRuntimeSetting{SettingName: "internal_profiling", Service: "datadog-cluster-agent"}) +} + func setupClusterCheck(ctx context.Context) (*clusterchecks.Handler, error) { handler, err := clusterchecks.NewHandler(common.AC) if err != nil { @@ -445,7 +411,7 @@ func setupClusterCheck(ctx context.Context) (*clusterchecks.Handler, error) { } go handler.Run(ctx) - log.Info("Started cluster check Autodiscovery") + pkglog.Info("Started cluster check Autodiscovery") return handler, nil } diff --git a/cmd/cluster-agent/subcommands/start/command_test.go b/cmd/cluster-agent/subcommands/start/command_test.go new file mode 100644 index 0000000000000..09da8ef3b1527 --- /dev/null +++ b/cmd/cluster-agent/subcommands/start/command_test.go @@ -0,0 +1,24 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +package start + +import ( + "testing" + + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +func TestCommand(t *testing.T) { + fxutil.TestOneShotSubcommand(t, + Commands(&command.GlobalParams{}), + []string{"start"}, + start, + func() {}) +} diff --git a/cmd/cluster-agent/app/compliance.go b/cmd/cluster-agent/subcommands/start/compliance.go similarity index 98% rename from cmd/cluster-agent/app/compliance.go rename to cmd/cluster-agent/subcommands/start/compliance.go index d47f1dec362a4..9a08aff29cb21 100644 --- a/cmd/cluster-agent/app/compliance.go +++ b/cmd/cluster-agent/subcommands/start/compliance.go @@ -3,10 +3,10 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build kubeapiserver -// +build kubeapiserver +//go:build !windows && kubeapiserver +// +build !windows,kubeapiserver -package app +package start import ( "context" diff --git a/cmd/cluster-agent/subcommands/status/command.go b/cmd/cluster-agent/subcommands/status/command.go new file mode 100644 index 0000000000000..38219abee6bde --- /dev/null +++ b/cmd/cluster-agent/subcommands/status/command.go @@ -0,0 +1,114 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package status implements 'cluster-agent status'. +package status + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/pkg/api/util" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/status" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" + "go.uber.org/fx" +) + +type cliParams struct { + jsonStatus bool + prettyPrintJSON bool + statusFilePath string +} + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cliParams := &cliParams{} + cmd := &cobra.Command{ + Use: "status", + Short: "Print the current status", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + return fxutil.OneShot(run, + fx.Supply(cliParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForOneShot(command.LoggerName, command.DefaultLogLevel, true), + }), + core.Bundle, + ) + }, + } + + cmd.Flags().BoolVarP(&cliParams.jsonStatus, "json", "j", false, "print out raw json") + cmd.Flags().BoolVarP(&cliParams.prettyPrintJSON, "pretty-json", "p", false, "pretty print JSON") + cmd.Flags().StringVarP(&cliParams.statusFilePath, "file", "o", "", "Output the status command to a file") + + return []*cobra.Command{cmd} +} + +func run(log log.Component, config config.Component, cliParams *cliParams) error { + fmt.Printf("Getting the status from the agent.\n") + var e error + var s string + c := util.GetClient(false) // FIX: get certificates right then make this true + // TODO use https + urlstr := fmt.Sprintf("https://localhost:%v/status", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port")) + + // Set session token + e = util.SetAuthToken() + if e != nil { + return e + } + + r, e := util.DoGet(c, urlstr, util.LeaveConnectionOpen) + if e != nil { + var errMap = make(map[string]string) + json.Unmarshal(r, &errMap) //nolint:errcheck + // If the error has been marshalled into a json object, check it and return it properly + if err, found := errMap["error"]; found { + e = fmt.Errorf(err) + } + + fmt.Printf(` + Could not reach agent: %v + Make sure the agent is running before requesting the status. + Contact support if you continue having issues.`, e) + return e + } + + // 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, r, "", " ") //nolint:errcheck + s = prettyJSON.String() + } else if cliParams.jsonStatus { + s = string(r) + } else { + formattedStatus, err := status.FormatDCAStatus(r) + if err != nil { + return err + } + s = formattedStatus + } + + if cliParams.statusFilePath != "" { + os.WriteFile(cliParams.statusFilePath, []byte(s), 0644) //nolint:errcheck + } else { + fmt.Println(s) + } + + return nil +} diff --git a/cmd/cluster-agent/subcommands/subcommands.go b/cmd/cluster-agent/subcommands/subcommands.go new file mode 100644 index 0000000000000..92956210d3439 --- /dev/null +++ b/cmd/cluster-agent/subcommands/subcommands.go @@ -0,0 +1,48 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +package subcommands + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + cmdcheck "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/check" + cmdclusterchecks "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/clusterchecks" + cmdcompliance "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/compliance" + cmdconfig "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/config" + cmdconfigcheck "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/configcheck" + cmddiagnose "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/diagnose" + cmdflare "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/flare" + cmdhealth "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/health" + cmdmetamap "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/metamap" + cmdsecrethelper "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/secrethelper" + cmdstart "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/start" + cmdstatus "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/status" + cmdtelemetry "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/telemetry" + cmdversion "github.com/DataDog/datadog-agent/cmd/cluster-agent/subcommands/version" +) + +// ClusterAgentSubcommands returns SubcommandFactories for the subcommands +// supported with the current build flags. +func ClusterAgentSubcommands() []command.SubcommandFactory { + return []command.SubcommandFactory{ + cmdstart.Commands, + cmdversion.Commands, + cmdcheck.Commands, + cmdconfig.Commands, + cmdhealth.Commands, + cmdconfigcheck.Commands, + cmdclusterchecks.Commands, + cmdcompliance.Commands, + cmdflare.Commands, + cmddiagnose.Commands, + cmdmetamap.Commands, + cmdsecrethelper.Commands, + cmdtelemetry.Commands, + cmdstatus.Commands, + } +} diff --git a/cmd/cluster-agent/subcommands/telemetry/command.go b/cmd/cluster-agent/subcommands/telemetry/command.go new file mode 100644 index 0000000000000..fbf27e285b4a5 --- /dev/null +++ b/cmd/cluster-agent/subcommands/telemetry/command.go @@ -0,0 +1,37 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package telemetry implements 'cluster-agent telemetry'. +package telemetry + +import ( + "fmt" + + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/flare" + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + cmd := &cobra.Command{ + Use: "telemetry", + Short: "Print the telemetry metrics exposed by the cluster agent", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + payload, err := flare.QueryDCAMetrics() + if err != nil { + return err + } + fmt.Print(string(payload)) + return nil + }, + } + + return []*cobra.Command{cmd} +} diff --git a/cmd/cluster-agent/subcommands/version/command.go b/cmd/cluster-agent/subcommands/version/command.go new file mode 100644 index 0000000000000..10fb87995956a --- /dev/null +++ b/cmd/cluster-agent/subcommands/version/command.go @@ -0,0 +1,22 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +// Package version implements 'cluster-agent version'. +package version + +import ( + "github.com/DataDog/datadog-agent/cmd/cluster-agent/command" + "github.com/DataDog/datadog-agent/pkg/cli/subcommands/version" + + "github.com/spf13/cobra" +) + +// Commands returns a slice of subcommands for the 'cluster-agent' command. +func Commands(globalParams *command.GlobalParams) []*cobra.Command { + return []*cobra.Command{version.MakeCommand("Cluster Agent")} +} diff --git a/comp/core/config/params.go b/comp/core/config/params.go index 9f2e7ab72c390..0b72c8f1b2f7f 100644 --- a/comp/core/config/params.go +++ b/comp/core/config/params.go @@ -93,6 +93,12 @@ func NewSecurityAgentParams(securityAgentConfigFilePaths []string, options ...fu return params } +func NewClusterAgentParams(configFilePath string, options ...func(*Params)) Params { + return NewParams(common.DefaultConfPath, + WithConfFilePath(configFilePath), + WithConfigName("datadog-cluster")) +} + func WithConfigName(name string) func(*Params) { return func(b *Params) { b.configName = name diff --git a/pkg/cli/subcommands/check/command.go b/pkg/cli/subcommands/check/command.go index 3343a7379afc2..e67366184d594 100644 --- a/pkg/cli/subcommands/check/command.go +++ b/pkg/cli/subcommands/check/command.go @@ -156,6 +156,9 @@ func run(log log.Component, config config.Component, cliParams *cliParams) error if cliParams.generateIntegrationTraces { if pkgconfig.Datadog.IsSet("integration_tracing") { previousIntegrationTracing = pkgconfig.Datadog.GetBool("integration_tracing") + + } + if pkgconfig.Datadog.IsSet("integration_tracing_exhaustive") { previousIntegrationTracingExhaustive = pkgconfig.Datadog.GetBool("integration_tracing_exhaustive") } pkgconfig.Datadog.Set("integration_tracing", true) diff --git a/pkg/cli/subcommands/clusterchecks/command.go b/pkg/cli/subcommands/clusterchecks/command.go new file mode 100644 index 0000000000000..109e55e342e5f --- /dev/null +++ b/pkg/cli/subcommands/clusterchecks/command.go @@ -0,0 +1,135 @@ +// 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 clusterchecks builds a 'clusterchecks' command to be used in binaries. +package clusterchecks + +import ( + "bytes" + "encoding/json" + "fmt" + + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/pkg/api/util" + "github.com/DataDog/datadog-agent/pkg/clusteragent/clusterchecks/types" + pkgconfig "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/flare" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/fatih/color" + "github.com/spf13/cobra" + "go.uber.org/fx" +) + +const ( + loggerName = "CLUSTER" + defaultLogLevel = "off" +) + +type GlobalParams struct { + ConfFilePath string +} + +type cliParams struct { + checkName string +} + +// MakeCommand returns a `clusterchecks` command to be used by cluster-agent +// binaries. +func MakeCommand(globalParamsGetter func() GlobalParams) *cobra.Command { + cliParams := &cliParams{} + + cmd := &cobra.Command{ + Use: "clusterchecks", + Short: "Prints the active cluster check configurations", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + globalParams := globalParamsGetter() + + return fxutil.OneShot(run, + fx.Supply(cliParams), + fx.Supply(bundleParams(globalParams)), + core.Bundle, + ) + }, + } + + cmd.Flags().StringVarP(&cliParams.checkName, "check", "", "", "the check name to filter for") + + rebalanceCmd := &cobra.Command{ + Use: "rebalance", + Short: "Rebalances cluster checks", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + globalParams := globalParamsGetter() + + return fxutil.OneShot(rebalance, + fx.Supply(bundleParams(globalParams)), + core.Bundle, + ) + }, + } + + cmd.AddCommand(rebalanceCmd) + + return cmd +} + +func bundleParams(globalParams GlobalParams) core.BundleParams { + return core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath, config.WithConfigLoadSecrets(true)), + LogParams: log.LogForOneShot(loggerName, defaultLogLevel, true), + } +} + +func run(log log.Component, config config.Component, cliParams *cliParams) error { + if err := flare.GetClusterChecks(color.Output, cliParams.checkName); err != nil { + return err + } + + return flare.GetEndpointsChecks(color.Output, cliParams.checkName) +} + +func rebalance(log log.Component, config config.Component) error { + fmt.Println("Requesting a cluster check rebalance...") + c := util.GetClient(false) // FIX: get certificates right then make this true + urlstr := fmt.Sprintf("https://localhost:%v/api/v1/clusterchecks/rebalance", pkgconfig.Datadog.GetInt("cluster_agent.cmd_port")) + + // Set session token + err := util.SetAuthToken() + if err != nil { + return err + } + + r, err := util.DoPost(c, urlstr, "application/json", bytes.NewBuffer([]byte{})) + if err != nil { + var errMap = make(map[string]string) + json.Unmarshal(r, &errMap) //nolint:errcheck + // If the error has been marshalled into a json object, check it and return it properly + if e, found := errMap["error"]; found { + err = fmt.Errorf(e) + } + + fmt.Printf(` + Could not reach agent: %v + Make sure the agent is running before requesting the cluster checks rebalancing. + Contact support if you continue having issues.`, err) + + return err + } + + checksMoved := make([]types.RebalanceResponse, 0) + json.Unmarshal(r, &checksMoved) //nolint:errcheck + + fmt.Printf("%d cluster checks rebalanced successfully\n", len(checksMoved)) + + for _, check := range checksMoved { + fmt.Printf("Check %s with weight %d moved from node %s to %s. source diff: %d, dest diff: %d\n", + check.CheckID, check.CheckWeight, check.SourceNodeName, check.DestNodeName, check.SourceDiff, check.DestDiff) + } + + return nil +} diff --git a/pkg/cli/subcommands/clusterchecks/command_test.go b/pkg/cli/subcommands/clusterchecks/command_test.go new file mode 100644 index 0000000000000..ac2ecfccaef2f --- /dev/null +++ b/pkg/cli/subcommands/clusterchecks/command_test.go @@ -0,0 +1,44 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +package clusterchecks + +import ( + "testing" + + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" +) + +func TestCommand(t *testing.T) { + commands := []*cobra.Command{ + MakeCommand(func() GlobalParams { + return GlobalParams{} + }), + } + + fxutil.TestOneShotSubcommand(t, + commands, + []string{"clusterchecks"}, + run, + func() {}) +} + +func TestRebalance(t *testing.T) { + commands := []*cobra.Command{ + MakeCommand(func() GlobalParams { + return GlobalParams{} + }), + } + + fxutil.TestOneShotSubcommand(t, + commands, + []string{"clusterchecks", "rebalance"}, + rebalance, + func() {}) +} diff --git a/pkg/cli/subcommands/dcaconfigcheck/command.go b/pkg/cli/subcommands/dcaconfigcheck/command.go new file mode 100644 index 0000000000000..d08decd206f5c --- /dev/null +++ b/pkg/cli/subcommands/dcaconfigcheck/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. + +// Package dcaconfigcheck builds a 'configcheck' command to be used in binaries. +package dcaconfigcheck + +import ( + "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/pkg/flare" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/fatih/color" + "github.com/spf13/cobra" + "go.uber.org/fx" +) + +type GlobalParams struct { + ConfFilePath string +} + +type cliParams struct { + verbose bool +} + +// MakeCommand returns a `configcheck` command to be used by cluster-agent +// binaries. +func MakeCommand(globalParamsGetter func() GlobalParams) *cobra.Command { + cliParams := &cliParams{} + cmd := &cobra.Command{ + Use: "configcheck", + Aliases: []string{"checkconfig"}, + Short: "Print all configurations loaded & resolved of a running cluster agent", + Long: ``, + RunE: func(cmd *cobra.Command, args []string) error { + globalParams := globalParamsGetter() + + return fxutil.OneShot(run, + fx.Supply(cliParams), + fx.Supply(core.BundleParams{ + ConfigParams: config.NewClusterAgentParams(globalParams.ConfFilePath), + LogParams: log.LogForOneShot("CLUSTER", "off", true), + }), + core.Bundle, + ) + }, + } + + cmd.Flags().BoolVarP(&cliParams.verbose, "verbose", "v", false, "print additional debug info") + + return cmd +} + +func run(log log.Component, config config.Component, cliParams *cliParams) error { + return flare.GetClusterAgentConfigCheck(color.Output, cliParams.verbose) +} diff --git a/pkg/cli/subcommands/dcaconfigcheck/command_test.go b/pkg/cli/subcommands/dcaconfigcheck/command_test.go new file mode 100644 index 0000000000000..c7136be5a09c6 --- /dev/null +++ b/pkg/cli/subcommands/dcaconfigcheck/command_test.go @@ -0,0 +1,30 @@ +// 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 && kubeapiserver +// +build !windows,kubeapiserver + +package dcaconfigcheck + +import ( + "testing" + + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/spf13/cobra" +) + +func TestCommand(t *testing.T) { + commands := []*cobra.Command{ + MakeCommand(func() GlobalParams { + return GlobalParams{} + }), + } + + fxutil.TestOneShotSubcommand(t, + commands, + []string{"configcheck"}, + run, + func() {}) +}