From d640c306b4f59e3465c393d57d571d3be248f30e Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Wed, 11 Jan 2023 18:18:42 +0100 Subject: [PATCH 01/14] better error msg --- pkg/cli/subcommands/check/command.go | 37 +++++++++++++++++++++++ pkg/cli/subcommands/check/command_test.go | 20 ++++++++++++ 2 files changed, 57 insertions(+) diff --git a/pkg/cli/subcommands/check/command.go b/pkg/cli/subcommands/check/command.go index ecdae2924c40dc..a7708c02f66af1 100644 --- a/pkg/cli/subcommands/check/command.go +++ b/pkg/cli/subcommands/check/command.go @@ -39,6 +39,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/status" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/hostname" + "github.com/DataDog/datadog-agent/pkg/util/jsonquery" "github.com/DataDog/datadog-agent/pkg/util/scrubber" ) @@ -56,6 +57,7 @@ type cliParams struct { checkTimes int checkPause int checkName string + instanceFilter string checkDelay int logLevel string formatJSON bool @@ -118,6 +120,7 @@ func MakeCommand(globalParamsGetter func() GlobalParams) *cobra.Command { cmd.Flags().IntVar(&cliParams.checkPause, "pause", 0, "pause between multiple runs of the check, in milliseconds") cmd.Flags().StringVarP(&cliParams.logLevel, "log-level", "l", "", "set the log level (default 'off') (deprecated, use the env var DD_LOG_LEVEL instead)") cmd.Flags().IntVarP(&cliParams.checkDelay, "delay", "d", 100, "delay between running the check and grabbing the metrics in milliseconds") + cmd.Flags().StringVarP(&cliParams.instanceFilter, "instance-filter", "", "", "TODO:XXX") cmd.Flags().BoolVarP(&cliParams.formatJSON, "json", "", false, "format aggregator and check runner output as json") cmd.Flags().BoolVarP(&cliParams.formatTable, "table", "", false, "format aggregator and check runner output as an ascii table") cmd.Flags().StringVarP(&cliParams.breakPoint, "breakpoint", "b", "", "set a breakpoint at a particular line number (Python checks only)") @@ -193,6 +196,25 @@ func run(log log.Component, config config.Component, cliParams *cliParams) error allConfigs := common.WaitForConfigsFromAD(waitCtx, []string{cliParams.checkName}, int(cliParams.discoveryMinInstances)) cancelTimeout() + if cliParams.instanceFilter != "" { + var newAllConfigs []integration.Config + for _, conf := range allConfigs { + var newInstances []integration.Data + for _, instance := range conf.Instances { + exist, err := YAMLExistQuery(instance, cliParams.instanceFilter) + if err != nil { + return fmt.Errorf("instance filter error: %v", err) + } + if exist { + newInstances = append(newInstances, instance) + } + } + conf.Instances = newInstances + newAllConfigs = append(newAllConfigs, conf) + } + allConfigs = newAllConfigs + } + // make sure the checks in cs are not JMX checks for idx := range allConfigs { conf := &allConfigs[idx] @@ -628,3 +650,18 @@ func populateMemoryProfileConfig(cliParams *cliParams, initConfig map[string]int func disableCmdPort() { os.Setenv("DD_CMD_PORT", "0") // 0 indicates the OS should pick an unused port } + +// YAMLExistQuery check a property/value from a YAML exist (jq style syntax) +func YAMLExistQuery(data []byte, query string) (bool, error) { + var yamlContent interface{} + if err := yaml.Unmarshal(data, &yamlContent); err != nil { + return false, err + } + yamlContent = jsonquery.NormalizeYAMLForGoJQ(yamlContent) + output, _, err := jsonquery.RunSingleOutput(query, yamlContent) + var exist bool + if err := yaml.Unmarshal([]byte(output), &exist); err != nil { + return false, fmt.Errorf("filter query must return a boolean: %s", err) + } + return exist, err +} diff --git a/pkg/cli/subcommands/check/command_test.go b/pkg/cli/subcommands/check/command_test.go index 6be7e7e86db24d..61fc8774ab9f1b 100644 --- a/pkg/cli/subcommands/check/command_test.go +++ b/pkg/cli/subcommands/check/command_test.go @@ -9,9 +9,11 @@ import ( "testing" "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/DataDog/datadog-agent/comp/core" + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -34,3 +36,21 @@ func TestCommand(t *testing.T) { require.Equal(t, true, coreParams.ConfigLoadSecrets()) }) } + +func TestYAMLExistQuery(t *testing.T) { + exist, err := YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.50\"") + assert.NoError(t, err) + assert.True(t, exist) + + exist, err = YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.99\"") + assert.NoError(t, err) + assert.False(t, exist) + + exist, err = YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address") + assert.EqualError(t, err, "filter query must return a boolean: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `127.0.0.50` into bool") + assert.False(t, exist) + + exist, err = YAMLExistQuery(integration.Data("{}"), ".ip_address == \"127.0.0.99\"") + assert.NoError(t, err) + assert.False(t, exist) +} From 9e76794c65f1e71646c0d64aaeea0faba5c71ba5 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Wed, 11 Jan 2023 18:26:48 +0100 Subject: [PATCH 02/14] better help --- pkg/cli/subcommands/check/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cli/subcommands/check/command.go b/pkg/cli/subcommands/check/command.go index a7708c02f66af1..b459349e7d0582 100644 --- a/pkg/cli/subcommands/check/command.go +++ b/pkg/cli/subcommands/check/command.go @@ -120,7 +120,7 @@ func MakeCommand(globalParamsGetter func() GlobalParams) *cobra.Command { cmd.Flags().IntVar(&cliParams.checkPause, "pause", 0, "pause between multiple runs of the check, in milliseconds") cmd.Flags().StringVarP(&cliParams.logLevel, "log-level", "l", "", "set the log level (default 'off') (deprecated, use the env var DD_LOG_LEVEL instead)") cmd.Flags().IntVarP(&cliParams.checkDelay, "delay", "d", 100, "delay between running the check and grabbing the metrics in milliseconds") - cmd.Flags().StringVarP(&cliParams.instanceFilter, "instance-filter", "", "", "TODO:XXX") + cmd.Flags().StringVarP(&cliParams.instanceFilter, "instance-filter", "", "", "filter instances using jq style syntax, example: --instance-filter '.ip_address == \"127.0.0.51\"'") cmd.Flags().BoolVarP(&cliParams.formatJSON, "json", "", false, "format aggregator and check runner output as json") cmd.Flags().BoolVarP(&cliParams.formatTable, "table", "", false, "format aggregator and check runner output as an ascii table") cmd.Flags().StringVarP(&cliParams.breakPoint, "breakpoint", "b", "", "set a breakpoint at a particular line number (Python checks only)") From 95565408c8e7f78c4e35fe19a0465a09cae11224 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Wed, 11 Jan 2023 18:32:41 +0100 Subject: [PATCH 03/14] Add reno --- ...ilter_to_agent_check_command-c166bec4ff8ebf96.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml diff --git a/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml b/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml new file mode 100644 index 00000000000000..72c77abd66de2c --- /dev/null +++ b/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + Add ``--instance-filter`` option to agent check command. From 9f45c3bd6a2934c6316b87550fe2d204b1eee16f Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Wed, 11 Jan 2023 18:46:13 +0100 Subject: [PATCH 04/14] instance filter --- cmd/agent/common/autodiscovery.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index e3d1e7f14cdafb..1906cf66bd53ef 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -269,6 +269,7 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin // placing items in configChan go AC.AddScheduler("check-cmd", schedulerFunc(func(configs []integration.Config) { for _, cfg := range configs { + // TODO: apply --instance-filter here if match(cfg) && waiting.Load() { configChan <- cfg } From a989b57f8643152c8d944f6d86772e7911baf671 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 09:32:22 +0100 Subject: [PATCH 05/14] Update releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml Co-authored-by: Austin Lai <76412946+alai97@users.noreply.github.com> --- ...instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml b/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml index 72c77abd66de2c..7d25f7775d6a55 100644 --- a/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml +++ b/releasenotes/notes/add_instance_filter_to_agent_check_command-c166bec4ff8ebf96.yaml @@ -8,4 +8,4 @@ --- enhancements: - | - Add ``--instance-filter`` option to agent check command. + Add an ``--instance-filter`` option to the Agent check command. From 40fe2010fa30af9a3de346b5b2a6e5eef8d0b9ac Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:14:04 +0100 Subject: [PATCH 06/14] move to autodiscovery.go --- cmd/agent/common/autodiscovery.go | 43 +++++++++++++++++++---- cmd/agent/subcommands/jmx/command.go | 7 ++-- cmd/cluster-agent/commands/check/check.go | 2 +- pkg/cli/subcommands/check/command.go | 39 +++----------------- pkg/cli/subcommands/check/command_test.go | 20 ----------- pkg/util/jsonquery/yaml.go | 16 +++++++++ pkg/util/jsonquery/yaml_test.go | 25 +++++++++++++ 7 files changed, 87 insertions(+), 65 deletions(-) create mode 100644 pkg/util/jsonquery/yaml_test.go diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index 1906cf66bd53ef..17d4f4c8d78e9d 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -7,6 +7,8 @@ package common import ( "context" + "fmt" + "github.com/DataDog/datadog-agent/pkg/util/jsonquery" "time" "go.uber.org/atomic" @@ -211,14 +213,14 @@ func (sf schedulerFunc) Stop() { // // If the context is cancelled, then any accumulated, matching changes are // returned, even if that is fewer than discoveryMinInstances. -func WaitForConfigsFromAD(ctx context.Context, checkNames []string, discoveryMinInstances int) (configs []integration.Config) { - return waitForConfigsFromAD(ctx, false, checkNames, discoveryMinInstances) +func WaitForConfigsFromAD(ctx context.Context, checkNames []string, discoveryMinInstances int, instanceFilter string) (configs []integration.Config, lastError error) { + return waitForConfigsFromAD(ctx, false, checkNames, discoveryMinInstances, instanceFilter) } // WaitForAllConfigsFromAD waits until its context expires, and then returns // the full set of checks scheduled by AD. -func WaitForAllConfigsFromAD(ctx context.Context) (configs []integration.Config) { - return waitForConfigsFromAD(ctx, true, []string{}, 0) +func WaitForAllConfigsFromAD(ctx context.Context) (configs []integration.Config, lastError error) { + return waitForConfigsFromAD(ctx, true, []string{}, 0, "") } // waitForConfigsFromAD waits for configs from the AD scheduler and returns them. @@ -234,7 +236,7 @@ func WaitForAllConfigsFromAD(ctx context.Context) (configs []integration.Config) // If wildcard is true, this gathers all configs scheduled before the context // is cancelled, and then returns. It will not return before the context is // cancelled. -func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []string, discoveryMinInstances int) (configs []integration.Config) { +func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []string, discoveryMinInstances int, instanceFilter string) (configs []integration.Config, returnErr error) { configChan := make(chan integration.Config) // signal to the scheduler when we are no longer waiting, so we do not continue @@ -265,12 +267,20 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin } } + stopChan := make(chan struct{}) // add the scheduler in a goroutine, since it will schedule any "catch-up" immediately, // placing items in configChan go AC.AddScheduler("check-cmd", schedulerFunc(func(configs []integration.Config) { for _, cfg := range configs { - // TODO: apply --instance-filter here - if match(cfg) && waiting.Load() { + instances, err := filterInstances(cfg.Instances, instanceFilter) + if err != nil { + returnErr = err + stopChan <- struct{}{} + break + } + cfg.Instances = instances + + if len(cfg.Instances) > 0 && match(cfg) && waiting.Load() { configChan <- cfg } } @@ -280,9 +290,28 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin select { case cfg := <-configChan: configs = append(configs, cfg) + case <-stopChan: + return case <-ctx.Done(): return } } return } + +func filterInstances(instances []integration.Data, instanceFilter string) ([]integration.Data, error) { + if instanceFilter == "" { + return instances, nil + } + var newInstances []integration.Data + for _, instance := range instances { + exist, err := jsonquery.YAMLCheckExist(instance, instanceFilter) + if err != nil { + return nil, fmt.Errorf("instance filter errorxx: %v", err) + } + if exist { + newInstances = append(newInstances, instance) + } + } + return newInstances, nil +} diff --git a/cmd/agent/subcommands/jmx/command.go b/cmd/agent/subcommands/jmx/command.go index 7db96d29643077..99a88f595a84f5 100644 --- a/cmd/agent/subcommands/jmx/command.go +++ b/cmd/agent/subcommands/jmx/command.go @@ -241,9 +241,12 @@ func runJmxCommandConsole(log log.Component, config config.Component, cliParams context.Background(), time.Duration(cliParams.discoveryTimeout)*time.Second) var allConfigs []integration.Config if len(cliParams.cliSelectedChecks) == 0 { - allConfigs = common.WaitForAllConfigsFromAD(waitCtx) + allConfigs, err = common.WaitForAllConfigsFromAD(waitCtx) } else { - allConfigs = common.WaitForConfigsFromAD(waitCtx, cliParams.cliSelectedChecks, int(cliParams.discoveryMinInstances)) + allConfigs, err = common.WaitForConfigsFromAD(waitCtx, cliParams.cliSelectedChecks, int(cliParams.discoveryMinInstances), "") + } + if err != nil { + return err } cancelTimeout() diff --git a/cmd/cluster-agent/commands/check/check.go b/cmd/cluster-agent/commands/check/check.go index 166cfced98778e..d149bb1a29397b 100644 --- a/cmd/cluster-agent/commands/check/check.go +++ b/cmd/cluster-agent/commands/check/check.go @@ -164,7 +164,7 @@ func Check(loggerName config.LoggerName, confFilePath *string, flagNoColor *bool waitCtx, cancelTimeout := context.WithTimeout( context.Background(), time.Duration(discoveryTimeout)*time.Second) - allConfigs := common.WaitForConfigsFromAD(waitCtx, []string{checkName}, int(discoveryMinInstances)) + allConfigs := common.WaitForConfigsFromAD(waitCtx, []string{checkName}, int(discoveryMinInstances), "") cancelTimeout() // make sure the checks in cs are not JMX checks diff --git a/pkg/cli/subcommands/check/command.go b/pkg/cli/subcommands/check/command.go index b459349e7d0582..f874473ad15753 100644 --- a/pkg/cli/subcommands/check/command.go +++ b/pkg/cli/subcommands/check/command.go @@ -39,7 +39,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/status" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/hostname" - "github.com/DataDog/datadog-agent/pkg/util/jsonquery" "github.com/DataDog/datadog-agent/pkg/util/scrubber" ) @@ -193,26 +192,11 @@ func run(log log.Component, config config.Component, cliParams *cliParams) error waitCtx, cancelTimeout := context.WithTimeout( context.Background(), time.Duration(cliParams.discoveryTimeout)*time.Second) - allConfigs := common.WaitForConfigsFromAD(waitCtx, []string{cliParams.checkName}, int(cliParams.discoveryMinInstances)) - cancelTimeout() - if cliParams.instanceFilter != "" { - var newAllConfigs []integration.Config - for _, conf := range allConfigs { - var newInstances []integration.Data - for _, instance := range conf.Instances { - exist, err := YAMLExistQuery(instance, cliParams.instanceFilter) - if err != nil { - return fmt.Errorf("instance filter error: %v", err) - } - if exist { - newInstances = append(newInstances, instance) - } - } - conf.Instances = newInstances - newAllConfigs = append(newAllConfigs, conf) - } - allConfigs = newAllConfigs + allConfigs, err := common.WaitForConfigsFromAD(waitCtx, []string{cliParams.checkName}, int(cliParams.discoveryMinInstances), cliParams.instanceFilter) + cancelTimeout() + if err != nil { + return err } // make sure the checks in cs are not JMX checks @@ -650,18 +634,3 @@ func populateMemoryProfileConfig(cliParams *cliParams, initConfig map[string]int func disableCmdPort() { os.Setenv("DD_CMD_PORT", "0") // 0 indicates the OS should pick an unused port } - -// YAMLExistQuery check a property/value from a YAML exist (jq style syntax) -func YAMLExistQuery(data []byte, query string) (bool, error) { - var yamlContent interface{} - if err := yaml.Unmarshal(data, &yamlContent); err != nil { - return false, err - } - yamlContent = jsonquery.NormalizeYAMLForGoJQ(yamlContent) - output, _, err := jsonquery.RunSingleOutput(query, yamlContent) - var exist bool - if err := yaml.Unmarshal([]byte(output), &exist); err != nil { - return false, fmt.Errorf("filter query must return a boolean: %s", err) - } - return exist, err -} diff --git a/pkg/cli/subcommands/check/command_test.go b/pkg/cli/subcommands/check/command_test.go index 61fc8774ab9f1b..6be7e7e86db24d 100644 --- a/pkg/cli/subcommands/check/command_test.go +++ b/pkg/cli/subcommands/check/command_test.go @@ -9,11 +9,9 @@ import ( "testing" "github.com/spf13/cobra" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/DataDog/datadog-agent/comp/core" - "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -36,21 +34,3 @@ func TestCommand(t *testing.T) { require.Equal(t, true, coreParams.ConfigLoadSecrets()) }) } - -func TestYAMLExistQuery(t *testing.T) { - exist, err := YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.50\"") - assert.NoError(t, err) - assert.True(t, exist) - - exist, err = YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.99\"") - assert.NoError(t, err) - assert.False(t, exist) - - exist, err = YAMLExistQuery(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address") - assert.EqualError(t, err, "filter query must return a boolean: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `127.0.0.50` into bool") - assert.False(t, exist) - - exist, err = YAMLExistQuery(integration.Data("{}"), ".ip_address == \"127.0.0.99\"") - assert.NoError(t, err) - assert.False(t, exist) -} diff --git a/pkg/util/jsonquery/yaml.go b/pkg/util/jsonquery/yaml.go index 58cfba9742efe2..0cdf380e52ea2e 100644 --- a/pkg/util/jsonquery/yaml.go +++ b/pkg/util/jsonquery/yaml.go @@ -7,6 +7,7 @@ package jsonquery import ( "fmt" + "gopkg.in/yaml.v3" "time" ) @@ -46,3 +47,18 @@ func NormalizeYAMLForGoJQ(v interface{}) interface{} { return v } } + +// YAMLCheckExist check a property/value from a YAML exist (jq style syntax) +func YAMLCheckExist(yamlData []byte, query string) (bool, error) { + var yamlContent interface{} + if err := yaml.Unmarshal(yamlData, &yamlContent); err != nil { + return false, err + } + yamlContent = NormalizeYAMLForGoJQ(yamlContent) + output, _, err := RunSingleOutput(query, yamlContent) + var exist bool + if err := yaml.Unmarshal([]byte(output), &exist); err != nil { + return false, fmt.Errorf("filter query must return a boolean: %s", err) + } + return exist, err +} diff --git a/pkg/util/jsonquery/yaml_test.go b/pkg/util/jsonquery/yaml_test.go new file mode 100644 index 00000000000000..2412d08d813599 --- /dev/null +++ b/pkg/util/jsonquery/yaml_test.go @@ -0,0 +1,25 @@ +package jsonquery + +import ( + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestYAMLExistQuery(t *testing.T) { + exist, err := YAMLCheckExist(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.50\"") + assert.NoError(t, err) + assert.True(t, exist) + + exist, err = YAMLCheckExist(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address == \"127.0.0.99\"") + assert.NoError(t, err) + assert.False(t, exist) + + exist, err = YAMLCheckExist(integration.Data("{\"ip_address\": \"127.0.0.50\"}"), ".ip_address") + assert.EqualError(t, err, "filter query must return a boolean: yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `127.0.0.50` into bool") + assert.False(t, exist) + + exist, err = YAMLCheckExist(integration.Data("{}"), ".ip_address == \"127.0.0.99\"") + assert.NoError(t, err) + assert.False(t, exist) +} From 723f9cc21f4f1b80f22f048facc47c2bff8e5c7a Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:29:24 +0100 Subject: [PATCH 07/14] Fix import --- cmd/agent/common/autodiscovery.go | 2 +- pkg/util/jsonquery/yaml_test.go | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index 17d4f4c8d78e9d..0051895522ff49 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -8,7 +8,6 @@ package common import ( "context" "fmt" - "github.com/DataDog/datadog-agent/pkg/util/jsonquery" "time" "go.uber.org/atomic" @@ -20,6 +19,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/autodiscovery/scheduler" "github.com/DataDog/datadog-agent/pkg/config" confad "github.com/DataDog/datadog-agent/pkg/config/autodiscovery" + "github.com/DataDog/datadog-agent/pkg/util/jsonquery" "github.com/DataDog/datadog-agent/pkg/util/log" ) diff --git a/pkg/util/jsonquery/yaml_test.go b/pkg/util/jsonquery/yaml_test.go index 2412d08d813599..b0dac5ed6112ea 100644 --- a/pkg/util/jsonquery/yaml_test.go +++ b/pkg/util/jsonquery/yaml_test.go @@ -1,9 +1,11 @@ package jsonquery import ( - "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" + + "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" ) func TestYAMLExistQuery(t *testing.T) { From 9c2894ddfaa3cabad45ab80a9e6471fb59cd94a4 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:29:45 +0100 Subject: [PATCH 08/14] fix import2 --- pkg/util/jsonquery/yaml.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/util/jsonquery/yaml.go b/pkg/util/jsonquery/yaml.go index 0cdf380e52ea2e..a980b6a59f8c1f 100644 --- a/pkg/util/jsonquery/yaml.go +++ b/pkg/util/jsonquery/yaml.go @@ -7,8 +7,9 @@ package jsonquery import ( "fmt" - "gopkg.in/yaml.v3" "time" + + "gopkg.in/yaml.v3" ) // Copy from https://github.com/itchyny/gojq/blob/main/cli/yaml.go From 1df1f6f97b5bad5ceb4e5383ba478f2a38d40433 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:36:44 +0100 Subject: [PATCH 09/14] refactor --- cmd/agent/common/autodiscovery.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index 0051895522ff49..d5241f8a321864 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -272,15 +272,20 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin // placing items in configChan go AC.AddScheduler("check-cmd", schedulerFunc(func(configs []integration.Config) { for _, cfg := range configs { - instances, err := filterInstances(cfg.Instances, instanceFilter) - if err != nil { - returnErr = err - stopChan <- struct{}{} - break + if instanceFilter != "" { + instances, err := filterInstances(cfg.Instances, instanceFilter) + if err != nil { + returnErr = err + stopChan <- struct{}{} + break + } + if len(instances) == 0 { + continue + } + cfg.Instances = instances } - cfg.Instances = instances - if len(cfg.Instances) > 0 && match(cfg) && waiting.Load() { + if match(cfg) && waiting.Load() { configChan <- cfg } } @@ -300,9 +305,6 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin } func filterInstances(instances []integration.Data, instanceFilter string) ([]integration.Data, error) { - if instanceFilter == "" { - return instances, nil - } var newInstances []integration.Data for _, instance := range instances { exist, err := jsonquery.YAMLCheckExist(instance, instanceFilter) From 7ac006adbe3c320300762a879669a77f0e53b51b Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:46:13 +0100 Subject: [PATCH 10/14] Add filter for cluster agent and jmx --- cmd/agent/subcommands/jmx/command.go | 4 +++- cmd/cluster-agent/commands/check/check.go | 7 ++++++- pkg/cli/subcommands/check/command.go | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cmd/agent/subcommands/jmx/command.go b/cmd/agent/subcommands/jmx/command.go index 99a88f595a84f5..25f723a2cdaf36 100644 --- a/cmd/agent/subcommands/jmx/command.go +++ b/cmd/agent/subcommands/jmx/command.go @@ -45,6 +45,7 @@ type cliParams struct { saveFlare bool discoveryTimeout uint discoveryMinInstances uint + instanceFilter string } // Commands returns a slice of subcommands for the 'agent' command. @@ -63,6 +64,7 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { jmxCmd.PersistentFlags().UintVarP(&cliParams.discoveryTimeout, "discovery-timeout", "", 5, "max retry duration until Autodiscovery resolves the check template (in seconds)") jmxCmd.PersistentFlags().UintVarP(&discoveryRetryInterval, "discovery-retry-interval", "", 1, "(unused)") jmxCmd.PersistentFlags().UintVarP(&cliParams.discoveryMinInstances, "discovery-min-instances", "", 1, "minimum number of config instances to be discovered before running the check(s)") + jmxCmd.PersistentFlags().StringVarP(&cliParams.instanceFilter, "instance-filter", "", "", "filter instances using jq style syntax, example: --instance-filter '.ip_address == \"127.0.0.51\"'") // All subcommands use the same provided components, with a different // oneShot callback, and with some complex derivation of the @@ -243,7 +245,7 @@ func runJmxCommandConsole(log log.Component, config config.Component, cliParams if len(cliParams.cliSelectedChecks) == 0 { allConfigs, err = common.WaitForAllConfigsFromAD(waitCtx) } else { - allConfigs, err = common.WaitForConfigsFromAD(waitCtx, cliParams.cliSelectedChecks, int(cliParams.discoveryMinInstances), "") + allConfigs, err = common.WaitForConfigsFromAD(waitCtx, cliParams.cliSelectedChecks, int(cliParams.discoveryMinInstances), cliParams.instanceFilter) } if err != nil { return err diff --git a/cmd/cluster-agent/commands/check/check.go b/cmd/cluster-agent/commands/check/check.go index d149bb1a29397b..b53c0594ba4fbf 100644 --- a/cmd/cluster-agent/commands/check/check.go +++ b/cmd/cluster-agent/commands/check/check.go @@ -43,6 +43,7 @@ var ( checkPause int checkName string checkDelay int + instanceFilter string logLevel string formatJSON bool formatTable bool @@ -72,6 +73,7 @@ func setupCmd(cmd *cobra.Command) { 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)") @@ -164,8 +166,11 @@ func Check(loggerName config.LoggerName, confFilePath *string, flagNoColor *bool waitCtx, cancelTimeout := context.WithTimeout( context.Background(), time.Duration(discoveryTimeout)*time.Second) - allConfigs := common.WaitForConfigsFromAD(waitCtx, []string{checkName}, int(discoveryMinInstances), "") + 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 { diff --git a/pkg/cli/subcommands/check/command.go b/pkg/cli/subcommands/check/command.go index f874473ad15753..3343a7379afc29 100644 --- a/pkg/cli/subcommands/check/command.go +++ b/pkg/cli/subcommands/check/command.go @@ -56,8 +56,8 @@ type cliParams struct { checkTimes int checkPause int checkName string - instanceFilter string checkDelay int + instanceFilter string logLevel string formatJSON bool formatTable bool From 937bd99280f96f83f80f26390b6e139174c498c0 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 10:57:12 +0100 Subject: [PATCH 11/14] Add copyright --- pkg/util/jsonquery/yaml_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkg/util/jsonquery/yaml_test.go b/pkg/util/jsonquery/yaml_test.go index b0dac5ed6112ea..231141e78d6be2 100644 --- a/pkg/util/jsonquery/yaml_test.go +++ b/pkg/util/jsonquery/yaml_test.go @@ -1,3 +1,8 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2023-present Datadog, Inc. + package jsonquery import ( From 8027fece9558b1d984adef3ab8682903b5e76e1f Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 11:13:10 +0100 Subject: [PATCH 12/14] fix typo --- cmd/agent/common/autodiscovery.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index d5241f8a321864..253274183df2a6 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -309,7 +309,7 @@ func filterInstances(instances []integration.Data, instanceFilter string) ([]int for _, instance := range instances { exist, err := jsonquery.YAMLCheckExist(instance, instanceFilter) if err != nil { - return nil, fmt.Errorf("instance filter errorxx: %v", err) + return nil, fmt.Errorf("instance filter error: %v", err) } if exist { newInstances = append(newInstances, instance) From a0ca08ea4a42be6244add49cd8c385520f5be0f7 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Thu, 12 Jan 2023 11:25:25 +0100 Subject: [PATCH 13/14] fix jmx/command.go --- cmd/agent/subcommands/jmx/command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/agent/subcommands/jmx/command.go b/cmd/agent/subcommands/jmx/command.go index 25f723a2cdaf36..1720a08e874da8 100644 --- a/cmd/agent/subcommands/jmx/command.go +++ b/cmd/agent/subcommands/jmx/command.go @@ -247,10 +247,10 @@ func runJmxCommandConsole(log log.Component, config config.Component, cliParams } else { allConfigs, err = common.WaitForConfigsFromAD(waitCtx, cliParams.cliSelectedChecks, int(cliParams.discoveryMinInstances), cliParams.instanceFilter) } + cancelTimeout() if err != nil { return err } - cancelTimeout() err = standalone.ExecJMXCommandConsole(cliParams.command, cliParams.cliSelectedChecks, cliParams.jmxLogLevel, allConfigs) From 89534fd924a75b718092fb90df6330b61ed6d786 Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Fri, 13 Jan 2023 09:42:41 +0100 Subject: [PATCH 14/14] return aggregate errors --- cmd/agent/common/autodiscovery.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/cmd/agent/common/autodiscovery.go b/cmd/agent/common/autodiscovery.go index 253274183df2a6..8077cbaaa7c0dd 100644 --- a/cmd/agent/common/autodiscovery.go +++ b/cmd/agent/common/autodiscovery.go @@ -11,6 +11,7 @@ import ( "time" "go.uber.org/atomic" + utilserror "k8s.io/apimachinery/pkg/util/errors" "github.com/DataDog/datadog-agent/pkg/autodiscovery" "github.com/DataDog/datadog-agent/pkg/autodiscovery/integration" @@ -271,13 +272,13 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin // add the scheduler in a goroutine, since it will schedule any "catch-up" immediately, // placing items in configChan go AC.AddScheduler("check-cmd", schedulerFunc(func(configs []integration.Config) { + var errors []error for _, cfg := range configs { if instanceFilter != "" { - instances, err := filterInstances(cfg.Instances, instanceFilter) - if err != nil { - returnErr = err - stopChan <- struct{}{} - break + instances, filterErrors := filterInstances(cfg.Instances, instanceFilter) + if len(filterErrors) > 0 { + errors = append(errors, filterErrors...) + continue } if len(instances) == 0 { continue @@ -289,6 +290,10 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin configChan <- cfg } } + if len(errors) > 0 { + returnErr = utilserror.NewAggregate(errors) + stopChan <- struct{}{} + } }), true) for wildcard || len(configs) < discoveryMinInstances { @@ -304,16 +309,18 @@ func waitForConfigsFromAD(ctx context.Context, wildcard bool, checkNames []strin return } -func filterInstances(instances []integration.Data, instanceFilter string) ([]integration.Data, error) { +func filterInstances(instances []integration.Data, instanceFilter string) ([]integration.Data, []error) { var newInstances []integration.Data + var errors []error for _, instance := range instances { exist, err := jsonquery.YAMLCheckExist(instance, instanceFilter) if err != nil { - return nil, fmt.Errorf("instance filter error: %v", err) + errors = append(errors, fmt.Errorf("instance filter error: %v", err)) + continue } if exist { newInstances = append(newInstances, instance) } } - return newInstances, nil + return newInstances, errors }