From 3cbeb226ec15e661902487b0b2ea9fe79c55ae1f Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Wed, 29 May 2024 16:41:32 +0200 Subject: [PATCH 01/54] avoid systemd conflicts (#25944) Co-authored-by: arbll --- pkg/fleet/installer/service/datadog_agent.go | 80 +++++++++---- .../installer/service/datadog_installer.go | 5 + pkg/fleet/installer/service/systemd.go | 19 +-- .../tests/installer/all_packages_test.go | 4 + test/new-e2e/tests/installer/host/host.go | 32 +++-- .../tests/installer/package_agent_test.go | 109 ++++++++++++++++-- 6 files changed, 195 insertions(+), 54 deletions(-) diff --git a/pkg/fleet/installer/service/datadog_agent.go b/pkg/fleet/installer/service/datadog_agent.go index ec65181b63467..15067527c2d69 100644 --- a/pkg/fleet/installer/service/datadog_agent.go +++ b/pkg/fleet/installer/service/datadog_agent.go @@ -21,6 +21,7 @@ import ( ) const ( + pathOldAgent = "/opt/datadog-agent" agentSymlink = "/usr/bin/datadog-agent" agentUnit = "datadog-agent.service" traceAgentUnit = "datadog-agent-trace.service" @@ -62,18 +63,22 @@ func SetupAgent(ctx context.Context, _ []string) (err error) { span.Finish(tracer.WithError(err)) }() + if err = stopOldAgentUnits(ctx); err != nil { + return err + } + for _, unit := range stableUnits { if err = loadUnit(ctx, unit); err != nil { - return err + return fmt.Errorf("failed to load %s: %v", unit, err) } } for _, unit := range experimentalUnits { if err = loadUnit(ctx, unit); err != nil { - return err + return fmt.Errorf("failed to load %s: %v", unit, err) } } if err = os.MkdirAll("/etc/datadog-agent", 0755); err != nil { - return err + return fmt.Errorf("failed to create /etc/datadog-agent: %v", err) } ddAgentUID, ddAgentGID, err := getAgentIDs() if err != nil { @@ -81,31 +86,30 @@ func SetupAgent(ctx context.Context, _ []string) (err error) { } if err = os.Chown("/etc/datadog-agent", ddAgentUID, ddAgentGID); err != nil { - return err + return fmt.Errorf("failed to chown /etc/datadog-agent: %v", err) } if err = systemdReload(ctx); err != nil { - return err + return fmt.Errorf("failed to reload systemd daemon: %v", err) } - for _, unit := range stableUnits { - if err = enableUnit(ctx, unit); err != nil { - return err - } + // enabling the agentUnit only is enough as others are triggered by it + if err = enableUnit(ctx, agentUnit); err != nil { + return fmt.Errorf("failed to enable %s: %v", agentUnit, err) } if err = exec.CommandContext(ctx, "ln", "-sf", "/opt/datadog-packages/datadog-agent/stable/bin/agent/agent", agentSymlink).Run(); err != nil { - return err + return fmt.Errorf("failed to create symlink: %v", err) } // write installinfo before start, or the agent could write it // TODO: add installer version properly if err = installinfo.WriteInstallInfo("installer_package", "manual_update"); err != nil { - return err + return fmt.Errorf("failed to write install info: %v", err) } _, err = os.Stat("/etc/datadog-agent/datadog.yaml") if err != nil && !os.IsNotExist(err) { - return err + return fmt.Errorf("failed to check if /etc/datadog-agent/datadog.yaml exists: %v", err) } // this is expected during a fresh install with the install script / asible / chef / etc... // the config is populated afterwards by the install method and the agent is restarted @@ -114,6 +118,9 @@ func SetupAgent(ctx context.Context, _ []string) (err error) { return err } } + if err = removeOldAgentFiles(); err != nil { + return fmt.Errorf("failed to remove old agent files: %v", err) + } return nil } @@ -133,20 +140,18 @@ func RemoveAgent(ctx context.Context) error { log.Warnf("Failed to stop %s: %s", unit, err) } } - // purge experimental units + + if err := disableUnit(ctx, agentUnit); err != nil { + log.Warnf("Failed to disable %s: %s", agentUnit, err) + } + + // remove units from disk for _, unit := range experimentalUnits { - if err := disableUnit(ctx, unit); err != nil { - log.Warnf("Failed to disable %s: %s", unit, err) - } if err := removeUnit(ctx, unit); err != nil { log.Warnf("Failed to remove %s: %s", unit, err) } } - // purge stable units for _, unit := range stableUnits { - if err := disableUnit(ctx, unit); err != nil { - log.Warnf("Failed to disable %s: %s", unit, err) - } if err := removeUnit(ctx, unit); err != nil { log.Warnf("Failed to remove %s: %s", unit, err) } @@ -159,6 +164,41 @@ func RemoveAgent(ctx context.Context) error { return nil } +func oldAgentInstalled() bool { + _, err := os.Stat(pathOldAgent) + return err == nil +} + +func stopOldAgentUnits(ctx context.Context) error { + if !oldAgentInstalled() { + return nil + } + span, ctx := tracer.StartSpanFromContext(ctx, "remove_old_agent_units") + defer span.Finish() + for _, unit := range stableUnits { + if err := stopUnit(ctx, unit); err != nil { + exitError, ok := err.(*exec.ExitError) + if ok && exitError.ExitCode() == 5 { + // exit code 5 means the unit is not loaded, we can continue + continue + } + return fmt.Errorf("failed to stop %s: %v", unit, err) + } + if err := disableUnit(ctx, unit); err != nil { + return fmt.Errorf("failed to disable %s: %v", unit, err) + } + } + return nil +} + +// removeOldAgentFiles removes old agent files +func removeOldAgentFiles() error { + if !oldAgentInstalled() { + return nil + } + return os.RemoveAll(pathOldAgent) +} + // StartAgentExperiment starts the agent experiment func StartAgentExperiment(ctx context.Context) error { return startUnit(ctx, agentExp, "--no-block") diff --git a/pkg/fleet/installer/service/datadog_installer.go b/pkg/fleet/installer/service/datadog_installer.go index 929b7c4d067b1..b40a8866acc23 100644 --- a/pkg/fleet/installer/service/datadog_installer.go +++ b/pkg/fleet/installer/service/datadog_installer.go @@ -124,6 +124,11 @@ func SetupInstaller(ctx context.Context) (err error) { return nil } + err = os.MkdirAll(systemdPath, 0755) + if err != nil { + return fmt.Errorf("error creating %s: %w", systemdPath, err) + } + for _, unit := range installerUnits { if err = loadUnit(ctx, unit); err != nil { return err diff --git a/pkg/fleet/installer/service/systemd.go b/pkg/fleet/installer/service/systemd.go index b1f52449b5c47..65ae8dcba914c 100644 --- a/pkg/fleet/installer/service/systemd.go +++ b/pkg/fleet/installer/service/systemd.go @@ -19,24 +19,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) -var ( - systemdPath = findSystemdPath() -) - -const ( - debSystemdPath = "/lib/systemd/system" // todo load it at build time from omnibus - rpmSystemdPath = "/usr/lib/systemd/system" -) - -// findSystemdPath todo: this is a hacky way to detect on which os family we are currently -// running and finding the correct systemd path. -// We should probably provide the correct path when we build the package -func findSystemdPath() (systemdPath string) { - if _, err := os.Stat(rpmSystemdPath); err == nil { - return rpmSystemdPath - } - return debSystemdPath -} +const systemdPath = "/etc/systemd/system" func stopUnit(ctx context.Context, unit string, args ...string) error { span, _ := tracer.StartSpanFromContext(ctx, "stop_unit") diff --git a/test/new-e2e/tests/installer/all_packages_test.go b/test/new-e2e/tests/installer/all_packages_test.go index 980173ed3cf13..298dee72376bf 100644 --- a/test/new-e2e/tests/installer/all_packages_test.go +++ b/test/new-e2e/tests/installer/all_packages_test.go @@ -156,6 +156,10 @@ func (s *packageBaseSuite) RunInstallScriptWithError(params ...string) error { } func (s *packageBaseSuite) RunInstallScript(params ...string) { + // bugfix for https://major.io/p/systemd-in-fedora-22-failed-to-restart-service-access-denied/ + if s.os.Flavor == e2eos.CentOS && s.os.Version == e2eos.CentOS7.Version { + s.Env().RemoteHost.MustExecute("sudo systemctl daemon-reexec") + } err := s.RunInstallScriptWithError(params...) require.NoErrorf(s.T(), err, "installer not properly installed. logs: \n%s\n%s", s.Env().RemoteHost.MustExecute("cat /tmp/datadog-installer-stdout.log"), s.Env().RemoteHost.MustExecute("cat /tmp/datadog-installer-stderr.log")) } diff --git a/test/new-e2e/tests/installer/host/host.go b/test/new-e2e/tests/installer/host/host.go index 809b22c364a2d..90e93b5dc9d25 100644 --- a/test/new-e2e/tests/installer/host/host.go +++ b/test/new-e2e/tests/installer/host/host.go @@ -31,6 +31,7 @@ type Host struct { os e2eos.Descriptor arch e2eos.Architecture systemdVersion int + pkgManager string } // Option is an option to configure a Host. @@ -49,14 +50,25 @@ func New(t *testing.T, remote *components.RemoteHost, os e2eos.Descriptor, arch } host.uploadFixtures() host.setSystemdVersion() + if _, err := host.remote.Execute("command -v dpkg-query"); err == nil { + host.pkgManager = "dpkg" + } else if _, err := host.remote.Execute("command -v rpm"); err == nil { + host.pkgManager = "rpm" + } else { + t.Fatal("no package manager found") + } return host } +// GetPkgManager returns the package manager of the host. +func (h *Host) GetPkgManager() string { + return h.pkgManager +} + func (h *Host) setSystemdVersion() { strVersion := strings.TrimSpace(h.remote.MustExecute("systemctl --version | head -n1 | awk '{print $2}'")) version, err := strconv.Atoi(strVersion) require.NoError(h.t, err) - h.t.Log("Systemd version:", version) h.systemdVersion = version } @@ -140,12 +152,13 @@ func (h *Host) AssertPackageInstalledByInstaller(pkgs ...string) { // AssertPackageInstalledByPackageManager checks if a package is installed by the package manager on the host. func (h *Host) AssertPackageInstalledByPackageManager(pkgs ...string) { for _, pkg := range pkgs { - if _, err := h.remote.Execute("command -v dpkg-query"); err == nil { + switch h.pkgManager { + case "dpkg": h.remote.MustExecute("dpkg-query -l " + pkg) - } else if _, err := h.remote.Execute("command -v rpm"); err == nil { + case "rpm": h.remote.MustExecute("rpm -q " + pkg) - } else { - h.t.Fatal("no package manager found") + default: + h.t.Fatal("unsupported package manager") } } } @@ -153,12 +166,13 @@ func (h *Host) AssertPackageInstalledByPackageManager(pkgs ...string) { // AssertPackageNotInstalledByPackageManager checks if a package is not installed by the package manager on the host. func (h *Host) AssertPackageNotInstalledByPackageManager(pkgs ...string) { for _, pkg := range pkgs { - if _, err := h.remote.Execute("command -v dpkg-query"); err == nil { + switch h.pkgManager { + case "dpkg": h.remote.MustExecute("! dpkg-query -l " + pkg) - } else if _, err := h.remote.Execute("command -v rpm"); err == nil { + case "rpm": h.remote.MustExecute("! rpm -q " + pkg) - } else { - h.t.Fatal("no package manager found") + default: + h.t.Fatal("unsupported package manager") } } } diff --git a/test/new-e2e/tests/installer/package_agent_test.go b/test/new-e2e/tests/installer/package_agent_test.go index 2603e4c045297..9fd65b8bddc8e 100644 --- a/test/new-e2e/tests/installer/package_agent_test.go +++ b/test/new-e2e/tests/installer/package_agent_test.go @@ -7,6 +7,7 @@ package installer import ( "fmt" + "path/filepath" awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" "github.com/DataDog/datadog-agent/test/new-e2e/tests/installer/host" @@ -39,14 +40,10 @@ func testAgent(os e2eos.Descriptor, arch e2eos.Architecture) packageSuite { func (s *packageAgentSuite) TestInstall() { s.RunInstallScript(envForceInstall("datadog-agent")) defer s.Purge() - s.host.WaitForUnitActive("datadog-agent.service", "datadog-agent-trace.service", "datadog-agent-process.service") + s.host.WaitForUnitActive(agentUnit, traceUnit, processUnit) state := s.host.State() - - state.AssertUnitsLoaded("datadog-agent.service", "datadog-agent-trace.service", "datadog-agent-process.service", "datadog-agent-sysprobe.service", "datadog-agent-security.service") - state.AssertUnitsEnabled("datadog-agent.service", "datadog-agent-trace.service", "datadog-agent-process.service", "datadog-agent-sysprobe.service", "datadog-agent-security.service") - state.AssertUnitsRunning("datadog-agent.service", "datadog-agent-trace.service", "datadog-agent-process.service") - state.AssertUnitsDead("datadog-agent-sysprobe.service", "datadog-agent-security.service") + s.assertUnits(state, false) state.AssertFileExists("/etc/datadog-agent/install_info", 0644, "root", "root") state.AssertFileExists("/etc/datadog-agent/datadog.yaml", 0640, "dd-agent", "dd-agent") @@ -54,6 +51,79 @@ func (s *packageAgentSuite) TestInstall() { // state.AssertFileExists("/etc/datadog-agent/install.json", 0644, "dd-agent", "dd-agent") } +func (s *packageAgentSuite) assertUnits(state host.State, oldUnits bool) { + state.AssertUnitsLoaded(agentUnit, traceUnit, processUnit, probeUnit, securityUnit) + state.AssertUnitsEnabled(agentUnit) + state.AssertUnitsRunning(agentUnit, traceUnit, processUnit) + state.AssertUnitsDead(probeUnit, securityUnit) + + systemdPath := "/etc/systemd/system" + if oldUnits { + pkgManager := s.host.GetPkgManager() + switch pkgManager { + case "dpkg": + systemdPath = "/lib/systemd/system" + case "rpm": + systemdPath = "/usr/lib/systemd/system" + default: + s.T().Fatalf("unsupported package manager: %s", pkgManager) + } + } + + for _, unit := range []string{agentUnit, traceUnit, processUnit, probeUnit, securityUnit} { + + s.host.AssertUnitProperty(unit, "FragmentPath", filepath.Join(systemdPath, unit)) + } +} + +// TestUpgrade_AgentDebRPM_to_OCI tests the upgrade from DEB/RPM agent to the OCI one. +func (s *packageAgentSuite) TestUpgrade_AgentDebRPM_to_OCI() { + // install deb/rpm agent + s.RunInstallScript(envForceNoInstall("datadog-agent")) + s.host.AssertPackageInstalledByPackageManager("datadog-agent") + + defer s.Purge() + defer s.purgeAgentDebInstall() + + state := s.host.State() + s.assertUnits(state, true) + state.AssertDirExists("/opt/datadog-agent", 0755, "dd-agent", "dd-agent") + + // install OCI agent + s.RunInstallScript(envForceInstall("datadog-agent")) + + state = s.host.State() + s.assertUnits(state, false) + state.AssertPathDoesNotExist("/opt/datadog-agent") + s.host.AssertPackageInstalledByInstaller("datadog-agent") + s.host.AssertPackageInstalledByPackageManager("datadog-agent") +} + +// TestUpgrade_Agent_OCI_then_DebRpm agent deb/rpm install while OCI one is installed +func (s *packageAgentSuite) TestUpgrade_Agent_OCI_then_DebRpm() { + // install OCI agent + s.RunInstallScript(envForceInstall("datadog-agent")) + defer s.Purge() + + state := s.host.State() + s.assertUnits(state, false) + state.AssertPathDoesNotExist("/opt/datadog-agent") + + // is_installed avoids a re-install of datadog-agent with the install script + s.RunInstallScript(envForceNoInstall("datadog-agent")) + state.AssertPathDoesNotExist("/opt/datadog-agent") + + // install deb/rpm manually + s.installDebRPMAgent() + defer s.purgeAgentDebInstall() + s.host.AssertPackageInstalledByPackageManager("datadog-agent") + + state = s.host.State() + s.assertUnits(state, false) + state.AssertDirExists("/opt/datadog-agent", 0755, "dd-agent", "dd-agent") + s.host.AssertPackageInstalledByInstaller("datadog-agent") +} + func (s *packageAgentSuite) TestExperimentTimeout() { s.RunInstallScript(envForceInstall("datadog-agent")) defer s.Purge() @@ -124,7 +194,7 @@ func (s *packageAgentSuite) TestExperimentIgnoringSigterm() { SetStopWithSigkill("trace-agent") for _, unit := range []string{traceUnitXP, processUnitXP, agentUnitXP} { - s.T().Logf("Testing timeoutStop of unit %s", traceUnitXP) + s.T().Logf("Testing timeoutStop of unit %s", unit) s.host.AssertUnitProperty(unit, "TimeoutStopUSec", "1min 30s") s.host.Run(fmt.Sprintf("sudo mkdir -p /etc/systemd/system/%s.d/", unit)) defer s.host.Run(fmt.Sprintf("sudo rm -rf /etc/systemd/system/%s.d/", unit)) @@ -299,3 +369,28 @@ func (s *packageAgentSuite) TestExperimentStopped() { ) } } + +func (s *packageAgentSuite) purgeAgentDebInstall() { + pkgManager := s.host.GetPkgManager() + switch pkgManager { + case "dpkg": + s.Env().RemoteHost.Execute("sudo apt-get remove -y --purge datadog-agent") + case "rpm": + s.Env().RemoteHost.Execute("sudo yum remove -y datadog-agent") + default: + s.T().Fatalf("unsupported package manager: %s", pkgManager) + } +} + +func (s *packageAgentSuite) installDebRPMAgent() { + pkgManager := s.host.GetPkgManager() + switch pkgManager { + case "dpkg": + s.Env().RemoteHost.Execute("sudo apt-get install -y --force-yes datadog-agent") + case "rpm": + s.Env().RemoteHost.Execute("sudo yum -y install datadog-agent") + default: + s.T().Fatalf("unsupported package manager: %s", pkgManager) + } + +} From ebeee88c4ff5fa92d2bdf4207ea499efa40641f5 Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Wed, 29 May 2024 14:43:02 +0000 Subject: [PATCH 02/54] Build the docs when the workflow is modified (#26050) --- .github/workflows/docs-dev.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/docs-dev.yml b/.github/workflows/docs-dev.yml index 0bd8a1ee8afc8..96efef946c7a3 100644 --- a/.github/workflows/docs-dev.yml +++ b/.github/workflows/docs-dev.yml @@ -6,11 +6,13 @@ on: - main paths: - docs/** + - .github/workflows/docs-dev.yml pull_request: branches: - main paths: - docs/** + - .github/workflows/docs-dev.yml concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} From e9713c0a90879a4cd2d09db777b0e9c10998ca90 Mon Sep 17 00:00:00 2001 From: Paul Laffon Date: Wed, 29 May 2024 17:41:46 +0200 Subject: [PATCH 03/54] [DJM-251] Add new parameter djm_config.enabled to enable Data Jobs Monitoring (#25846) * Add new parameter djm_config.enabled to enable Data Jobs Monitoring * rst uses double backticks Co-authored-by: Vickenty Fesunov --------- Co-authored-by: Vickenty Fesunov --- pkg/aggregator/aggregator.go | 8 +++++++ pkg/aggregator/aggregator_test.go | 24 +++++++++++++++++++ pkg/config/setup/config.go | 3 +++ ...d-djm-enabled-config-667dd030d0bf7d83.yaml | 11 +++++++++ 4 files changed, 46 insertions(+) create mode 100644 releasenotes/notes/add-djm-enabled-config-667dd030d0bf7d83.yaml diff --git a/pkg/aggregator/aggregator.go b/pkg/aggregator/aggregator.go index 9e3a58515de33..c9a8af639d688 100644 --- a/pkg/aggregator/aggregator.go +++ b/pkg/aggregator/aggregator.go @@ -281,6 +281,14 @@ func NewBufferedAggregator(s serializer.MetricSerializer, eventPlatformForwarder agentName = flavor.HerokuAgent } + if config.Datadog().GetBool("djm_config.enabled") { + AddRecurrentSeries(&metrics.Serie{ + Name: "datadog.djm.agent_host", + Points: []metrics.Point{{Value: 1.0}}, + MType: metrics.APIGaugeType, + }) + } + tagsStore := tags.NewStore(config.Datadog().GetBool("aggregator_use_tags_store"), "aggregator") aggregator := &BufferedAggregator{ diff --git a/pkg/aggregator/aggregator_test.go b/pkg/aggregator/aggregator_test.go index a8dd65ce5dd35..8cfc12672cc61 100644 --- a/pkg/aggregator/aggregator_test.go +++ b/pkg/aggregator/aggregator_test.go @@ -595,6 +595,30 @@ func TestTimeSamplerFlush(t *testing.T) { assertSeriesEqual(t, s.series, expectedSeries) } +func TestAddDJMRecurrentSeries(t *testing.T) { + // this test IS USING globals (recurrentSeries) + // - + + djmEnabled := pkgconfig.Datadog().GetBool("djm_config.enabled") + pkgconfig.Datadog().SetWithoutSource("djm_config.enabled", true) + defer pkgconfig.Datadog().SetWithoutSource("djm_config.enabled", djmEnabled) + + s := &MockSerializerIterableSerie{} + // NewBufferedAggregator with DJM enable will create a new recurrentSeries + NewBufferedAggregator(s, nil, "hostname", DefaultFlushInterval) + + expectedRecurrentSeries := metrics.Series{&metrics.Serie{ + Name: "datadog.djm.agent_host", + Points: []metrics.Point{{Value: 1.0}}, + MType: metrics.APIGaugeType, + }} + + require.EqualValues(t, expectedRecurrentSeries, recurrentSeries) + + // Reset recurrentSeries + recurrentSeries = metrics.Series{} +} + // The implementation of MockSerializer.SendIterableSeries uses `s.Called(series).Error(0)`. // It calls internaly `Printf` on each field of the real type of `IterableStreamJSONMarshaler` which is `IterableSeries`. // It can lead to a race condition, if another goruntine call `IterableSeries.Append` which modifies `series.count`. diff --git a/pkg/config/setup/config.go b/pkg/config/setup/config.go index bf3810d124dd5..7c60bd3930564 100644 --- a/pkg/config/setup/config.go +++ b/pkg/config/setup/config.go @@ -908,6 +908,9 @@ func InitConfig(config pkgconfigmodel.Config) { config.BindEnvAndSetDefault("remote_updates", false) config.BindEnvAndSetDefault("installer.registry.url", "") config.BindEnvAndSetDefault("installer.registry.auth", "") + + // Data Jobs Monitoring config + config.BindEnvAndSetDefault("djm_config.enabled", false) } func agent(config pkgconfigmodel.Config) { diff --git a/releasenotes/notes/add-djm-enabled-config-667dd030d0bf7d83.yaml b/releasenotes/notes/add-djm-enabled-config-667dd030d0bf7d83.yaml new file mode 100644 index 0000000000000..64d6d4a623a0a --- /dev/null +++ b/releasenotes/notes/add-djm-enabled-config-667dd030d0bf7d83.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. +--- +features: + - | + Add new parameter ``djm_config.enabled`` to enable Data Jobs Monitoring From 46b14bcd5a74badd542392f74a3c98dc093a3ad9 Mon Sep 17 00:00:00 2001 From: Kacper <89013263+kacper-murzyn@users.noreply.github.com> Date: Wed, 29 May 2024 17:41:56 +0200 Subject: [PATCH 04/54] last_stable updated to 7.54.0 (#26068) * last_stable updated to 7.54.0 * Update release.json --- release.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.json b/release.json index 9bdfb7196e050..4251bc6fb9352 100644 --- a/release.json +++ b/release.json @@ -3,7 +3,7 @@ "current_milestone": "7.55.0", "last_stable": { "6": "6.53.0", - "7": "7.53.0" + "7": "7.54.0" }, "nightly": { "INTEGRATIONS_CORE_VERSION": "master", From f2791932596d085ac42d6273a1f8fbf65ccc6086 Mon Sep 17 00:00:00 2001 From: Bryce Kahle Date: Wed, 29 May 2024 08:42:11 -0700 Subject: [PATCH 05/54] replace slashes in gitlab section id (#26034) Fixes section id for junit file uploads --- tasks/libs/common/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/libs/common/utils.py b/tasks/libs/common/utils.py index b2dd09d44762d..9a477f51aafe9 100644 --- a/tasks/libs/common/utils.py +++ b/tasks/libs/common/utils.py @@ -742,7 +742,7 @@ def is_pr_context(branch, pr_id, test_name): @contextmanager def collapsed_section(section_name): - section_id = section_name.replace(" ", "_") + section_id = section_name.replace(" ", "_").replace("/", "_") in_ci = running_in_gitlab_ci() try: if in_ci: From d1233c56a45a2f4f069360d9a2e424e5c56fb302 Mon Sep 17 00:00:00 2001 From: Dustin Long Date: Wed, 29 May 2024 12:01:45 -0400 Subject: [PATCH 06/54] Move alternative otel collector implementations into new component layout (#25836) Co-authored-by: GustavoCaso --- cmd/agent/subcommands/run/command.go | 2 +- cmd/agent/subcommands/run/command_windows.go | 2 +- cmd/serverless/dependencies_linux_amd64.txt | 1 + cmd/serverless/dependencies_linux_arm64.txt | 1 + comp/otelcol/bundle.go | 4 +- comp/otelcol/collector/def/component.go | 6 +- .../{component.go => fx-pipeline/fx.go} | 17 ++--- comp/otelcol/collector/fx/fx.go | 2 +- .../collector/impl-pipeline/no_otlp.go | 25 +++++++ .../collector/{ => impl-pipeline}/pipeline.go | 68 +++++++++++-------- .../collector/{ => impl-pipeline}/status.go | 16 ++--- .../status_templates/otlp.tmpl | 0 comp/otelcol/collector/impl/collector.go | 10 +-- comp/otelcol/collector/no_otlp.go | 38 ----------- comp/otelcol/otlp/collector.go | 8 +-- .../otelcol/otlp/datatype/collector_status.go | 13 ++++ tasks/components.py | 1 - 17 files changed, 106 insertions(+), 108 deletions(-) rename comp/otelcol/collector/{component.go => fx-pipeline/fx.go} (50%) create mode 100644 comp/otelcol/collector/impl-pipeline/no_otlp.go rename comp/otelcol/collector/{ => impl-pipeline}/pipeline.go (57%) rename comp/otelcol/collector/{ => impl-pipeline}/status.go (74%) rename comp/otelcol/collector/{ => impl-pipeline}/status_templates/otlp.tmpl (100%) delete mode 100644 comp/otelcol/collector/no_otlp.go create mode 100644 comp/otelcol/otlp/datatype/collector_status.go diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index 7b5f598614080..cecf2b74ddc1a 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -106,7 +106,7 @@ import ( netflowServer "github.com/DataDog/datadog-agent/comp/netflow/server" "github.com/DataDog/datadog-agent/comp/networkpath" "github.com/DataDog/datadog-agent/comp/otelcol" - otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector" + otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" processAgent "github.com/DataDog/datadog-agent/comp/process/agent" processagentStatusImpl "github.com/DataDog/datadog-agent/comp/process/status/statusimpl" diff --git a/cmd/agent/subcommands/run/command_windows.go b/cmd/agent/subcommands/run/command_windows.go index 6561b52e8ebfd..33bcde6fca29e 100644 --- a/cmd/agent/subcommands/run/command_windows.go +++ b/cmd/agent/subcommands/run/command_windows.go @@ -69,7 +69,7 @@ import ( "github.com/DataDog/datadog-agent/comp/metadata/packagesigning" "github.com/DataDog/datadog-agent/comp/metadata/runner" netflowServer "github.com/DataDog/datadog-agent/comp/netflow/server" - otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector" + otelcollector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" processAgent "github.com/DataDog/datadog-agent/comp/process/agent" "github.com/DataDog/datadog-agent/comp/remote-config/rcclient" "github.com/DataDog/datadog-agent/pkg/serializer" diff --git a/cmd/serverless/dependencies_linux_amd64.txt b/cmd/serverless/dependencies_linux_amd64.txt index 8589b5f124db7..2c6e9689dc098 100644 --- a/cmd/serverless/dependencies_linux_amd64.txt +++ b/cmd/serverless/dependencies_linux_amd64.txt @@ -114,6 +114,7 @@ github.com/DataDog/datadog-agent/comp/otelcol/otlp github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/logsagentexporter github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/serializerexporter github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/processor/infraattributesprocessor +github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype github.com/DataDog/datadog-agent/comp/otelcol/otlp/internal/configutils github.com/DataDog/datadog-agent/comp/remote-config/rcclient/types github.com/DataDog/datadog-agent/comp/serializer/compression diff --git a/cmd/serverless/dependencies_linux_arm64.txt b/cmd/serverless/dependencies_linux_arm64.txt index 518a1dc9e0135..49379cf102325 100644 --- a/cmd/serverless/dependencies_linux_arm64.txt +++ b/cmd/serverless/dependencies_linux_arm64.txt @@ -114,6 +114,7 @@ github.com/DataDog/datadog-agent/comp/otelcol/otlp github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/logsagentexporter github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/serializerexporter github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/processor/infraattributesprocessor +github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype github.com/DataDog/datadog-agent/comp/otelcol/otlp/internal/configutils github.com/DataDog/datadog-agent/comp/remote-config/rcclient/types github.com/DataDog/datadog-agent/comp/serializer/compression diff --git a/comp/otelcol/bundle.go b/comp/otelcol/bundle.go index bf544730a9dbc..7d8623b2f4701 100644 --- a/comp/otelcol/bundle.go +++ b/comp/otelcol/bundle.go @@ -8,7 +8,7 @@ package otelcol import ( - "github.com/DataDog/datadog-agent/comp/otelcol/collector" + collectorfx "github.com/DataDog/datadog-agent/comp/otelcol/collector/fx-pipeline" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -17,5 +17,5 @@ import ( // Bundle specifies the bundle for the OTLP ingest pipeline. func Bundle() fxutil.BundleOptions { return fxutil.Bundle( - collector.Module()) + collectorfx.Module()) } diff --git a/comp/otelcol/collector/def/component.go b/comp/otelcol/collector/def/component.go index f7dcaff6c5f83..bf2b4b176b8b5 100644 --- a/comp/otelcol/collector/def/component.go +++ b/comp/otelcol/collector/def/component.go @@ -3,18 +3,16 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build otlp - // Package collector defines the OpenTelemetry Collector component. package collector import ( - "github.com/DataDog/datadog-agent/comp/otelcol/otlp" + "github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype" ) // team: opentelemetry // Component specifies the interface implemented by the collector module. type Component interface { - Status() otlp.CollectorStatus + Status() datatype.CollectorStatus } diff --git a/comp/otelcol/collector/component.go b/comp/otelcol/collector/fx-pipeline/fx.go similarity index 50% rename from comp/otelcol/collector/component.go rename to comp/otelcol/collector/fx-pipeline/fx.go index 72016fb758f63..0091ff6603037 100644 --- a/comp/otelcol/collector/component.go +++ b/comp/otelcol/collector/fx-pipeline/fx.go @@ -3,25 +3,18 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build otlp - -// Package collector implements the OpenTelemetry Collector component. -package collector +// Package fx creates the modules for fx +package fx import ( - "go.uber.org/fx" - - collectordef "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" + collectorimpl "github.com/DataDog/datadog-agent/comp/otelcol/collector/impl-pipeline" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) // team: opentelemetry -// Component specifies the interface implemented by the collector module. -type Component = collectordef.Component - -// Module specifies the Collector module bundle. +// Module for OTel Agent func Module() fxutil.Module { return fxutil.Component( - fx.Provide(newPipeline)) + fxutil.ProvideComponentConstructor(collectorimpl.NewComponent)) } diff --git a/comp/otelcol/collector/fx/fx.go b/comp/otelcol/collector/fx/fx.go index 08dd50c2e1581..18f5f04598d40 100644 --- a/comp/otelcol/collector/fx/fx.go +++ b/comp/otelcol/collector/fx/fx.go @@ -18,5 +18,5 @@ import ( // Module for OTel Agent func Module() fxutil.Module { return fxutil.Component( - fxutil.ProvideComponentConstructor(collectorimpl.New)) + fxutil.ProvideComponentConstructor(collectorimpl.NewComponent)) } diff --git a/comp/otelcol/collector/impl-pipeline/no_otlp.go b/comp/otelcol/collector/impl-pipeline/no_otlp.go new file mode 100644 index 0000000000000..6c13829a106c8 --- /dev/null +++ b/comp/otelcol/collector/impl-pipeline/no_otlp.go @@ -0,0 +1,25 @@ +// 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 !otlp + +// Package collector contains a no-op implementation of the collector +package collector + +import ( + collector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" + "github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype" +) + +type noopImpl struct{} + +func (i *noopImpl) Status() datatype.CollectorStatus { + return datatype.CollectorStatus{} +} + +// NewComponent returns a new instance of the collector component. +func NewComponent() collector.Component { + return &noopImpl{} +} diff --git a/comp/otelcol/collector/pipeline.go b/comp/otelcol/collector/impl-pipeline/pipeline.go similarity index 57% rename from comp/otelcol/collector/pipeline.go rename to comp/otelcol/collector/impl-pipeline/pipeline.go index 9a9fd57d058c2..79dcb1cf875ec 100644 --- a/comp/otelcol/collector/pipeline.go +++ b/comp/otelcol/collector/impl-pipeline/pipeline.go @@ -5,19 +5,21 @@ //go:build otlp +// Package collector implements the collector component package collector import ( "context" - "go.uber.org/fx" - "github.com/DataDog/datadog-agent/comp/core/config" corelog "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/core/status" + compdef "github.com/DataDog/datadog-agent/comp/def" "github.com/DataDog/datadog-agent/comp/metadata/inventoryagent" + collector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" "github.com/DataDog/datadog-agent/comp/otelcol/otlp" + "github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype" "github.com/DataDog/datadog-agent/pkg/logs/message" "github.com/DataDog/datadog-agent/pkg/serializer" "github.com/DataDog/datadog-agent/pkg/util/optional" @@ -27,14 +29,12 @@ const ( otlpEnabled = "feature_otlp_enabled" ) -// dependencies specifies a list of dependencies required for the collector +// Requires specifies a list of dependencies required for the collector // to be instantiated. -type dependencies struct { - fx.In - - // Lc specifies the fx lifecycle settings, used for appending startup +type Requires struct { + // Lc specifies the lifecycle settings, used for appending startup // and shutdown hooks. - Lc fx.Lifecycle + Lc compdef.Lifecycle // Config specifies the Datadog Agent's configuration component. Config config.Component @@ -53,36 +53,40 @@ type dependencies struct { InventoryAgent inventoryagent.Component } -type provides struct { - fx.Out +// Provides specifics the types returned by the constructor +type Provides struct { + compdef.Out - Comp Component + Comp collector.Component StatusProvider status.InformationProvider } -type collector struct { - deps dependencies - col *otlp.Pipeline +type collectorImpl struct { + col *otlp.Pipeline + config config.Component + log corelog.Component + serializer serializer.MetricSerializer + logsAgent optional.Option[logsagentpipeline.Component] + inventoryAgent inventoryagent.Component } -func (c *collector) start(context.Context) error { - deps := c.deps - on := otlp.IsEnabled(deps.Config) - deps.InventoryAgent.Set(otlpEnabled, on) +func (c *collectorImpl) start(context.Context) error { + on := otlp.IsEnabled(c.config) + c.inventoryAgent.Set(otlpEnabled, on) if !on { return nil } var logch chan *message.Message - if v, ok := deps.LogsAgent.Get(); ok { + if v, ok := c.logsAgent.Get(); ok { if provider := v.GetPipelineProvider(); provider != nil { logch = provider.NextPipelineChan() } } var err error - col, err := otlp.NewPipelineFromAgentConfig(deps.Config, deps.Serializer, logch) + col, err := otlp.NewPipelineFromAgentConfig(c.config, c.serializer, logch) if err != nil { // failure to start the OTLP component shouldn't fail startup - deps.Log.Errorf("Error creating the OTLP ingest pipeline: %v", err) + c.log.Errorf("Error creating the OTLP ingest pipeline: %v", err) return nil } c.col = col @@ -91,13 +95,13 @@ func (c *collector) start(context.Context) error { ctx := context.Background() go func() { if err := col.Run(ctx); err != nil { - deps.Log.Errorf("Error running the OTLP ingest pipeline: %v", err) + c.log.Errorf("Error running the OTLP ingest pipeline: %v", err) } }() return nil } -func (c *collector) stop(context.Context) error { +func (c *collectorImpl) stop(context.Context) error { if c.col != nil { c.col.Stop() } @@ -105,22 +109,26 @@ func (c *collector) stop(context.Context) error { } // Status returns the status of the collector. -func (c *collector) Status() otlp.CollectorStatus { +func (c *collectorImpl) Status() datatype.CollectorStatus { return c.col.GetCollectorStatus() } -// newPipeline creates a new Component for this module and returns any errors on failure. -func newPipeline(deps dependencies) (provides, error) { - collector := &collector{ - deps: deps, +// NewComponent creates a new Component for this module and returns any errors on failure. +func NewComponent(reqs Requires) (Provides, error) { + collector := &collectorImpl{ + config: reqs.Config, + log: reqs.Log, + serializer: reqs.Serializer, + logsAgent: reqs.LogsAgent, + inventoryAgent: reqs.InventoryAgent, } - deps.Lc.Append(fx.Hook{ + reqs.Lc.Append(compdef.Hook{ OnStart: collector.start, OnStop: collector.stop, }) - return provides{ + return Provides{ Comp: collector, StatusProvider: status.NewInformationProvider(collector), }, nil diff --git a/comp/otelcol/collector/status.go b/comp/otelcol/collector/impl-pipeline/status.go similarity index 74% rename from comp/otelcol/collector/status.go rename to comp/otelcol/collector/impl-pipeline/status.go index 7b23aab22b356..c3019563ab636 100644 --- a/comp/otelcol/collector/status.go +++ b/comp/otelcol/collector/impl-pipeline/status.go @@ -18,7 +18,7 @@ import ( //go:embed status_templates var templatesFS embed.FS -func (c *collector) getStatusInfo() map[string]interface{} { +func (c *collectorImpl) getStatusInfo() map[string]interface{} { stats := make(map[string]interface{}) c.populateStatus(stats) @@ -26,9 +26,9 @@ func (c *collector) getStatusInfo() map[string]interface{} { return stats } -func (c *collector) populateStatus(stats map[string]interface{}) { +func (c *collectorImpl) populateStatus(stats map[string]interface{}) { otlpStatus := make(map[string]interface{}) - otlpIsEnabled := otlp.IsEnabled(c.deps.Config) + otlpIsEnabled := otlp.IsEnabled(c.config) var otlpCollectorStatus otlp.CollectorStatus @@ -45,28 +45,28 @@ func (c *collector) populateStatus(stats map[string]interface{}) { } // Name returns the name -func (c *collector) Name() string { +func (c *collectorImpl) Name() string { return "OTLP" } // Name returns the section -func (c *collector) Section() string { +func (c *collectorImpl) Section() string { return "OTLP" } // JSON populates the status map -func (c *collector) JSON(_ bool, stats map[string]interface{}) error { +func (c *collectorImpl) JSON(_ bool, stats map[string]interface{}) error { c.populateStatus(stats) return nil } // Text renders the text output -func (c *collector) Text(_ bool, buffer io.Writer) error { +func (c *collectorImpl) Text(_ bool, buffer io.Writer) error { return status.RenderText(templatesFS, "otlp.tmpl", buffer, c.getStatusInfo()) } // HTML renders the html output -func (c *collector) HTML(_ bool, _ io.Writer) error { +func (c *collectorImpl) HTML(_ bool, _ io.Writer) error { return nil } diff --git a/comp/otelcol/collector/status_templates/otlp.tmpl b/comp/otelcol/collector/impl-pipeline/status_templates/otlp.tmpl similarity index 100% rename from comp/otelcol/collector/status_templates/otlp.tmpl rename to comp/otelcol/collector/impl-pipeline/status_templates/otlp.tmpl diff --git a/comp/otelcol/collector/impl/collector.go b/comp/otelcol/collector/impl/collector.go index 14c53bfb25089..5cf318eeaf981 100644 --- a/comp/otelcol/collector/impl/collector.go +++ b/comp/otelcol/collector/impl/collector.go @@ -21,9 +21,9 @@ import ( collectorcontrib "github.com/DataDog/datadog-agent/comp/otelcol/collector-contrib/def" collector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" - "github.com/DataDog/datadog-agent/comp/otelcol/otlp" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/datadogexporter" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/processor/infraattributesprocessor" + "github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype" "github.com/DataDog/datadog-agent/pkg/serializer" "github.com/DataDog/datadog-agent/pkg/util/optional" ) @@ -58,8 +58,8 @@ type Provides struct { FlareProvider flarebuilder.Provider } -// New returns a new instance of the collector component. -func New(reqs Requires) (Provides, error) { +// NewComponent returns a new instance of the collector component. +func NewComponent(reqs Requires) (Provides, error) { set := otelcol.CollectorSettings{ BuildInfo: component.BuildInfo{ Version: "0.0.1", @@ -123,8 +123,8 @@ func (c *collectorImpl) fillFlare(fb flarebuilder.FlareBuilder) error { return nil } -func (c *collectorImpl) Status() otlp.CollectorStatus { - return otlp.CollectorStatus{ +func (c *collectorImpl) Status() datatype.CollectorStatus { + return datatype.CollectorStatus{ Status: c.col.GetState().String(), } } diff --git a/comp/otelcol/collector/no_otlp.go b/comp/otelcol/collector/no_otlp.go deleted file mode 100644 index f33dc189e5776..0000000000000 --- a/comp/otelcol/collector/no_otlp.go +++ /dev/null @@ -1,38 +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 !otlp - -// Package collector implements the OTLP Collector component for non-OTLP builds. -package collector - -import ( - "github.com/DataDog/datadog-agent/pkg/util/fxutil" - "go.uber.org/fx" -) - -// Component represents the no-op Component interface. -type Component interface { - Start() error - Stop() -} - -// Module specifies the fx module for non-OTLP builds. -func Module() fxutil.Module { - return fxutil.Component( - fx.Provide(newPipeline)) -} - -func newPipeline() (Component, error) { - return noOpComp{}, nil -} - -type noOpComp struct{} - -// Start is a no-op. -func (noOpComp) Start() error { return nil } - -// Stop is a no-op. -func (noOpComp) Stop() {} diff --git a/comp/otelcol/otlp/collector.go b/comp/otelcol/otlp/collector.go index 5e8d0462bd7ba..4af8f540d7c4e 100644 --- a/comp/otelcol/otlp/collector.go +++ b/comp/otelcol/otlp/collector.go @@ -32,6 +32,7 @@ import ( "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/logsagentexporter" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/serializerexporter" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/processor/infraattributesprocessor" + "github.com/DataDog/datadog-agent/comp/otelcol/otlp/datatype" "github.com/DataDog/datadog-agent/pkg/logs/message" "github.com/DataDog/datadog-agent/pkg/serializer" "github.com/DataDog/datadog-agent/pkg/util/flavor" @@ -189,11 +190,8 @@ type Pipeline struct { col *otelcol.Collector } -// CollectorStatus is the status struct for an OTLP pipeline's collector -type CollectorStatus struct { - Status string - ErrorMessage string -} +// CollectorStatus is an alias to the datatype, for convenience +type CollectorStatus = datatype.CollectorStatus // NewPipeline defines a new OTLP pipeline. func NewPipeline(cfg PipelineConfig, s serializer.MetricSerializer, logsAgentChannel chan *message.Message) (*Pipeline, error) { diff --git a/comp/otelcol/otlp/datatype/collector_status.go b/comp/otelcol/otlp/datatype/collector_status.go new file mode 100644 index 0000000000000..1a6cb1209b8fd --- /dev/null +++ b/comp/otelcol/otlp/datatype/collector_status.go @@ -0,0 +1,13 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2021-present Datadog, Inc. + +// Package datatype declares basic datatypes used by OTLP +package datatype + +// CollectorStatus is the status struct for an OTLP pipeline's collector +type CollectorStatus struct { + Status string + ErrorMessage string +} diff --git a/tasks/components.py b/tasks/components.py index efad8fc60057c..1bf94d91b44c8 100644 --- a/tasks/components.py +++ b/tasks/components.py @@ -55,7 +55,6 @@ def has_type_component(content): "comp/metadata/inventoryagent/component.go", "comp/netflow/config/component.go", "comp/netflow/server/component.go", - "comp/otelcol/collector/component.go", "comp/remote-config/rcclient/component.go", "comp/trace/agent/component.go", "comp/trace/config/component.go", From 3c1ea9978176c16e6303a5702c14f9c4602cdb08 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Wed, 29 May 2024 18:18:10 +0200 Subject: [PATCH 07/54] [CWS] add hash action (#25870) --- pkg/security/probe/probe_ebpf.go | 3 + pkg/security/probe/probe_ebpfless.go | 3 + pkg/security/rules/monitor/policy_monitor.go | 11 +++ .../rules/monitor/policy_monitor_easyjson.go | 73 +++++++++++++++++++ pkg/security/secl/rules/actions.go | 4 + 5 files changed, 94 insertions(+) diff --git a/pkg/security/probe/probe_ebpf.go b/pkg/security/probe/probe_ebpf.go index b847f8102b7e7..49f43d90ebbb8 100644 --- a/pkg/security/probe/probe_ebpf.go +++ b/pkg/security/probe/probe_ebpf.go @@ -2051,6 +2051,9 @@ func (p *EBPFProbe) HandleActions(ctx *eval.Context, rule *rules.Rule) { p.probe.DispatchCustomEvent(rule, event) } + case action.Hash != nil: + // force the resolution as it will force the hash resolution as well + ev.ResolveFields() } } } diff --git a/pkg/security/probe/probe_ebpfless.go b/pkg/security/probe/probe_ebpfless.go index 76bb268a83a4a..cc1a47669b034 100644 --- a/pkg/security/probe/probe_ebpfless.go +++ b/pkg/security/probe/probe_ebpfless.go @@ -557,6 +557,9 @@ func (p *EBPFLessProbe) HandleActions(ctx *eval.Context, rule *rules.Rule) { p.processKiller.KillAndReport(action.Kill.Scope, action.Kill.Signal, ev, func(pid uint32, sig uint32) error { return p.processKiller.KillFromUserspace(pid, sig, ev) }) + case action.Hash != nil: + // force the resolution as it will force the hash resolution as well + ev.ResolveFields() } } } diff --git a/pkg/security/rules/monitor/policy_monitor.go b/pkg/security/rules/monitor/policy_monitor.go index 9690bb108dabf..96ea530109c16 100644 --- a/pkg/security/rules/monitor/policy_monitor.go +++ b/pkg/security/rules/monitor/policy_monitor.go @@ -188,6 +188,13 @@ type RuleAction struct { Filter *string `json:"filter,omitempty"` Set *RuleSetAction `json:"set,omitempty"` Kill *RuleKillAction `json:"kill,omitempty"` + Hash *HashAction `json:"hash,omitempty"` +} + +// HashAction is used to report 'hash' action +// easyjson:json +type HashAction struct { + Enabled bool `json:"enabled,omitempty"` } // RuleSetAction is used to report 'set' action @@ -267,6 +274,10 @@ func RuleStateFromDefinition(def *rules.RuleDefinition, status string, message s Append: action.Set.Append, Scope: string(action.Set.Scope), } + case action.Hash != nil: + ruleAction.Hash = &HashAction{ + Enabled: true, + } } ruleState.Actions = append(ruleState.Actions, ruleAction) } diff --git a/pkg/security/rules/monitor/policy_monitor_easyjson.go b/pkg/security/rules/monitor/policy_monitor_easyjson.go index d4db727f7676b..802f10a5b1e00 100644 --- a/pkg/security/rules/monitor/policy_monitor_easyjson.go +++ b/pkg/security/rules/monitor/policy_monitor_easyjson.go @@ -512,6 +512,16 @@ func easyjson6151911dDecodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor4( } (*out.Kill).UnmarshalEasyJSON(in) } + case "hash": + if in.IsNull() { + in.Skip() + out.Hash = nil + } else { + if out.Hash == nil { + out.Hash = new(HashAction) + } + (*out.Hash).UnmarshalEasyJSON(in) + } default: in.SkipRecursive() } @@ -552,6 +562,16 @@ func easyjson6151911dEncodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor4( } (*in.Kill).MarshalEasyJSON(out) } + if in.Hash != nil { + const prefix string = ",\"hash\":" + if first { + first = false + out.RawString(prefix[1:]) + } else { + out.RawString(prefix) + } + (*in.Hash).MarshalEasyJSON(out) + } out.RawByte('}') } @@ -761,3 +781,56 @@ func (v HeartbeatEvent) MarshalEasyJSON(w *jwriter.Writer) { func (v *HeartbeatEvent) UnmarshalEasyJSON(l *jlexer.Lexer) { easyjson6151911dDecodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor6(l, v) } +func easyjson6151911dDecodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor7(in *jlexer.Lexer, out *HashAction) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "enabled": + out.Enabled = bool(in.Bool()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson6151911dEncodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor7(out *jwriter.Writer, in HashAction) { + out.RawByte('{') + first := true + _ = first + if in.Enabled { + const prefix string = ",\"enabled\":" + first = false + out.RawString(prefix[1:]) + out.Bool(bool(in.Enabled)) + } + out.RawByte('}') +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v HashAction) MarshalEasyJSON(w *jwriter.Writer) { + easyjson6151911dEncodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor7(w, v) +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *HashAction) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson6151911dDecodeGithubComDataDogDatadogAgentPkgSecurityRulesMonitor7(l, v) +} diff --git a/pkg/security/secl/rules/actions.go b/pkg/security/secl/rules/actions.go index 42daacf763841..378ba1460db3f 100644 --- a/pkg/security/secl/rules/actions.go +++ b/pkg/security/secl/rules/actions.go @@ -29,6 +29,7 @@ type ActionDefinition struct { Set *SetDefinition `yaml:"set"` Kill *KillDefinition `yaml:"kill"` CoreDump *CoreDumpDefinition `yaml:"coredump"` + Hash *HashDefinition `yaml:"hash"` // internal InternalCallback *InternalCallbackDefinition @@ -129,3 +130,6 @@ type CoreDumpDefinition struct { Dentry bool `yaml:"dentry"` NoCompression bool `yaml:"no_compression"` } + +// HashDefinition describes the 'hash' section of a rule action +type HashDefinition struct{} From 2844cfb492ff89e404e0b0cc052b07807660f483 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Wed, 29 May 2024 18:24:31 +0200 Subject: [PATCH 08/54] [CWS] windows multi approvers (#26067) * ensure old approvers are not kept after policy reloading * add new approver capabilities for other FRIM event types * ensure we take into account all event basenames for approvers * ensure approver are able to skip event types with no active rule * add test for approver mechanism * disable approvers in `TestETWFileNotifications` --- .../probe/kfilters/capabilities_windows.go | 18 +++++++ .../probe/probe_kernel_file_windows.go | 2 +- .../probe/probe_kernel_file_windows_test.go | 1 + pkg/security/probe/probe_windows.go | 36 +++++++++++-- pkg/security/tests/file_windows_test.go | 50 ++++++++++++++++++- 5 files changed, 100 insertions(+), 7 deletions(-) diff --git a/pkg/security/probe/kfilters/capabilities_windows.go b/pkg/security/probe/kfilters/capabilities_windows.go index 5ffba3ef71358..503866e04ead0 100644 --- a/pkg/security/probe/kfilters/capabilities_windows.go +++ b/pkg/security/probe/kfilters/capabilities_windows.go @@ -16,4 +16,22 @@ func init() { FieldValueTypes: eval.ScalarValueType | eval.PatternValueType, }, } + allCapabilities["rename"] = Capabilities{ + "rename.file.name": { + PolicyFlags: PolicyFlagBasename, + FieldValueTypes: eval.ScalarValueType | eval.PatternValueType, + }, + } + allCapabilities["delete"] = Capabilities{ + "delete.file.name": { + PolicyFlags: PolicyFlagBasename, + FieldValueTypes: eval.ScalarValueType | eval.PatternValueType, + }, + } + allCapabilities["write"] = Capabilities{ + "write.file.name": { + PolicyFlags: PolicyFlagBasename, + FieldValueTypes: eval.ScalarValueType | eval.PatternValueType, + }, + } } diff --git a/pkg/security/probe/probe_kernel_file_windows.go b/pkg/security/probe/probe_kernel_file_windows.go index f749f77695e71..b3335a45ac3e3 100644 --- a/pkg/security/probe/probe_kernel_file_windows.go +++ b/pkg/security/probe/probe_kernel_file_windows.go @@ -166,7 +166,7 @@ func (wp *WindowsProbe) parseCreateHandleArgs(e *etw.DDEventRecord) (*createHand // not amazing to double compute the basename.. basename := filepath.Base(ca.fileName) - if !wp.approve("create.file.name", basename) { + if !wp.approveFimBasename(basename) { wp.discardedFileHandles.Add(fileObjectPointer(ca.fileObject), struct{}{}) wp.stats.createFileApproverRejects++ return nil, errDiscardedPath diff --git a/pkg/security/probe/probe_kernel_file_windows_test.go b/pkg/security/probe/probe_kernel_file_windows_test.go index 4c6cd15f61d05..814fb78a0d2ba 100644 --- a/pkg/security/probe/probe_kernel_file_windows_test.go +++ b/pkg/security/probe/probe_kernel_file_windows_test.go @@ -538,6 +538,7 @@ func TestETWFileNotifications(t *testing.T) { testfilerename := ex + ".testfilerename" wp, err := createTestProbe() + wp.disableApprovers = true require.NoError(t, err) // teardownTestProe calls the stop function on etw, which will diff --git a/pkg/security/probe/probe_windows.go b/pkg/security/probe/probe_windows.go index 400bd34c7afa6..bb01030515fa4 100644 --- a/pkg/security/probe/probe_windows.go +++ b/pkg/security/probe/probe_windows.go @@ -11,6 +11,7 @@ import ( "errors" "fmt" "path/filepath" + "slices" "sync" "time" @@ -102,8 +103,11 @@ type WindowsProbe struct { // channel handling. Currently configurable, but should probably be set // to false with a configurable size value blockonchannelsend bool + // approvers - approvers map[eval.Field][]approver + disableApprovers bool + currentEventTypes []string + approvers map[eval.Field][]approver } type approver interface { @@ -319,12 +323,29 @@ func (p *WindowsProbe) Stop() { } } +func (p *WindowsProbe) approveFimBasename(value string) bool { + fields := []string{"create.file.name", "rename.file.name", "delete.file.name", "write.file.name"} + eventTypes := []string{"create", "rename", "delete", "write"} + + for i, field := range fields { + eventType := eventTypes[i] + if p.approve(field, eventType, value) { + return true + } + } + return false +} + // currently support only string base approver for now -func (p *WindowsProbe) approve(field eval.Field, value string) bool { +func (p *WindowsProbe) approve(field eval.Field, eventType string, value string) bool { + if p.disableApprovers { + return true + } + approvers, exists := p.approvers[field] if !exists { - // no approvers, so no filtering for this field - return true + // no approvers, so no filtering for this field, except if no rule for this event type + return slices.Contains(p.currentEventTypes, eventType) } for _, approver := range approvers { @@ -1006,7 +1027,9 @@ func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetRe p.isRenameEnabled = false p.isDeleteEnabled = false - for _, eventType := range rs.GetEventTypes() { + p.currentEventTypes = rs.GetEventTypes() + + for _, eventType := range p.currentEventTypes { switch eventType { case model.FileRenameEventType.String(): p.isRenameEnabled = true @@ -1022,6 +1045,9 @@ func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetRe return nil, err } + // remove old approvers + clear(p.approvers) + for eventType, report := range ars.Policies { if err := p.SetApprovers(eventType, report.Approvers); err != nil { return nil, err diff --git a/pkg/security/tests/file_windows_test.go b/pkg/security/tests/file_windows_test.go index 8e559f8d6f475..491fa3ea6cab4 100644 --- a/pkg/security/tests/file_windows_test.go +++ b/pkg/security/tests/file_windows_test.go @@ -164,7 +164,55 @@ func TestWriteFileEvent(t *testing.T) { t.Fatal(err) } - test.Run(t, "delete", func(t *testing.T, kind wrapperType, cmdFunc func(cmd string, args []string, envs []string) *exec.Cmd) { + test.Run(t, "write", func(t *testing.T, kind wrapperType, cmdFunc func(cmd string, args []string, envs []string) *exec.Cmd) { + test.WaitSignal(t, func() error { + f, err := os.OpenFile("C:\\Temp\\test.bad", os.O_WRONLY, 0755) + if err != nil { + return err + } + if _, err := f.WriteString("test"); err != nil { + return err + } + return f.Close() + }, test.validateFileEvent(t, noWrapperType, func(event *model.Event, rule *rules.Rule) { + assertFieldEqualCaseInsensitve(t, event, "write.file.name", "test.bad", event, "write.file.name file didn't match") + })) + }) +} + +func TestWriteFileEventWithCreate(t *testing.T) { + ruleDefs := []*rules.RuleDefinition{ + { + ID: "test_create_polute", + Expression: `create.file.name =~ "*.dll"`, + }, + { + ID: "test_write_file", + Expression: `write.file.name =~ "test.bad" && write.file.path =~ "C:\Temp\**"`, + }, + } + opts := testOpts{ + enableFIM: true, + } + test, err := newTestModule(t, nil, ruleDefs, withStaticOpts(opts)) + if err != nil { + t.Fatal(err) + } + defer test.Close() + // this is kinda hokey. ETW (which is what FIM is based on) takes an indeterminant amount of time to start up. + // so wait around for it to start + time.Sleep(5 * time.Second) + + os.MkdirAll("C:\\Temp", 0755) + f, err := os.Create("C:\\Temp\\test.bad") + if err != nil { + t.Fatal(err) + } + if err := f.Close(); err != nil { + t.Fatal(err) + } + + test.Run(t, "write", func(t *testing.T, kind wrapperType, cmdFunc func(cmd string, args []string, envs []string) *exec.Cmd) { test.WaitSignal(t, func() error { f, err := os.OpenFile("C:\\Temp\\test.bad", os.O_WRONLY, 0755) if err != nil { From c7efe438c39710cd1698e3408279107916744d26 Mon Sep 17 00:00:00 2001 From: David Ortiz Date: Wed, 29 May 2024 19:05:26 +0200 Subject: [PATCH 09/54] [kubernetes/apiserver] Initialize metadata controller properly (#26070) --- .../controllers/metadata_controller.go | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/pkg/util/kubernetes/apiserver/controllers/metadata_controller.go b/pkg/util/kubernetes/apiserver/controllers/metadata_controller.go index e0b7269a59e4d..5cd8a339af695 100644 --- a/pkg/util/kubernetes/apiserver/controllers/metadata_controller.go +++ b/pkg/util/kubernetes/apiserver/controllers/metadata_controller.go @@ -52,38 +52,6 @@ func newMetadataController(endpointsInformer coreinformers.EndpointsInformer, wm queue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "endpoints"), } - go func() { - wmetaFilterParams := workloadmeta.FilterParams{ - Kinds: []workloadmeta.Kind{workloadmeta.KindKubernetesNode}, - Source: workloadmeta.SourceAll, - EventType: workloadmeta.EventTypeAll, - } - - wmetaEventsCh := wmeta.Subscribe( - "metadata-controller", - workloadmeta.NormalPriority, - workloadmeta.NewFilter(&wmetaFilterParams), - ) - defer wmeta.Unsubscribe(wmetaEventsCh) - - for eventBundle := range wmetaEventsCh { - eventBundle.Acknowledge() - - for _, event := range eventBundle.Events { - node := event.Entity.(*workloadmeta.KubernetesNode) - - switch event.Type { - case workloadmeta.EventTypeSet: - m.addNode(node.Name) - case workloadmeta.EventTypeUnset: - m.deleteNode(node.Name) - default: - log.Warnf("Unknown event type %v", event.Type) - } - } - } - }() - if _, err := endpointsInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ AddFunc: m.addEndpoints, UpdateFunc: m.updateEndpoints, @@ -110,6 +78,20 @@ func (m *metadataController) run(stopCh <-chan struct{}) { return } + wmetaFilterParams := workloadmeta.FilterParams{ + Kinds: []workloadmeta.Kind{workloadmeta.KindKubernetesNode}, + Source: workloadmeta.SourceAll, + EventType: workloadmeta.EventTypeAll, + } + + wmetaEventsCh := m.wmeta.Subscribe( + "metadata-controller", + workloadmeta.NormalPriority, + workloadmeta.NewFilter(&wmetaFilterParams), + ) + defer m.wmeta.Unsubscribe(wmetaEventsCh) + + go m.processWorkloadmetaNodeEvents(wmetaEventsCh) go wait.Until(m.worker, time.Second, stopCh) <-stopCh } @@ -134,6 +116,25 @@ func (m *metadataController) processNextWorkItem() bool { return true } +func (m *metadataController) processWorkloadmetaNodeEvents(wmetaEventsCh chan workloadmeta.EventBundle) { + for eventBundle := range wmetaEventsCh { + eventBundle.Acknowledge() + + for _, event := range eventBundle.Events { + node := event.Entity.(*workloadmeta.KubernetesNode) + + switch event.Type { + case workloadmeta.EventTypeSet: + m.addNode(node.Name) + case workloadmeta.EventTypeUnset: + m.deleteNode(node.Name) + default: + log.Warnf("Unknown event type %v", event.Type) + } + } + } +} + func (m *metadataController) addNode(name string) { bundle := m.store.getCopyOrNew(name) m.store.set(name, bundle) From 2948aa9015c2098bf1d0a972b9d729c0d9da2f86 Mon Sep 17 00:00:00 2001 From: Stanley Liu Date: Wed, 29 May 2024 17:03:31 -0400 Subject: [PATCH 10/54] Implement metrics for infraattributesprocessor (#25711) * Initial version * Rename tagenrichmentprocessor to infraattributesprocessor * Fix merge * Rename config_logs_strict * Add tagger component * Fix tests and lint * tidy * Refactor to tag multiple entities * Rename * Temp update mapping go attributes * tidy * Update attributes mapping * Fix resource metrics loop, add more tests * Update attributes to v0.16.1 * Update attributes to v0.16.1 * Add container_image_metadata * generate licenses * Rename * Fix merge conflicts * generate deps --- LICENSE-3rdparty.csv | 1 + cmd/otel-agent/subcommands/run/command.go | 4 + cmd/serverless/dependencies_linux_amd64.txt | 1 + cmd/serverless/dependencies_linux_arm64.txt | 1 + .../collector/impl-pipeline/pipeline.go | 7 +- comp/otelcol/collector/impl/collector.go | 4 +- comp/otelcol/otlp/collector.go | 19 +- comp/otelcol/otlp/collector_test.go | 13 +- .../exporter/datadogexporter/go.mod | 2 +- .../exporter/datadogexporter/go.sum | 4 +- .../exporter/logsagentexporter/go.mod | 2 +- .../exporter/logsagentexporter/go.sum | 4 +- .../exporter/serializerexporter/go.mod | 2 +- .../exporter/serializerexporter/go.sum | 4 +- .../otlp/components/pipeline/provider/go.mod | 2 +- .../infraattributesprocessor/config.go | 4 + .../infraattributesprocessor/config_test.go | 8 +- .../infraattributesprocessor/factory.go | 45 ++-- .../infraattributesprocessor/factory_test.go | 14 +- .../infraattributesprocessor/logs.go | 2 +- .../infraattributesprocessor/logs_test.go | 6 +- .../infraattributesprocessor/metadata.go | 6 +- .../infraattributesprocessor/metrics.go | 110 ++++++++- .../infraattributesprocessor/metrics_test.go | 226 ++++++++++++++++-- .../infraattributesprocessor/package_test.go | 16 -- .../infraattributesprocessor/traces.go | 2 +- .../infraattributesprocessor/traces_test.go | 6 +- .../otlp/map_provider_not_serverless_test.go | 8 +- go.mod | 5 +- go.sum | 4 +- pkg/serverless/otlp/otlp.go | 2 +- 31 files changed, 432 insertions(+), 102 deletions(-) delete mode 100644 comp/otelcol/otlp/components/processor/infraattributesprocessor/package_test.go diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 4bc52d22e032a..cabb2fc4688ec 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -3085,6 +3085,7 @@ core,go.opentelemetry.io/collector/semconv/v1.13.0,Apache-2.0,Copyright The Open core,go.opentelemetry.io/collector/semconv/v1.16.0,Apache-2.0,Copyright The OpenTelemetry Authors core,go.opentelemetry.io/collector/semconv/v1.17.0,Apache-2.0,Copyright The OpenTelemetry Authors core,go.opentelemetry.io/collector/semconv/v1.18.0,Apache-2.0,Copyright The OpenTelemetry Authors +core,go.opentelemetry.io/collector/semconv/v1.21.0,Apache-2.0,Copyright The OpenTelemetry Authors core,go.opentelemetry.io/collector/semconv/v1.22.0,Apache-2.0,Copyright The OpenTelemetry Authors core,go.opentelemetry.io/collector/semconv/v1.6.1,Apache-2.0,Copyright The OpenTelemetry Authors core,go.opentelemetry.io/collector/semconv/v1.8.0,Apache-2.0,Copyright The OpenTelemetry Authors diff --git a/cmd/otel-agent/subcommands/run/command.go b/cmd/otel-agent/subcommands/run/command.go index 7b48903eb4a54..41c61dac6ff4d 100644 --- a/cmd/otel-agent/subcommands/run/command.go +++ b/cmd/otel-agent/subcommands/run/command.go @@ -20,6 +20,8 @@ import ( corelogimpl "github.com/DataDog/datadog-agent/comp/core/log/logimpl" "github.com/DataDog/datadog-agent/comp/core/secrets" "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + "github.com/DataDog/datadog-agent/comp/core/tagger" + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" "github.com/DataDog/datadog-agent/comp/core/workloadmeta" "github.com/DataDog/datadog-agent/comp/forwarder" "github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder" @@ -81,6 +83,8 @@ func runOTelAgentCommand(_ context.Context, params *subcommands.GlobalParams, op inventoryagentimpl.Module(), workloadmeta.Module(), hostnameimpl.Module(), + fx.Provide(tagger.NewTaggerParams), + taggerimpl.Module(), sysprobeconfig.NoneModule(), fetchonlyimpl.Module(), collectorfx.Module(), diff --git a/cmd/serverless/dependencies_linux_amd64.txt b/cmd/serverless/dependencies_linux_amd64.txt index 2c6e9689dc098..30a8125e3371c 100644 --- a/cmd/serverless/dependencies_linux_amd64.txt +++ b/cmd/serverless/dependencies_linux_amd64.txt @@ -611,6 +611,7 @@ go.opentelemetry.io/collector/receiver/otlpreceiver/internal/trace go.opentelemetry.io/collector/receiver/receiverhelper go.opentelemetry.io/collector/semconv/v1.17.0 go.opentelemetry.io/collector/semconv/v1.18.0 +go.opentelemetry.io/collector/semconv/v1.21.0 go.opentelemetry.io/collector/semconv/v1.6.1 go.opentelemetry.io/collector/service go.opentelemetry.io/collector/service/extensions diff --git a/cmd/serverless/dependencies_linux_arm64.txt b/cmd/serverless/dependencies_linux_arm64.txt index 49379cf102325..65442897051cb 100644 --- a/cmd/serverless/dependencies_linux_arm64.txt +++ b/cmd/serverless/dependencies_linux_arm64.txt @@ -610,6 +610,7 @@ go.opentelemetry.io/collector/receiver/otlpreceiver/internal/trace go.opentelemetry.io/collector/receiver/receiverhelper go.opentelemetry.io/collector/semconv/v1.17.0 go.opentelemetry.io/collector/semconv/v1.18.0 +go.opentelemetry.io/collector/semconv/v1.21.0 go.opentelemetry.io/collector/semconv/v1.6.1 go.opentelemetry.io/collector/service go.opentelemetry.io/collector/service/extensions diff --git a/comp/otelcol/collector/impl-pipeline/pipeline.go b/comp/otelcol/collector/impl-pipeline/pipeline.go index 79dcb1cf875ec..8420a24b02a38 100644 --- a/comp/otelcol/collector/impl-pipeline/pipeline.go +++ b/comp/otelcol/collector/impl-pipeline/pipeline.go @@ -14,6 +14,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" corelog "github.com/DataDog/datadog-agent/comp/core/log" "github.com/DataDog/datadog-agent/comp/core/status" + "github.com/DataDog/datadog-agent/comp/core/tagger" compdef "github.com/DataDog/datadog-agent/comp/def" "github.com/DataDog/datadog-agent/comp/metadata/inventoryagent" collector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" @@ -51,6 +52,8 @@ type Requires struct { // InventoryAgent require the inventory metadata payload, allowing otelcol to add data to it. InventoryAgent inventoryagent.Component + + Tagger tagger.Component } // Provides specifics the types returned by the constructor @@ -68,6 +71,7 @@ type collectorImpl struct { serializer serializer.MetricSerializer logsAgent optional.Option[logsagentpipeline.Component] inventoryAgent inventoryagent.Component + tagger tagger.Component } func (c *collectorImpl) start(context.Context) error { @@ -83,7 +87,7 @@ func (c *collectorImpl) start(context.Context) error { } } var err error - col, err := otlp.NewPipelineFromAgentConfig(c.config, c.serializer, logch) + col, err := otlp.NewPipelineFromAgentConfig(c.config, c.serializer, logch, c.tagger) if err != nil { // failure to start the OTLP component shouldn't fail startup c.log.Errorf("Error creating the OTLP ingest pipeline: %v", err) @@ -121,6 +125,7 @@ func NewComponent(reqs Requires) (Provides, error) { serializer: reqs.Serializer, logsAgent: reqs.LogsAgent, inventoryAgent: reqs.InventoryAgent, + tagger: reqs.Tagger, } reqs.Lc.Append(compdef.Hook{ diff --git a/comp/otelcol/collector/impl/collector.go b/comp/otelcol/collector/impl/collector.go index 5cf318eeaf981..d9cf67eb04861 100644 --- a/comp/otelcol/collector/impl/collector.go +++ b/comp/otelcol/collector/impl/collector.go @@ -17,6 +17,7 @@ import ( flarebuilder "github.com/DataDog/datadog-agent/comp/core/flare/builder" "github.com/DataDog/datadog-agent/comp/core/hostname" corelog "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/comp/core/tagger" compdef "github.com/DataDog/datadog-agent/comp/def" collectorcontrib "github.com/DataDog/datadog-agent/comp/otelcol/collector-contrib/def" collector "github.com/DataDog/datadog-agent/comp/otelcol/collector/def" @@ -48,6 +49,7 @@ type Requires struct { Serializer serializer.MetricSerializer LogsAgent optional.Option[logsagentpipeline.Component] HostName hostname.Component + Tagger tagger.Component } // Provides declares the output types from the constructor @@ -76,7 +78,7 @@ func NewComponent(reqs Requires) (Provides, error) { } else { factories.Exporters[datadogexporter.Type] = datadogexporter.NewFactory(reqs.Serializer, nil, reqs.HostName) } - factories.Processors[infraattributesprocessor.Type] = infraattributesprocessor.NewFactory() + factories.Processors[infraattributesprocessor.Type] = infraattributesprocessor.NewFactory(reqs.Tagger) return factories, nil }, ConfigProvider: reqs.Provider, diff --git a/comp/otelcol/otlp/collector.go b/comp/otelcol/otlp/collector.go index 4af8f540d7c4e..e3896be2933e2 100644 --- a/comp/otelcol/otlp/collector.go +++ b/comp/otelcol/otlp/collector.go @@ -82,7 +82,7 @@ func (t *tagEnricher) Enrich(_ context.Context, extraTags []string, dimensions * return enrichedTags } -func getComponents(s serializer.MetricSerializer, logsAgentChannel chan *message.Message) ( +func getComponents(s serializer.MetricSerializer, logsAgentChannel chan *message.Message, tagger tagger.Component) ( otelcol.Factories, error, ) { @@ -115,10 +115,11 @@ func getComponents(s serializer.MetricSerializer, logsAgentChannel chan *message errs = append(errs, err) } - processors, err := processor.MakeFactoryMap( - batchprocessor.NewFactory(), - infraattributesprocessor.NewFactory(), - ) + processorFactories := []processor.Factory{batchprocessor.NewFactory()} + if tagger != nil { + processorFactories = append(processorFactories, infraattributesprocessor.NewFactory(tagger)) + } + processors, err := processor.MakeFactoryMap(processorFactories...) if err != nil { errs = append(errs, err) } @@ -194,7 +195,7 @@ type Pipeline struct { type CollectorStatus = datatype.CollectorStatus // NewPipeline defines a new OTLP pipeline. -func NewPipeline(cfg PipelineConfig, s serializer.MetricSerializer, logsAgentChannel chan *message.Message) (*Pipeline, error) { +func NewPipeline(cfg PipelineConfig, s serializer.MetricSerializer, logsAgentChannel chan *message.Message, tagger tagger.Component) (*Pipeline, error) { buildInfo, err := getBuildInfo() if err != nil { return nil, fmt.Errorf("failed to get build info: %w", err) @@ -214,7 +215,7 @@ func NewPipeline(cfg PipelineConfig, s serializer.MetricSerializer, logsAgentCha col, err := otelcol.NewCollector(otelcol.CollectorSettings{ Factories: func() (otelcol.Factories, error) { - return getComponents(s, logsAgentChannel) + return getComponents(s, logsAgentChannel, tagger) }, BuildInfo: buildInfo, DisableGracefulShutdown: true, @@ -251,7 +252,7 @@ func (p *Pipeline) Stop() { // NewPipelineFromAgentConfig creates a new pipeline from the given agent configuration, metric serializer and logs channel. It returns // any potential failure. -func NewPipelineFromAgentConfig(cfg config.Component, s serializer.MetricSerializer, logsAgentChannel chan *message.Message) (*Pipeline, error) { +func NewPipelineFromAgentConfig(cfg config.Component, s serializer.MetricSerializer, logsAgentChannel chan *message.Message, tagger tagger.Component) (*Pipeline, error) { pcfg, err := FromAgentConfig(cfg) if err != nil { pipelineError.Store(fmt.Errorf("config error: %w", err)) @@ -260,7 +261,7 @@ func NewPipelineFromAgentConfig(cfg config.Component, s serializer.MetricSeriali if err := checkAndUpdateCfg(cfg, pcfg, logsAgentChannel); err != nil { return nil, err } - p, err := NewPipeline(pcfg, s, logsAgentChannel) + p, err := NewPipeline(pcfg, s, logsAgentChannel, tagger) if err != nil { pipelineError.Store(fmt.Errorf("failed to build pipeline: %w", err)) return nil, pipelineError.Load() diff --git a/comp/otelcol/otlp/collector_test.go b/comp/otelcol/otlp/collector_test.go index 3c5c88d872de0..e9e746b664c34 100644 --- a/comp/otelcol/otlp/collector_test.go +++ b/comp/otelcol/otlp/collector_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/testutil" "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/logs/message" @@ -23,13 +24,17 @@ import ( ) func TestGetComponents(t *testing.T) { - _, err := getComponents(&serializer.MockSerializer{}, make(chan *message.Message)) + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + _, err := getComponents(&serializer.MockSerializer{}, make(chan *message.Message), fakeTagger) // No duplicate component require.NoError(t, err) } func AssertSucessfulRun(t *testing.T, pcfg PipelineConfig) { - p, err := NewPipeline(pcfg, &serializer.MockSerializer{}, make(chan *message.Message)) + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + p, err := NewPipeline(pcfg, &serializer.MockSerializer{}, make(chan *message.Message), fakeTagger) require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() @@ -54,7 +59,9 @@ func AssertSucessfulRun(t *testing.T, pcfg PipelineConfig) { } func AssertFailedRun(t *testing.T, pcfg PipelineConfig, expected string) { - p, err := NewPipeline(pcfg, &serializer.MockSerializer{}, make(chan *message.Message)) + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + p, err := NewPipeline(pcfg, &serializer.MockSerializer{}, make(chan *message.Message), fakeTagger) require.NoError(t, err) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod index dc68cc4f9e5e1..0619a0aba20ab 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod @@ -85,7 +85,7 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/message v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/serializer v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/hostname/validate v0.54.0-rc.2 - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 github.com/stretchr/testify v1.9.0 go.opentelemetry.io/collector/component v0.100.0 diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum index 865ca96d98bd5..31449ad831d59 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum @@ -10,8 +10,8 @@ github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 h1:EbzDX8HPk5uE2FsJYx github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0 h1:VJT1Jjlz/ca999FEqaAS+He7S4eB14a+PJjczgRdgAY= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0/go.mod h1:66XlN7QpQKqIvw8e2UbCXV5X8wGnEw851nT9BjJ75dY= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 h1:g/ztrLYZNfkpW6Bt8kMnLed5DaKRHEtiKE0opHXLHJk= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3CgdMXpDplrf9/gIr13+/g/tUzUcBMk2ZhXgzLE= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0 h1:NbKlfbjR2joF52jEBLs3MEnT6l5zM3MCyhUFkqARZpk= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0/go.mod h1:+LijQ2LdlocAQ4WB+7KsoIGe90bfogkRslubd9swVow= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 h1:H5DzD3rwgQCX0VI3A16KgsdmC5grUCyDFflaZDpfgMc= diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod index 6738710153680..119f1230fb91f 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod @@ -42,7 +42,7 @@ require ( github.com/DataDog/datadog-agent/pkg/logs/message v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/logs/sources v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.14.0 github.com/stormcat24/protodep v0.1.8 github.com/stretchr/testify v1.9.0 diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum index fdb527179c99c..e02b4cea1d411 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum @@ -4,8 +4,8 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8 github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/DataDog/datadog-api-client-go/v2 v2.13.0 h1:2c1dXSyUfum2YIVoYlqnBhV5JOG1cLSW+4jB3RrKjLc= github.com/DataDog/datadog-api-client-go/v2 v2.13.0/go.mod h1:kntOqXEh1SmjwSDzW/eJkr9kS7EqttvEkelglWtJRbg= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 h1:10TPqpTlIkmDPFWVIEZ4ZX3rWrCrx3rEoeoAooZr6LM= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3CgdMXpDplrf9/gIr13+/g/tUzUcBMk2ZhXgzLE= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.14.0 h1:nma5ZICTbHZ0YoMu18ziWGSLK1ICzMm6rJTv+IatJ0U= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.14.0/go.mod h1:xUiGj13q5uHPboc0xZ754fyusiF5C2RxNzOFdTbdZFA= github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod index 5a6904988d052..455a9545dcfe2 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod @@ -62,7 +62,7 @@ require ( github.com/DataDog/datadog-agent/pkg/serializer v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/tagset v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.54.0-rc.2 - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.14.0 github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum index f61fcaa4f266e..2c5a7da1b8a34 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum @@ -6,8 +6,8 @@ github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 h1:EbzDX8HPk5uE2FsJYx github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.14.0 h1:J0IEqkrB8BjtuDHofR8Q3J+Z8829Ja1Mlix9cyG8wJI= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.14.0/go.mod h1:66XlN7QpQKqIvw8e2UbCXV5X8wGnEw851nT9BjJ75dY= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 h1:10TPqpTlIkmDPFWVIEZ4ZX3rWrCrx3rEoeoAooZr6LM= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3CgdMXpDplrf9/gIr13+/g/tUzUcBMk2ZhXgzLE= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.14.0 h1:2Ou9n/048KM9E70G+LpJ4svBH2kOb8NkxwEqEGAamHo= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.14.0/go.mod h1:CAj5xIuF45abugAck7OzjMLH5y20ylaEj1qbQEjU1gk= github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0 h1:QHx6B/VUx3rZQqrQNZI5BfypbhhGSRzCz05viyJEQmM= diff --git a/comp/otelcol/otlp/components/pipeline/provider/go.mod b/comp/otelcol/otlp/components/pipeline/provider/go.mod index b8e737ffcc0ff..dd36344c5d0e2 100644 --- a/comp/otelcol/otlp/components/pipeline/provider/go.mod +++ b/comp/otelcol/otlp/components/pipeline/provider/go.mod @@ -82,7 +82,7 @@ require ( github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect github.com/DataDog/gohai v0.0.0-20230524154621-4316413895ee // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.16.0 // indirect - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 // indirect + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 // indirect diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/config.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/config.go index a24a25b8db6fa..8b1c438bdcfc2 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/config.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/config.go @@ -7,6 +7,8 @@ package infraattributesprocessor import ( "go.opentelemetry.io/collector/component" + + "github.com/DataDog/datadog-agent/comp/core/tagger/types" ) // Config defines configuration for processor. @@ -14,6 +16,8 @@ type Config struct { Metrics MetricInfraAttributes `mapstructure:"metrics"` Logs LogInfraAttributes `mapstructure:"logs"` Traces TraceInfraAttributes `mapstructure:"traces"` + + Cardinality types.TagCardinality `mapstructure:"cardinality"` } // MetricInfraAttributes - configuration for metrics. diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/config_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/config_test.go index c8bf6903ec9dc..0011a3b0a8d14 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/config_test.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/config_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/confmap/confmaptest" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" ) // TestLoadingConfigStrictLogs tests loading testdata/logs_strict.yaml @@ -34,8 +36,10 @@ func TestLoadingConfigStrictLogs(t *testing.T) { for _, tt := range tests { t.Run(tt.id.String(), func(t *testing.T) { - factory := NewFactory() - cfg := factory.CreateDefaultConfig() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + f := NewFactory(fakeTagger) + cfg := f.CreateDefaultConfig() sub, err := cm.Sub(tt.id.String()) require.NoError(t, err) diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory.go index b71129f5bfc16..00046a13a8296 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory.go @@ -8,6 +8,9 @@ package infraattributesprocessor import ( "context" + "github.com/DataDog/datadog-agent/comp/core/tagger" + "github.com/DataDog/datadog-agent/comp/core/tagger/types" + "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/processor" @@ -16,28 +19,38 @@ import ( var processorCapabilities = consumer.Capabilities{MutatesData: true} +type factory struct { + tagger tagger.Component +} + // NewFactory returns a new factory for the InfraAttributes processor. -func NewFactory() processor.Factory { +func NewFactory(tagger tagger.Component) processor.Factory { + f := &factory{ + tagger: tagger, + } + return processor.NewFactory( Type, - createDefaultConfig, - processor.WithMetrics(createMetricsProcessor, MetricsStability), - processor.WithLogs(createLogsProcessor, LogsStability), - processor.WithTraces(createTracesProcessor, TracesStability), + f.createDefaultConfig, + processor.WithMetrics(f.createMetricsProcessor, MetricsStability), + processor.WithLogs(f.createLogsProcessor, LogsStability), + processor.WithTraces(f.createTracesProcessor, TracesStability), ) } -func createDefaultConfig() component.Config { - return &Config{} +func (f *factory) createDefaultConfig() component.Config { + return &Config{ + Cardinality: types.LowCardinality, + } } -func createMetricsProcessor( +func (f *factory) createMetricsProcessor( ctx context.Context, set processor.CreateSettings, cfg component.Config, nextConsumer consumer.Metrics, ) (processor.Metrics, error) { - tep, err := newInfraAttributesMetricProcessor(set, cfg.(*Config)) + iap, err := newInfraAttributesMetricProcessor(set, cfg.(*Config), f.tagger) if err != nil { return nil, err } @@ -46,17 +59,17 @@ func createMetricsProcessor( set, cfg, nextConsumer, - tep.processMetrics, + iap.processMetrics, processorhelper.WithCapabilities(processorCapabilities)) } -func createLogsProcessor( +func (f *factory) createLogsProcessor( ctx context.Context, set processor.CreateSettings, cfg component.Config, nextConsumer consumer.Logs, ) (processor.Logs, error) { - tep, err := newInfraAttributesLogsProcessor(set, cfg.(*Config)) + iap, err := newInfraAttributesLogsProcessor(set, cfg.(*Config)) if err != nil { return nil, err } @@ -65,17 +78,17 @@ func createLogsProcessor( set, cfg, nextConsumer, - tep.processLogs, + iap.processLogs, processorhelper.WithCapabilities(processorCapabilities)) } -func createTracesProcessor( +func (f *factory) createTracesProcessor( ctx context.Context, set processor.CreateSettings, cfg component.Config, nextConsumer consumer.Traces, ) (processor.Traces, error) { - tep, err := newInfraAttributesSpanProcessor(set, cfg.(*Config)) + iap, err := newInfraAttributesSpanProcessor(set, cfg.(*Config)) if err != nil { return nil, err } @@ -84,6 +97,6 @@ func createTracesProcessor( set, cfg, nextConsumer, - tep.processTraces, + iap.processTraces, processorhelper.WithCapabilities(processorCapabilities)) } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory_test.go index 0019d68fe9eef..d168a11b40922 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory_test.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/factory_test.go @@ -18,17 +18,23 @@ import ( "go.opentelemetry.io/collector/confmap/confmaptest" "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/processor/processortest" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" ) func TestType(t *testing.T) { - factory := NewFactory() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + factory := NewFactory(fakeTagger) pType := factory.Type() assert.Equal(t, pType, Type) } func TestCreateDefaultConfig(t *testing.T) { - factory := NewFactory() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + factory := NewFactory(fakeTagger) cfg := factory.CreateDefaultConfig() assert.NoError(t, componenttest.CheckConfigStruct(cfg)) } @@ -50,10 +56,12 @@ func TestCreateProcessors(t *testing.T) { t.Run(tt.configName, func(t *testing.T) { cm, err := confmaptest.LoadConf(filepath.Join("testdata", tt.configName)) require.NoError(t, err) + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() for k := range cm.ToStringMap() { // Check if all processor variations that are defined in test config can be actually created - factory := NewFactory() + factory := NewFactory(fakeTagger) cfg := factory.CreateDefaultConfig() sub, err := cm.Sub(k) diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs.go index 577c3ce5015ac..ad326b36c8d80 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs.go @@ -22,7 +22,7 @@ func newInfraAttributesLogsProcessor(set processor.CreateSettings, _ *Config) (* logger: set.Logger, } - set.Logger.Info("Logs Tag Enrichment configured") + set.Logger.Info("Logs Infra Attributes Processor configured") return telp, nil } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs_test.go index f0404fd588663..7775262b101a7 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs_test.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/logs_test.go @@ -14,6 +14,8 @@ import ( "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/pdata/plog" "go.opentelemetry.io/collector/processor/processortest" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" ) type logNameTest struct { @@ -40,7 +42,9 @@ func TestInfraAttributesLogProcessor(t *testing.T) { cfg := &Config{ Logs: LogInfraAttributes{}, } - factory := NewFactory() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + factory := NewFactory(fakeTagger) flp, err := factory.CreateLogsProcessor( context.Background(), processortest.NewNopCreateSettings(), diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metadata.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metadata.go index bf8eba0ad92d0..18eeafea7ed2c 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metadata.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metadata.go @@ -12,7 +12,7 @@ import ( ) var ( - // Type for tag enrichment processor. + // Type for infra attributes processor. Type = component.MustNewType("infraattributes") ) @@ -25,12 +25,12 @@ const ( LogsStability = component.StabilityLevelAlpha ) -// Meter for tag enrichement. +// Meter for infra attributes processor. func Meter(settings component.TelemetrySettings) metric.Meter { return settings.MeterProvider.Meter("otelcol/infraattributes") } -// Tracer for tag enrichment. +// Tracer for infra attributes processor. func Tracer(settings component.TelemetrySettings) trace.Tracer { return settings.TracerProvider.Tracer("otelcol/infraattributes") } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics.go index 63e09edd7d892..95a834b84f10c 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics.go @@ -7,24 +7,120 @@ package infraattributesprocessor import ( "context" + "fmt" + "strings" + "github.com/DataDog/datadog-agent/comp/core/tagger" + "github.com/DataDog/datadog-agent/comp/core/tagger/types" + + "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/processor" + conventions "go.opentelemetry.io/collector/semconv/v1.21.0" "go.uber.org/zap" ) type infraAttributesMetricProcessor struct { - logger *zap.Logger + logger *zap.Logger + tagger tagger.Component + cardinality types.TagCardinality } -func newInfraAttributesMetricProcessor(set processor.CreateSettings, _ *Config) (*infraAttributesMetricProcessor, error) { - tesp := &infraAttributesMetricProcessor{ - logger: set.Logger, +func newInfraAttributesMetricProcessor(set processor.CreateSettings, cfg *Config, tagger tagger.Component) (*infraAttributesMetricProcessor, error) { + iamp := &infraAttributesMetricProcessor{ + logger: set.Logger, + tagger: tagger, + cardinality: cfg.Cardinality, } - set.Logger.Info("Metric Tag Enrichment configured") - return tesp, nil + set.Logger.Info("Metric Infra Attributes Processor configured") + return iamp, nil } -func (temp *infraAttributesMetricProcessor) processMetrics(_ context.Context, md pmetric.Metrics) (pmetric.Metrics, error) { +// TODO: Replace OriginIDFromAttributes in opentelemetry-mapping-go with this method +// entityIDsFromAttributes gets the entity IDs from resource attributes. +// If not found, an empty string slice is returned. +func entityIDsFromAttributes(attrs pcommon.Map) []string { + entityIDs := make([]string, 0, 8) + // Prefixes come from pkg/util/kubernetes/kubelet and pkg/util/containers. + if containerID, ok := attrs.Get(conventions.AttributeContainerID); ok { + entityIDs = append(entityIDs, fmt.Sprintf("container_id://%v", containerID.AsString())) + } + if containerImageID, ok := attrs.Get(conventions.AttributeContainerImageID); ok { + splitImageID := strings.SplitN(containerImageID.AsString(), "@sha256:", 2) + if len(splitImageID) == 2 { + entityIDs = append(entityIDs, fmt.Sprintf("container_image_metadata://sha256:%v", splitImageID[1])) + } + } + if ecsTaskArn, ok := attrs.Get(conventions.AttributeAWSECSTaskARN); ok { + entityIDs = append(entityIDs, fmt.Sprintf("ecs_task://%v", ecsTaskArn.AsString())) + } + if deploymentName, ok := attrs.Get(conventions.AttributeK8SDeploymentName); ok { + namespace, namespaceOk := attrs.Get(conventions.AttributeK8SNamespaceName) + if namespaceOk { + entityIDs = append(entityIDs, fmt.Sprintf("deployment://%v/%v", namespace.AsString(), deploymentName.AsString())) + } + } + if namespace, ok := attrs.Get(conventions.AttributeK8SNamespaceName); ok { + entityIDs = append(entityIDs, fmt.Sprintf("namespace://%v", namespace.AsString())) + } + if nodeUID, ok := attrs.Get(conventions.AttributeK8SNodeUID); ok { + entityIDs = append(entityIDs, fmt.Sprintf("kubernetes_node_uid://%v", nodeUID.AsString())) + } + if podUID, ok := attrs.Get(conventions.AttributeK8SPodUID); ok { + entityIDs = append(entityIDs, fmt.Sprintf("kubernetes_pod_uid://%v", podUID.AsString())) + } + if processPid, ok := attrs.Get(conventions.AttributeProcessPID); ok { + entityIDs = append(entityIDs, fmt.Sprintf("process://%v", processPid.AsString())) + } + return entityIDs +} + +func splitTag(tag string) (key string, value string) { + split := strings.SplitN(tag, ":", 2) + if len(split) < 2 || split[0] == "" || split[1] == "" { + return "", "" + } + return split[0], split[1] +} + +func (iamp *infraAttributesMetricProcessor) processMetrics(_ context.Context, md pmetric.Metrics) (pmetric.Metrics, error) { + rms := md.ResourceMetrics() + for i := 0; i < rms.Len(); i++ { + resourceAttributes := rms.At(i).Resource().Attributes() + entityIDs := entityIDsFromAttributes(resourceAttributes) + tagMap := make(map[string]string) + + // Get all unique tags from resource attributes and global tags + for _, entityID := range entityIDs { + entityTags, err := iamp.tagger.Tag(entityID, iamp.cardinality) + if err != nil { + iamp.logger.Error("Cannot get tags for entity", zap.String("entityID", entityID), zap.Error(err)) + continue + } + for _, tag := range entityTags { + k, v := splitTag(tag) + _, hasTag := tagMap[k] + if k != "" && v != "" && !hasTag { + tagMap[k] = v + } + } + } + globalTags, err := iamp.tagger.GlobalTags(iamp.cardinality) + if err != nil { + iamp.logger.Error("Cannot get global tags", zap.Error(err)) + } + for _, tag := range globalTags { + k, v := splitTag(tag) + _, hasTag := tagMap[k] + if k != "" && v != "" && !hasTag { + tagMap[k] = v + } + } + + // Add all tags as resource attributes + for k, v := range tagMap { + resourceAttributes.PutStr(k, v) + } + } return md, nil } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics_test.go index c6adb8492a7aa..50892d20c94a3 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics_test.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/metrics_test.go @@ -11,13 +11,20 @@ import ( "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" "go.opentelemetry.io/collector/processor/processortest" + conventions "go.opentelemetry.io/collector/semconv/v1.21.0" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl/collectors" + "github.com/DataDog/datadog-agent/comp/core/tagger/types" ) type metricNameTest struct { - name string - inMetrics pmetric.Metrics + name string + inMetrics pmetric.Metrics + outResourceAttributes []map[string]any } type metricWithResource struct { @@ -32,20 +39,101 @@ var ( standardTests = []metricNameTest{ { - name: "includeFilter", - inMetrics: testResourceMetrics([]metricWithResource{{metricNames: inMetricNames}}), + name: "one tag with global", + inMetrics: testResourceMetrics([]metricWithResource{{ + metricNames: inMetricNames, + resourceAttributes: map[string]any{ + "container.id": "test", + }, + }}), + outResourceAttributes: []map[string]any{{ + "global": "tag", + "container.id": "test", + "container": "id", + }}, + }, + { + name: "two tags with global", + inMetrics: testResourceMetrics([]metricWithResource{{ + metricNames: inMetricNames, + resourceAttributes: map[string]any{ + "container.id": "test", + "k8s.namespace.name": "namespace", + "k8s.deployment.name": "deployment", + }, + }}), + outResourceAttributes: []map[string]any{{ + "global": "tag", + "container.id": "test", + "k8s.namespace.name": "namespace", + "k8s.deployment.name": "deployment", + "container": "id", + "deployment": "name", + }}, + }, + { + name: "two resource metrics, two tags with global", + inMetrics: testResourceMetrics([]metricWithResource{ + { + metricNames: inMetricNames, + resourceAttributes: map[string]any{ + "container.id": "test", + }, + }, + { + metricNames: inMetricNames, + resourceAttributes: map[string]any{ + "k8s.namespace.name": "namespace", + "k8s.deployment.name": "deployment", + }, + }}), + outResourceAttributes: []map[string]any{ + { + "global": "tag", + "container.id": "test", + "container": "id", + }, + { + "global": "tag", + "k8s.namespace.name": "namespace", + "k8s.deployment.name": "deployment", + "deployment": "name", + }, + }, }, } ) +func testResourceMetrics(mwrs []metricWithResource) pmetric.Metrics { + md := pmetric.NewMetrics() + + for _, mwr := range mwrs { + rm := md.ResourceMetrics().AppendEmpty() + //nolint:errcheck + rm.Resource().Attributes().FromRaw(mwr.resourceAttributes) + ms := rm.ScopeMetrics().AppendEmpty().Metrics() + for _, name := range mwr.metricNames { + m := ms.AppendEmpty() + m.SetName(name) + } + } + return md +} + func TestInfraAttributesMetricProcessor(t *testing.T) { for _, test := range standardTests { t.Run(test.name, func(t *testing.T) { next := new(consumertest.MetricsSink) cfg := &Config{ - Metrics: MetricInfraAttributes{}, + Metrics: MetricInfraAttributes{}, + Cardinality: types.LowCardinality, } - factory := NewFactory() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + fakeTagger.SetTags("container_id://test", "test", []string{"container:id"}, nil, nil, nil) + fakeTagger.SetTags("deployment://namespace/deployment", "test", []string{"deployment:name"}, nil, nil, nil) + fakeTagger.SetTags(collectors.GlobalEntityID, "test", []string{"global:tag"}, nil, nil, nil) + factory := NewFactory(fakeTagger) fmp, err := factory.CreateMetricsProcessor( context.Background(), processortest.NewNopCreateSettings(), @@ -63,22 +151,124 @@ func TestInfraAttributesMetricProcessor(t *testing.T) { cErr := fmp.ConsumeMetrics(context.Background(), test.inMetrics) assert.Nil(t, cErr) assert.NoError(t, fmp.Shutdown(ctx)) + + assert.Len(t, next.AllMetrics(), 1) + for i, out := range test.outResourceAttributes { + rms := next.AllMetrics()[0].ResourceMetrics().At(i) + assert.NotNil(t, rms) + assert.EqualValues(t, out, rms.Resource().Attributes().AsRaw()) + } }) } } -func testResourceMetrics(mwrs []metricWithResource) pmetric.Metrics { - md := pmetric.NewMetrics() +func TestEntityIDsFromAttributes(t *testing.T) { + tests := []struct { + name string + attrs pcommon.Map + entityIDs []string + }{ + { + name: "none", + attrs: pcommon.NewMap(), + entityIDs: []string{}, + }, + { + name: "pod UID and container ID", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeContainerID: "container_id_goes_here", + conventions.AttributeK8SPodUID: "k8s_pod_uid_goes_here", + }) + return attributes + }(), + entityIDs: []string{"container_id://container_id_goes_here", "kubernetes_pod_uid://k8s_pod_uid_goes_here"}, + }, + { + name: "container image ID", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeContainerImageID: "docker.io/foo@sha256:sha_goes_here", + }) + return attributes + }(), + entityIDs: []string{"container_image_metadata://sha256:sha_goes_here"}, + }, + { + name: "ecs task arn", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeAWSECSTaskARN: "ecs_task_arn_goes_here", + }) + return attributes + }(), + entityIDs: []string{"ecs_task://ecs_task_arn_goes_here"}, + }, + { + name: "only deployment name without namespace", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeK8SDeploymentName: "k8s_deployment_name_goes_here", + }) + return attributes + }(), + entityIDs: []string{}, + }, + { + name: "deployment name and namespace", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeK8SDeploymentName: "k8s_deployment_name_goes_here", + conventions.AttributeK8SNamespaceName: "k8s_namespace_goes_here", + }) + return attributes + }(), + entityIDs: []string{"deployment://k8s_namespace_goes_here/k8s_deployment_name_goes_here", "namespace://k8s_namespace_goes_here"}, + }, + { + name: "only namespace name", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeK8SNamespaceName: "k8s_namespace_goes_here", + }) + return attributes + }(), + entityIDs: []string{"namespace://k8s_namespace_goes_here"}, + }, + { + name: "only node UID", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeK8SNodeUID: "k8s_node_uid_goes_here", + }) + return attributes + }(), + entityIDs: []string{"kubernetes_node_uid://k8s_node_uid_goes_here"}, + }, + { + name: "only process pid", + attrs: func() pcommon.Map { + attributes := pcommon.NewMap() + attributes.FromRaw(map[string]interface{}{ + conventions.AttributeProcessPID: "process_pid_goes_here", + }) + return attributes + }(), + entityIDs: []string{"process://process_pid_goes_here"}, + }, + } - for _, mwr := range mwrs { - rm := md.ResourceMetrics().AppendEmpty() - //nolint:errcheck - rm.Resource().Attributes().FromRaw(mwr.resourceAttributes) - ms := rm.ScopeMetrics().AppendEmpty().Metrics() - for _, name := range mwr.metricNames { - m := ms.AppendEmpty() - m.SetName(name) - } + for _, testInstance := range tests { + t.Run(testInstance.name, func(t *testing.T) { + entityIDs := entityIDsFromAttributes(testInstance.attrs) + assert.Equal(t, testInstance.entityIDs, entityIDs) + }) } - return md } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/package_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/package_test.go deleted file mode 100644 index b95a7add774bf..0000000000000 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/package_test.go +++ /dev/null @@ -1,16 +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 2024-present Datadog, Inc. - -package infraattributesprocessor - -import ( - "testing" - - "go.uber.org/goleak" -) - -func TestMain(m *testing.M) { - goleak.VerifyTestMain(m) -} diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces.go index 6a3134833165e..857a7c482c708 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces.go @@ -21,7 +21,7 @@ func newInfraAttributesSpanProcessor(set processor.CreateSettings, _ *Config) (* tesp := &infraAttributesSpanProcessor{ logger: set.Logger, } - set.Logger.Info("Span Tag Enrichment configured") + set.Logger.Info("Span Infra Attributes Processor configured") return tesp, nil } diff --git a/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces_test.go b/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces_test.go index f52767228d5ab..aebef2f510f60 100644 --- a/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces_test.go +++ b/comp/otelcol/otlp/components/processor/infraattributesprocessor/traces_test.go @@ -13,6 +13,8 @@ import ( "go.opentelemetry.io/collector/consumer/consumertest" "go.opentelemetry.io/collector/pdata/ptrace" "go.opentelemetry.io/collector/processor/processortest" + + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" ) // All the data we need to test the Span @@ -51,7 +53,9 @@ func TestInfraAttributesTraceProcessor(t *testing.T) { ctx := context.Background() next := new(consumertest.TracesSink) cfg := &Config{} - factory := NewFactory() + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + factory := NewFactory(fakeTagger) fmp, err := factory.CreateTracesProcessor( ctx, processortest.NewNopCreateSettings(), diff --git a/comp/otelcol/otlp/map_provider_not_serverless_test.go b/comp/otelcol/otlp/map_provider_not_serverless_test.go index 14c18fcf4f3d1..55609fb5eed90 100644 --- a/comp/otelcol/otlp/map_provider_not_serverless_test.go +++ b/comp/otelcol/otlp/map_provider_not_serverless_test.go @@ -12,13 +12,13 @@ import ( "context" "testing" - "github.com/DataDog/datadog-agent/pkg/logs/message" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.opentelemetry.io/collector/confmap" + "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" "github.com/DataDog/datadog-agent/comp/otelcol/otlp/testutil" + "github.com/DataDog/datadog-agent/pkg/logs/message" "github.com/DataDog/datadog-agent/pkg/serializer" ) @@ -1112,7 +1112,9 @@ func TestUnmarshal(t *testing.T) { }, }) require.NoError(t, err) - components, err := getComponents(&serializer.MockSerializer{}, make(chan *message.Message)) + fakeTagger := taggerimpl.SetupFakeTagger(t) + defer fakeTagger.ResetTagger() + components, err := getComponents(&serializer.MockSerializer{}, make(chan *message.Message), fakeTagger) require.NoError(t, err) _, err = provider.Get(context.Background(), components) diff --git a/go.mod b/go.mod index 1881cf5256056..b795241df28a6 100644 --- a/go.mod +++ b/go.mod @@ -139,7 +139,7 @@ require ( github.com/DataDog/ebpf-manager v0.6.0 github.com/DataDog/gopsutil v1.2.2 github.com/DataDog/nikos v1.12.4 - github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 github.com/DataDog/sketches-go v1.4.4 @@ -553,7 +553,7 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector/consumer v0.100.0 go.opentelemetry.io/collector/featuregate v1.7.0 - go.opentelemetry.io/collector/semconv v0.100.0 // indirect + go.opentelemetry.io/collector/semconv v0.100.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.26.0 // indirect go.opentelemetry.io/otel v1.26.0 // indirect @@ -718,7 +718,6 @@ require ( go.opentelemetry.io/collector/otelcol v0.100.0 go.opentelemetry.io/collector/processor v0.100.0 go.opentelemetry.io/collector/service v0.100.0 - go.uber.org/goleak v1.3.0 go4.org/intern v0.0.0-20230525184215-6c62f75575cb go4.org/mem v0.0.0-20220726221520-4f986261bf13 gotest.tools v2.2.0+incompatible diff --git a/go.sum b/go.sum index 6c02f96394e00..aea8c3125abbf 100644 --- a/go.sum +++ b/go.sum @@ -787,8 +787,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.16.0 h1:Jl7/oQQ github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.16.0/go.mod h1:P/l++2cDCeeq21KSmCEdXdMH9/WMdXP7uA/vjnxhtz8= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0 h1:VJT1Jjlz/ca999FEqaAS+He7S4eB14a+PJjczgRdgAY= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0/go.mod h1:66XlN7QpQKqIvw8e2UbCXV5X8wGnEw851nT9BjJ75dY= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 h1:g/ztrLYZNfkpW6Bt8kMnLed5DaKRHEtiKE0opHXLHJk= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3CgdMXpDplrf9/gIr13+/g/tUzUcBMk2ZhXgzLE= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0 h1:NbKlfbjR2joF52jEBLs3MEnT6l5zM3MCyhUFkqARZpk= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0/go.mod h1:+LijQ2LdlocAQ4WB+7KsoIGe90bfogkRslubd9swVow= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 h1:H5DzD3rwgQCX0VI3A16KgsdmC5grUCyDFflaZDpfgMc= diff --git a/pkg/serverless/otlp/otlp.go b/pkg/serverless/otlp/otlp.go index b0e11539d6408..bf03f7ca40b29 100644 --- a/pkg/serverless/otlp/otlp.go +++ b/pkg/serverless/otlp/otlp.go @@ -29,7 +29,7 @@ type ServerlessOTLPAgent struct { // NewServerlessOTLPAgent creates a new ServerlessOTLPAgent with the correct // otel pipeline. func NewServerlessOTLPAgent(serializer serializer.MetricSerializer) *ServerlessOTLPAgent { - pipeline, err := coreOtlp.NewPipelineFromAgentConfig(config.Datadog(), serializer, nil) + pipeline, err := coreOtlp.NewPipelineFromAgentConfig(config.Datadog(), serializer, nil, nil) if err != nil { log.Error("Error creating new otlp pipeline:", err) return nil From 1d448ec876f6f48bc6eb62f70332ef681422ec97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rodrigo=20Arg=C3=BCello?= Date: Thu, 30 May 2024 01:53:18 +0200 Subject: [PATCH 11/54] service_discovery: add telemetry (#25868) Co-authored-by: jonbodner --- .../corechecks/servicediscovery/errors.go | 32 +++ .../corechecks/servicediscovery/impl_linux.go | 129 ++++--------- .../servicediscovery/impl_linux_mock.go | 5 - .../servicediscovery/impl_linux_test.go | 63 +++++- .../servicediscovery/portlist/port.go | 9 +- .../servicediscovery/servicediscovery.go | 182 ++++++++++++++++-- .../servicediscovery/servicediscovery_mock.go | 12 +- .../corechecks/servicediscovery/telemetry.go | 58 ++++++ 8 files changed, 356 insertions(+), 134 deletions(-) create mode 100644 pkg/collector/corechecks/servicediscovery/errors.go create mode 100644 pkg/collector/corechecks/servicediscovery/telemetry.go diff --git a/pkg/collector/corechecks/servicediscovery/errors.go b/pkg/collector/corechecks/servicediscovery/errors.go new file mode 100644 index 0000000000000..2611d6fbbeecd --- /dev/null +++ b/pkg/collector/corechecks/servicediscovery/errors.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. + +package servicediscovery + +import ( + "fmt" +) + +type errCode string + +const ( + errorCodeProcfs errCode = "procfs" + errorCodePortPoller errCode = "port_poller" + errorCodeRepeatedServiceName errCode = "repeated_service_name" +) + +type errWithCode struct { + err error + code errCode + svc *serviceMetadata +} + +func (e errWithCode) Error() string { + return fmt.Sprintf("%s: %s", e.code, e.err.Error()) +} + +func (e errWithCode) Code() errCode { + return e.code +} diff --git a/pkg/collector/corechecks/servicediscovery/impl_linux.go b/pkg/collector/corechecks/servicediscovery/impl_linux.go index 181295e28eaca..c80882c508c90 100644 --- a/pkg/collector/corechecks/servicediscovery/impl_linux.go +++ b/pkg/collector/corechecks/servicediscovery/impl_linux.go @@ -41,7 +41,6 @@ type linuxImpl struct { bootTime uint64 serviceDetector *serviceDetector - sender *telemetrySender ignoreCfg map[string]bool ignoreProcs map[int]bool @@ -49,7 +48,7 @@ type linuxImpl struct { potentialServices map[int]*serviceInfo } -func newLinuxImpl(sender *telemetrySender, ignoreCfg map[string]bool) (osImpl, error) { +func newLinuxImpl(ignoreCfg map[string]bool) (osImpl, error) { for _, i := range ignoreCfgLinux { ignoreCfg[i] = true } @@ -70,7 +69,6 @@ func newLinuxImpl(sender *telemetrySender, ignoreCfg map[string]bool) (osImpl, e bootTime: stat.BootTime, portPoller: poller, time: realTime{}, - sender: sender, serviceDetector: newServiceDetector(), ignoreCfg: ignoreCfg, ignoreProcs: make(map[int]bool), @@ -79,54 +77,30 @@ func newLinuxImpl(sender *telemetrySender, ignoreCfg map[string]bool) (osImpl, e }, nil } -type processEvents struct { - started []serviceInfo - stopped []serviceInfo -} - -type eventsByName map[string]*processEvents - -func (e eventsByName) addStarted(svc serviceInfo) { - events, ok := e[svc.meta.Name] - if !ok { - events = &processEvents{} - } - events.started = append(events.started, svc) - e[svc.meta.Name] = events -} - -func (e eventsByName) addStopped(svc serviceInfo) { - events, ok := e[svc.meta.Name] - if !ok { - events = &processEvents{} - } - events.stopped = append(events.stopped, svc) - e[svc.meta.Name] = events -} - -func (li *linuxImpl) DiscoverServices() error { +func (li *linuxImpl) DiscoverServices() (*discoveredServices, error) { procs, err := li.aliveProcs() if err != nil { - return fmt.Errorf("failed to get alive processes: %w", err) + return nil, errWithCode{ + err: err, + code: errorCodeProcfs, + svc: nil, + } } - ports, err := li.openPorts() + ports, err := li.portPoller.OpenPorts() if err != nil { - return fmt.Errorf("failed to get open ports: %w", err) + return nil, errWithCode{ + err: err, + code: errorCodePortPoller, + svc: nil, + } + } + portsByPID := map[int]portlist.List{} + for _, p := range ports { + portsByPID[p.Pid] = append(portsByPID[p.Pid], p) } - log.Debugf("aliveProcs: %d | ignoreProcs: %d | runningServices: %d | potentials: %d | openPorts: %+v", - len(procs), - len(li.ignoreProcs), - len(li.aliveServices), - len(li.potentialServices), - ports, - ) - - var ( - started []serviceInfo - stopped []serviceInfo - ) + events := serviceEvents{} now := li.time.Now() @@ -136,13 +110,13 @@ func (li *linuxImpl) DiscoverServices() error { if _, ok := procs[pid]; ok { svc.LastHeartbeat = now li.aliveServices[pid] = svc - started = append(started, *svc) + events.start = append(events.start, *svc) } } clear(li.potentialServices) // check open ports - these will be potential new services if they are still alive in the next iteration. - for pid := range ports { + for pid := range portsByPID { if li.ignoreProcs[pid] { continue } @@ -155,8 +129,13 @@ func (li *linuxImpl) DiscoverServices() error { continue } - svc, err := li.getServiceInfo(p, ports) + svc, err := li.getServiceInfo(p, portsByPID) if err != nil { + telemetryFromError(errWithCode{ + err: err, + code: errorCodeProcfs, + svc: nil, + }) log.Errorf("[pid: %d] failed to get process info: %v", pid, err) li.ignoreProcs[pid] = true continue @@ -175,10 +154,10 @@ func (li *linuxImpl) DiscoverServices() error { for pid, svc := range li.aliveServices { if _, ok := procs[pid]; !ok { delete(li.aliveServices, pid) - stopped = append(stopped, *svc) + events.stop = append(events.stop, *svc) } else if now.Sub(svc.LastHeartbeat).Truncate(time.Minute) >= heartbeatTime { svc.LastHeartbeat = now - li.sender.sendHeartbeatServiceEvent(*svc) + events.heartbeat = append(events.heartbeat, *svc) } } @@ -189,38 +168,14 @@ func (li *linuxImpl) DiscoverServices() error { } } - // group started and stopped processes by name - events := make(eventsByName) - for _, p := range started { - events.addStarted(p) - } - for _, p := range stopped { - events.addStopped(p) - } - - potentialNames := map[string]bool{} - for _, p := range li.potentialServices { - potentialNames[p.meta.Name] = true - } - - for name, ev := range events { - if len(ev.started) > 0 && len(ev.stopped) > 0 { - log.Warnf("found multiple started/stopped processes with the same name, ignoring end-service events (name: %q)", name) - clear(ev.stopped) - } - for _, svc := range ev.started { - li.sender.sendStartServiceEvent(svc) - } - for _, svc := range ev.stopped { - if potentialNames[name] { - log.Debugf("there is a potential service with the same name as a stopped one, skipping end-service event (name: %q)", name) - break - } - li.sender.sendEndServiceEvent(svc) - } - } - - return nil + return &discoveredServices{ + aliveProcsCount: len(procs), + openPorts: ports, + ignoreProcs: li.ignoreProcs, + potentials: li.potentialServices, + runningServices: li.aliveServices, + events: events, + }, nil } func (li *linuxImpl) aliveProcs() (map[int]proc, error) { @@ -235,18 +190,6 @@ func (li *linuxImpl) aliveProcs() (map[int]proc, error) { return procMap, nil } -func (li *linuxImpl) openPorts() (map[int]portlist.List, error) { - ports, err := li.portPoller.OpenPorts() - if err != nil { - return nil, err - } - portMap := map[int]portlist.List{} - for _, p := range ports { - portMap[p.Pid] = append(portMap[p.Pid], p) - } - return portMap, nil -} - func (li *linuxImpl) getServiceInfo(p proc, openPorts map[int]portlist.List) (*serviceInfo, error) { cmdline, err := p.CmdLine() if err != nil { diff --git a/pkg/collector/corechecks/servicediscovery/impl_linux_mock.go b/pkg/collector/corechecks/servicediscovery/impl_linux_mock.go index 7f94c1a319229..af9fd777ee97c 100644 --- a/pkg/collector/corechecks/servicediscovery/impl_linux_mock.go +++ b/pkg/collector/corechecks/servicediscovery/impl_linux_mock.go @@ -7,11 +7,6 @@ // Code generated by MockGen. DO NOT EDIT. // Source: impl_linux.go -// -// Generated by this command: -// -// mockgen -source=impl_linux.go -package=servicediscovery -destination=impl_linux_mock.go -// // Package servicediscovery is a generated GoMock package. package servicediscovery diff --git a/pkg/collector/corechecks/servicediscovery/impl_linux_test.go b/pkg/collector/corechecks/servicediscovery/impl_linux_test.go index 19912a70dbfe3..98b01dde9ad40 100644 --- a/pkg/collector/corechecks/servicediscovery/impl_linux_test.go +++ b/pkg/collector/corechecks/servicediscovery/impl_linux_test.go @@ -9,6 +9,7 @@ package servicediscovery import ( "cmp" + "errors" "testing" "time" @@ -283,8 +284,6 @@ func Test_linuxImpl(t *testing.T) { }, }, { - // TODO: ideally we would like to emit some sort of telemetry for this case. - // For now, we just test we send the correct events to EvP. name: "repeated_service_name", checkRun: []*checkRun{ { @@ -560,8 +559,8 @@ func Test_linuxImpl(t *testing.T) { check.os.(*linuxImpl).procfs = mProcFS check.os.(*linuxImpl).portPoller = mPortPoller check.os.(*linuxImpl).time = mTimer - check.os.(*linuxImpl).sender.hostname = mHostname check.os.(*linuxImpl).bootTime = bootTimeSeconds + check.sender.hostname = mHostname err = check.Run() require.NoError(t, err) @@ -578,3 +577,61 @@ func Test_linuxImpl(t *testing.T) { }) } } + +type errorProcFS struct{} + +func (errorProcFS) AllProcs() ([]proc, error) { + return nil, errors.New("procFS failure") +} + +type emptyProcFS struct{} + +func (emptyProcFS) AllProcs() ([]proc, error) { + return []proc{}, nil +} + +type errorPortPoller struct{} + +func (errorPortPoller) OpenPorts() (portlist.List, error) { + return nil, errors.New("portPoller failure") +} + +func Test_linuxImpl_errors(t *testing.T) { + // bad procFS + { + li := linuxImpl{ + procfs: errorProcFS{}, + } + ds, err := li.DiscoverServices() + if ds != nil { + t.Error("expected nil discovery service") + } + var expected errWithCode + if errors.As(err, &expected) { + if expected.Code() != errorCodeProcfs { + t.Errorf("expected error code procfs: %#v", expected) + } + } else { + t.Error("expected errWithCode, got", err) + } + } + // bad portPoller + { + li := linuxImpl{ + procfs: emptyProcFS{}, + portPoller: errorPortPoller{}, + } + ds, err := li.DiscoverServices() + if ds != nil { + t.Error("expected nil discovery service") + } + var expected errWithCode + if errors.As(err, &expected) { + if expected.Code() != errorCodePortPoller { + t.Errorf("expected error code portPoller: %#v", expected) + } + } else { + t.Error("expected errWithCode, got", err) + } + } +} diff --git a/pkg/collector/corechecks/servicediscovery/portlist/port.go b/pkg/collector/corechecks/servicediscovery/portlist/port.go index a5af1dec74546..3567962826afa 100644 --- a/pkg/collector/corechecks/servicediscovery/portlist/port.go +++ b/pkg/collector/corechecks/servicediscovery/portlist/port.go @@ -39,12 +39,11 @@ func (a *Port) lessThan(b *Port) bool { type List []Port func (pl List) String() string { - var sb strings.Builder - for _, v := range pl { - fmt.Fprintf(&sb, "%-3s %5d %#v\n", - v.Proto, v.Port, v.Process) + out := make([]string, len(pl)) + for i, v := range pl { + out[i] = fmt.Sprintf("[%s]%s:%d", v.Process, v.Proto, v.Port) } - return strings.TrimRight(sb.String(), "\n") + return strings.Join(out, ",") } // sortAndDedup sorts ps in place (by Port.lessThan) and then returns diff --git a/pkg/collector/corechecks/servicediscovery/servicediscovery.go b/pkg/collector/corechecks/servicediscovery/servicediscovery.go index 370f0e3e5a7ad..47c68851bd7bc 100644 --- a/pkg/collector/corechecks/servicediscovery/servicediscovery.go +++ b/pkg/collector/corechecks/servicediscovery/servicediscovery.go @@ -18,18 +18,14 @@ import ( "github.com/DataDog/datadog-agent/pkg/aggregator/sender" "github.com/DataDog/datadog-agent/pkg/collector/check" "github.com/DataDog/datadog-agent/pkg/collector/corechecks" + "github.com/DataDog/datadog-agent/pkg/collector/corechecks/servicediscovery/portlist" pkgconfig "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/optional" ) //go:generate mockgen -source=$GOFILE -package=$GOPACKAGE -destination=servicediscovery_mock.go -type osImpl interface { - DiscoverServices() error -} - -var newOSImpl func(sender *telemetrySender, ignoreCfg map[string]bool) (osImpl, error) - const ( // CheckName is the name of the check. CheckName = "service_discovery" @@ -38,20 +34,16 @@ const ( heartbeatTime = 15 * time.Minute ) -type config struct { - IgnoreProcesses []string `yaml:"ignore_processes"` -} - -type procStat struct { - StartTime uint64 -} - type serviceInfo struct { process processInfo meta serviceMetadata LastHeartbeat time.Time } +type procStat struct { + StartTime uint64 +} + type processInfo struct { PID int CmdLine []string @@ -61,6 +53,35 @@ type processInfo struct { Ports []int } +type serviceEvents struct { + start []serviceInfo + stop []serviceInfo + heartbeat []serviceInfo +} + +type discoveredServices struct { + aliveProcsCount int + openPorts portlist.List + + ignoreProcs map[int]bool + potentials map[int]*serviceInfo + runningServices map[int]*serviceInfo + + events serviceEvents +} + +type osImpl interface { + DiscoverServices() (*discoveredServices, error) +} + +var ( + newOSImpl func(ignoreCfg map[string]bool) (osImpl, error) +) + +type config struct { + IgnoreProcesses []string `yaml:"ignore_processes"` +} + // Parse parses the configuration func (c *config) Parse(data []byte) error { if err := yaml.Unmarshal(data, c); err != nil { @@ -72,8 +93,10 @@ func (c *config) Parse(data []byte) error { // Check reports discovered services. type Check struct { corechecks.CheckBase - cfg *config - os osImpl + cfg *config + os osImpl + sender *telemetrySender + sentRepeatedEventPIDs map[int]bool } // Factory creates a new check factory @@ -88,8 +111,9 @@ func Factory() optional.Option[func() check.Check] { func newCheck() check.Check { return &Check{ - CheckBase: corechecks.NewCheckBase(CheckName), - cfg: &config{}, + CheckBase: corechecks.NewCheckBase(CheckName), + cfg: &config{}, + sentRepeatedEventPIDs: make(map[int]bool), } } @@ -117,8 +141,9 @@ func (c *Check) Configure(senderManager sender.SenderManager, _ uint64, instance if err != nil { return err } + c.sender = newTelemetrySender(s) - c.os, err = newOSImpl(newTelemetrySender(s), ignoreCfg) + c.os, err = newOSImpl(ignoreCfg) if err != nil { return err } @@ -128,7 +153,124 @@ func (c *Check) Configure(senderManager sender.SenderManager, _ uint64, instance // Run executes the check. func (c *Check) Run() error { - return c.os.DiscoverServices() + start := time.Now() + defer func() { + diff := time.Since(start).Seconds() + metricTimeToScan.Observe(diff) + }() + + disc, err := c.os.DiscoverServices() + if err != nil { + telemetryFromError(err) + return err + } + + log.Debugf("aliveProcs: %d | ignoreProcs: %d | runningServices: %d | potentials: %d | openPorts: %s", + disc.aliveProcsCount, + len(disc.ignoreProcs), + len(disc.runningServices), + len(disc.potentials), + disc.openPorts.String(), + ) + metricDiscoveredServices.Set(float64(len(disc.runningServices))) + + runningServicesByName := map[string][]*serviceInfo{} + for _, svc := range disc.runningServices { + runningServicesByName[svc.meta.Name] = append(runningServicesByName[svc.meta.Name], svc) + } + for _, svcs := range runningServicesByName { + if len(svcs) <= 1 { + continue + } + for _, svc := range svcs { + if c.sentRepeatedEventPIDs[svc.process.PID] { + continue + } + err := fmt.Errorf("found repeated service name: %s", svc.meta.Name) + telemetryFromError(errWithCode{ + err: err, + code: errorCodeRepeatedServiceName, + svc: &svc.meta, + }) + // track the PID, so we don't increase this counter in every run of the check. + c.sentRepeatedEventPIDs[svc.process.PID] = true + } + } + + potentialNames := map[string]bool{} + for _, p := range disc.potentials { + potentialNames[p.meta.Name] = true + } + + // group events by name in order to find repeated events for the same service name. + eventsByName := make(eventsByNameMap) + for _, p := range disc.events.start { + eventsByName.addStart(p) + } + for _, p := range disc.events.heartbeat { + eventsByName.addHeartbeat(p) + } + for _, p := range disc.events.stop { + if potentialNames[p.meta.Name] { + // we consider this situation a restart, so we skip the stop event. + log.Debugf("there is a potential service with the same name as a stopped one, skipping end-service event (name: %q)", p.meta.Name) + continue + } + eventsByName.addStop(p) + if c.sentRepeatedEventPIDs[p.process.PID] { + // delete this process from the map, so we track it if the PID gets reused + delete(c.sentRepeatedEventPIDs, p.process.PID) + } + } + + for name, ev := range eventsByName { + if len(ev.start) > 0 && len(ev.stop) > 0 || len(ev.heartbeat) > 0 && len(ev.stop) > 0 { + // this is a consequence of the possibility of generating the same service name for different processes. + // at this point, we just skip the end-service events so at least these services don't disappear in the UI. + log.Debugf("got multiple start/heartbeat/end service events for the same service name, skipping end-service events (name: %q)", name) + clear(ev.stop) + } + for _, svc := range ev.start { + c.sender.sendStartServiceEvent(svc) + } + for _, svc := range ev.heartbeat { + c.sender.sendHeartbeatServiceEvent(svc) + } + for _, svc := range ev.stop { + c.sender.sendEndServiceEvent(svc) + } + } + + return nil +} + +type eventsByNameMap map[string]*serviceEvents + +func (m eventsByNameMap) addStart(svc serviceInfo) { + events, ok := m[svc.meta.Name] + if !ok { + events = &serviceEvents{} + } + events.start = append(events.start, svc) + m[svc.meta.Name] = events +} + +func (m eventsByNameMap) addHeartbeat(svc serviceInfo) { + events, ok := m[svc.meta.Name] + if !ok { + events = &serviceEvents{} + } + events.heartbeat = append(events.heartbeat, svc) + m[svc.meta.Name] = events +} + +func (m eventsByNameMap) addStop(svc serviceInfo) { + events, ok := m[svc.meta.Name] + if !ok { + events = &serviceEvents{} + } + events.stop = append(events.stop, svc) + m[svc.meta.Name] = events } // Interval returns how often the check should run. diff --git a/pkg/collector/corechecks/servicediscovery/servicediscovery_mock.go b/pkg/collector/corechecks/servicediscovery/servicediscovery_mock.go index e06e6dbf2664d..6591e5f3b21d1 100644 --- a/pkg/collector/corechecks/servicediscovery/servicediscovery_mock.go +++ b/pkg/collector/corechecks/servicediscovery/servicediscovery_mock.go @@ -5,11 +5,6 @@ // Code generated by MockGen. DO NOT EDIT. // Source: servicediscovery.go -// -// Generated by this command: -// -// mockgen -source=servicediscovery.go -package=servicediscovery -destination=servicediscovery_mock.go -// // Package servicediscovery is a generated GoMock package. package servicediscovery @@ -45,11 +40,12 @@ func (m *MockosImpl) EXPECT() *MockosImplMockRecorder { } // DiscoverServices mocks base method. -func (m *MockosImpl) DiscoverServices() error { +func (m *MockosImpl) DiscoverServices() (*discoveredServices, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "DiscoverServices") - ret0, _ := ret[0].(error) - return ret0 + ret0, _ := ret[0].(*discoveredServices) + ret1, _ := ret[1].(error) + return ret0, ret1 } // DiscoverServices indicates an expected call of DiscoverServices. diff --git a/pkg/collector/corechecks/servicediscovery/telemetry.go b/pkg/collector/corechecks/servicediscovery/telemetry.go new file mode 100644 index 0000000000000..4f38a5e0fc434 --- /dev/null +++ b/pkg/collector/corechecks/servicediscovery/telemetry.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 servicediscovery + +import ( + "errors" + "github.com/DataDog/datadog-agent/pkg/telemetry" + "github.com/DataDog/datadog-agent/pkg/util/log" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + metricTagErrorCode = "error_code" + metricTagServiceName = "service_name" + metricTagServiceLanguage = "service_language" + metricTagServiceType = "service_type" +) + +var ( + metricFailureEvents = telemetry.NewCounterWithOpts( + CheckName, + "failure_events", + []string{metricTagErrorCode, metricTagServiceName, metricTagServiceLanguage, metricTagServiceType}, + "Number of times an error or an unexpected event happened.", + telemetry.Options{NoDoubleUnderscoreSep: true}, + ) + metricDiscoveredServices = telemetry.NewGaugeWithOpts( + CheckName, + "discovered_services", + []string{}, + "Number of discovered alive services.", + telemetry.Options{NoDoubleUnderscoreSep: true}, + ) + metricTimeToScan = telemetry.NewHistogramWithOpts( + CheckName, + "time_to_scan", + []string{}, + "Time it took to scan services", + prometheus.DefBuckets, + telemetry.Options{NoDoubleUnderscoreSep: true}, + ) +) + +func telemetryFromError(err error) { + var codeErr errWithCode + if errors.As(err, &codeErr) { + log.Debugf("sending telemetry for error: %v", err) + svc := serviceMetadata{} + if codeErr.svc != nil { + svc = *codeErr.svc + } + tags := []string{string(codeErr.Code()), svc.Name, svc.Language, svc.Type} + metricFailureEvents.Inc(tags...) + } +} From dcae0b8602384b11cb9819651f10786f0ff1fcd3 Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 06:17:51 +0000 Subject: [PATCH 12/54] Update the documentation to mention `deva` (#26047) * Update the documentation to mention deva * Fix anchors * Update docs/public/setup.md Co-authored-by: Ofek Lev * Update docs/dev/agent_dev_env.md * remove empty file * Update docs/dev/agent_dev_env.md Co-authored-by: Nicolas Schweitzer * comments --------- Co-authored-by: Ofek Lev Co-authored-by: Nicolas Schweitzer --- .gitignore | 4 + docs/dev/agent_build.md | 14 +-- docs/dev/agent_dev_env.md | 99 +++++++++++------- docs/dev/agent_omnibus.md | 8 +- docs/dev/checks/README.md | 8 +- docs/dev/checks/jmxfetch.md | 6 +- docs/dev/linters.md | 6 +- docs/public/assets/images/deva.gif | Bin 0 -> 291025 bytes docs/public/components/creating-components.md | 2 +- .../defining-bundles.md | 2 +- docs/public/guidelines/docs.md | 2 +- docs/public/setup.md | 41 +++++++- 12 files changed, 125 insertions(+), 67 deletions(-) create mode 100644 docs/public/assets/images/deva.gif diff --git a/.gitignore b/.gitignore index 9ea55fd2e5496..3f83af55d0fb9 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,10 @@ Dockerfiles/cluster-agent/nosys-seccomp # specific pre-commit hooks .pre-commit-config-*.yaml +# Utility tools +devagent +deva + # go-generated files datadog.yaml system-probe.yaml diff --git a/docs/dev/agent_build.md b/docs/dev/agent_build.md index d0ed6c0a3e72c..d4490f71e1c18 100644 --- a/docs/dev/agent_build.md +++ b/docs/dev/agent_build.md @@ -5,18 +5,18 @@ You can decide at build time which components of the Agent you want to find in the final artifact. By default, all the components are picked up, so if you want to replicate the same configuration of the Agent distributed via system packages, -all you have to do is `invoke agent.build`. +all you have to do is `deva agent.build`. To pick only certain components you have to invoke the task like this: ``` -invoke agent.build --build-include=zstd,etcd,python +deva agent.build --build-include=zstd,etcd,python ``` Conversely, if you want to exclude something: ``` -invoke agent.build --build-exclude=systemd,python +deva agent.build --build-exclude=systemd,python ``` This is the complete list of the available components: @@ -68,13 +68,13 @@ into the Agent binary. For instance, to override the defaults and bundle only th and the security Agents: ``` -invoke agent.build --bundle process-agent --bundle security-agent +deva agent.build --bundle process-agent --bundle security-agent ``` To disable bundling entirely: ``` -invoke agent.build --bundle agent +deva agent.build --bundle agent ``` One binary per Agent can still be built by using its own invoke task and passing the @@ -89,7 +89,7 @@ One binary per Agent can still be built by using its own invoke task and passing So to build the process Agent as a standalone self contained executable: ``` -invoke process-agent.build --no-bundle +deva process-agent.build --no-bundle ``` @@ -108,7 +108,7 @@ COPY agent /opt/datadog-agent/bin/agent/agent For this to work properly, two things are important: - Your change needs to be done on top of the `` tag from the DataDog repository. -- You need to run the invoke task with the proper embedded path `inv -e agent.build -e /opt/datadog-agent/embedded`. +- You need to run the invoke task with the proper embedded path `deva -e agent.build -e /opt/datadog-agent/embedded`. **Note**: This makes `invoke` install the build's artifacts in the `/opt/datadog-agent/embedded` folder. Make sure the folder exists and the current user has write permissions. diff --git a/docs/dev/agent_dev_env.md b/docs/dev/agent_dev_env.md index 70d922278b01d..933ab741a2feb 100644 --- a/docs/dev/agent_dev_env.md +++ b/docs/dev/agent_dev_env.md @@ -7,82 +7,105 @@ To build the agent on Windows, see [datadog-agent-buildimages](https://github.co ### Python -The Agent embeds a full-fledged CPython interpreter so it requires the +The Agent embeds a full-fledged CPython interpreter, so it requires the development files to be available in the dev env. The Agent can embed Python2 and/or Python3, you will need development files for all versions you want to support. -If you're on OSX/macOS, installing Python 2.7 and/or 3.9 with [Homebrew](https://brew.sh) +If you're on OSX/macOS, installing Python 2.7 and/or 3.11 with [Homebrew](https://brew.sh) brings along all the development files needed: **Please note that not using Python versions explicitly supported, you may have problems running the built Agent's Python checks, especially if using a virtualenv. -At this time, only Python 3.9 is confirmed to work as expected in the development +At this time, only Python 3.11 is confirmed to work as expected in the development environment.** ``` brew install python@2 -brew install python@3.9 +brew install python@3.11 ``` On Linux, depending on the distribution, you might need to explicitly install the development files, for example on Ubuntu: ``` sudo apt-get install python2.7-dev -sudo apt-get install python3.9-dev +sudo apt-get install python3.11-dev ``` -On Windows, install Python 2.7 and/or 3.9 via the [official installer](https://www.python.org/downloads/). +On Windows, install Python 2.7 and/or 3.11 via the [official installer](https://www.python.org/downloads/). #### Python Dependencies ##### Preface -To protect and isolate your system-wide python installation, a python virtual -environment is _highly_ recommended (though optional). It will help keep a -self-contained development environment and ensure a clean system Python. +[Invoke](http://www.pyinvoke.org) is a task runner written in Python that is extensively used in this project to orchestrate builds and test runs. To run the tasks, you need to have it installed on your machine. We offer two different ways to run our invoke tasks. + +##### `deva` (recommended) + +The `deva` CLI tool is a single binary that can be used to install and manage the development environment for the Agent, built by the Datadog team. It will install all the necessary Python dependencies for you. The development environment will be completely independent of your system Python installation. This tool leverages [PyApp](https://ofek.dev/pyapp/latest/), a wrapper for Python applications that bootstrap themselves at runtime. In our case, we wrap `invoke` itself and include the dependencies needed to work on the Agent. + +To install `deva`, you'll need to: + +1. Download the binary for your platform from the [releases page](https://github.com/DataDog/datadog-agent-devtools/releases/latest), +2. Make it executable (and optionally add it to your PATH), +3. Run the invoke command you need, using `deva` in place of `invoke` or `inv`. + +The Python environment will automatically be created on the first run. and will be reused for subsequent runs. For example: + +```shell +$ cd datadog-agent +$ curl -L -o deva https://github.com/DataDog/datadog-agent-devtools/releases/download/deva-v1.0.0/deva-aarch64-unknown-linux-gnu-1.0.0 +$ chmod +x deva +$ ./deva linter.go +``` + +Below a live demo of how the tool works: + +![deva_install](../public/assets/images/deva.gif) + +If you want to uninstall `deva`, you can simply run the `./deva self remove` command, which will remove the virtual environment from your system, and remove the binary. That's it. + +##### Manual Installation + +###### Virtual Environment + +To protect and isolate your system-wide python installation, a python virtual environment is _highly_ recommended (though optional). It will help keep a self-contained development environment and ensure a clean system Python. **Please note that due to the [way some virtual environments handle executable paths](https://bugs.python.org/issue22213) (e.g. `python -m venv`), not all virtual environment options will be able to run the built -agent correctly. At this time, the only confirmed virtual enviroment that is known for +agent correctly. At this time, the only confirmed virtual environment that is known for sure to work is `virtualenv`.** - Install the virtualenv module: -`python3 -m pip install virtualenv` + ``` + python3 -m pip install virtualenv + ``` - Create the virtual environment: -`virtualenv $GOPATH/src/github.com/DataDog/datadog-agent/venv` -- [Activate the virtualenv](https://virtualenv.pypa.io/en/latest/user_guide.html#activators) (OS-dependent). - This must be done for every new terminal before you start. - -If using virtual environments when running the built Agent, you may need to override the built -Agent's search path for Python check packages using the `PYTHONPATH` variable (your target path -must have the [pre-requisite core integration packages installed](https://datadoghq.dev/integrations-core/setup/) -though). + ``` + virtualenv $GOPATH/src/github.com/DataDog/datadog-agent/venv + ``` +- [Activate the virtualenv](https://virtualenv.pypa.io/en/latest/user_guide.html#activators) (OS-dependent). This must be done for every new terminal before you start. + +If using virtual environments when running the built Agent, you may need to override the built Agent's search path for Python check packages using the `PYTHONPATH` variable (your target path must have the [pre-requisite core integration packages installed](https://datadoghq.dev/integrations-core/setup/) though). + ```sh -PYTHONPATH="./venv/lib/python3.9/site-packages:$PYTHONPATH" ./agent run ... +PYTHONPATH="./venv/lib/python3.11/site-packages:$PYTHONPATH" ./agent run ... ``` -See also some notes in [./checks](./checks) about running custom python checks. +See also some notes in [./checks](https://github.com/DataDog/datadog-agent/tree/main/docs/dev/checks) about running custom python checks. -#### Invoke +###### Install Invoke and its dependencies -[Invoke](http://www.pyinvoke.org/) is a task runner written in Python -that is extensively used in this project to orchestrate builds and test -runs. Our invoke tasks are only compatible with Python 3, thus you will -need to use Python 3 to run them. +Our invoke tasks are only compatible with Python 3, thus you will need to use Python 3 to run them. -Though you may install invoke in a variety of way we suggest you use -the provided [requirements](https://github.com/DataDog/datadog-agent/blob/main/requirements.txt) -file and `pip`: +Though you may install invoke in a variety of way we suggest you use the provided [requirements](https://github.com/DataDog/datadog-agent/blob/main/requirements.txt) file and `pip`: ```bash -pip install -r requirements.txt +pip install -r tasks/requirements.txt ``` -This procedure ensures you not only get the correct version of `invoke`, but -also any additional python dependencies our development workflow may require, -at their expected versions. -It will also pull other handy development tools/deps (`reno`, or `docker`). +This procedure ensures you not only get the correct version of `invoke`, but also any additional python dependencies our development workflow may require, at their expected versions. It will also pull other handy development tools/deps (`reno`, or `docker`). + ### Golang @@ -97,7 +120,7 @@ binary properly.** ### Installing tooling -From the root of `datadog-agent`, run `invoke install-tools` to install go tooling. This uses `go` to install the necessary dependencies. +From the root of `datadog-agent`, run `deva install-tools` to install go tooling. This uses `go` to install the necessary dependencies. ### System or Embedded? @@ -170,7 +193,7 @@ dev environment. We use [Doxygen](http://www.doxygen.nl/) to generate the documentation for the `rtloader` part of the Agent. -To generate it (using the `invoke rtloader.generate-doc` command), you'll need to have Doxygen installed on your system and available in your `$PATH`. You can compile and install Doxygen from source with the instructions available [here](http://www.doxygen.nl/manual/install.html). +To generate it (using the `deva rtloader.generate-doc` command), you'll need to have Doxygen installed on your system and available in your `$PATH`. You can compile and install Doxygen from source with the instructions available [here](http://www.doxygen.nl/manual/install.html). Alternatively, you can use already-compiled Doxygen binaries from [here](http://www.doxygen.nl/download.html). To get the dependency graphs, you may also need to install the `dot` executable from [graphviz](http://www.graphviz.org/) and add it to your `$PATH`. @@ -192,7 +215,7 @@ The `shellcheck` pre-commit hook requires having the `shellcheck` binary install To install it, run: ```sh -inv install-shellcheck --destination +deva install-shellcheck --destination ``` (by default, the shellcheck binary is installed in `/usr/local/bin`). @@ -220,7 +243,7 @@ the dependencies needed to develop in this repository. To configure the vscode editor to use a container as remote development environment you need to: - Install the [devcontainer plugin](https://code.visualstudio.com/docs/remote/containers) and the [golang language plugin](https://code.visualstudio.com/docs/languages/go). -- Run the following invoke command `invoke vscode.setup-devcontainer --image ""`. +- Run the following invoke command `deva vscode.setup-devcontainer --image ""`. This command will create the devcontainer configuration file `./devcontainer/devcontainer.json`. - Start or restart your vscode editor. - A pop-up should show-up to propose to "reopen in container" your workspace. diff --git a/docs/dev/agent_omnibus.md b/docs/dev/agent_omnibus.md index 6a35462f447db..d5960c47f8b09 100644 --- a/docs/dev/agent_omnibus.md +++ b/docs/dev/agent_omnibus.md @@ -19,7 +19,7 @@ From the `datadog-agent` source folder, use the following command to run the `omnibus.build` task in a Docker container: ``` -docker run -v "$PWD:/go/src/github.com/DataDog/datadog-agent" -v "/tmp/omnibus:/omnibus" -v "/tmp/opt/datadog-agent:/opt/datadog-agent" -v"/tmp/gems:/gems" --workdir=/go/src/github.com/DataDog/datadog-agent datadog/agent-buildimages-deb_x64 inv -e omnibus.build --base-dir=/omnibus --gem-path=/gems +docker run -v "$PWD:/go/src/github.com/DataDog/datadog-agent" -v "/tmp/omnibus:/omnibus" -v "/tmp/opt/datadog-agent:/opt/datadog-agent" -v"/tmp/gems:/gems" --workdir=/go/src/github.com/DataDog/datadog-agent datadog/agent-buildimages-deb_x64 deva -e omnibus.build --base-dir=/omnibus --gem-path=/gems ``` The container will share 3 volumes with the host to avoid starting from scratch @@ -60,20 +60,20 @@ the filesystem without disrupting anything. To run Omnibus and build the package, make the `/opt` folder world readable and run: ``` -inv omnibus.build --base-dir=$HOME/.omnibus +deva omnibus.build --base-dir=$HOME/.omnibus ``` On Mac, you might want to skip the signing step by running: ``` -inv omnibus.build --base-dir=$HOME/.omnibus --skip-sign +deva omnibus.build --base-dir=$HOME/.omnibus --skip-sign ``` The path you pass with the `--base-dir` option will contain the sources downloaded by Omnibus in the `src` folder, the binaries cached after building those sources in the `cache` folder and the final deb/rpm/dmg artifacts in the `pkg` folder. You can fine tune an Omnibus run passing more options, see -`inv omnibus.build --help` for the list of all the available options. +`deva omnibus.build --help` for the list of all the available options. **Note:** it's strongly advised to pass `--base-dir` and point to a directory outside the Agent repo. By default Omnibus stores packages in the project folder diff --git a/docs/dev/checks/README.md b/docs/dev/checks/README.md index 7de492274f996..f5de45baf8bbc 100644 --- a/docs/dev/checks/README.md +++ b/docs/dev/checks/README.md @@ -146,7 +146,7 @@ class MyCheck(AgentCheck): 1. Place the configuration file `hello_world.yaml` in the `dev/dist/conf.d/` folder. 1. Place your Python code in the `dev/dist/` folder. -1. Run `inv agent.build` as usual. This step copies the contents +1. Run `deva agent.build` as usual. This step copies the contents of `dev/dist` into `bin/agent/dist`, which is where the Agent looks for your code. @@ -184,12 +184,12 @@ correctly. `python3` bins. 1. Activate the virtualenv (OS-dependent) 1. `python3 -m pip install '/path/to/integrations-core/datadog_checks_base[deps]'` -1. `PYTHONPATH="$PWD/venv/lib/python3.10/site-packages:$PYTHONPATH" inv agent.run` +1. `PYTHONPATH="$PWD/venv/lib/python3.10/site-packages:$PYTHONPATH" deva agent.run` #### Example for user install 1. `python3 -m pip install --user '/path/to//integrations-core/datadog_checks_base[deps]'` -1. `PYTHONPATH="$HOME/.local/lib/python3.10/site-packages:$PYTHONPATH" inv agent.run` +1. `PYTHONPATH="$HOME/.local/lib/python3.10/site-packages:$PYTHONPATH" deva agent.run` #### Getting the right `PYTHONPATH` You want the `site-packages` directory that the `datadog_checks_base` got @@ -266,7 +266,7 @@ depending on your configuration) from the `PATH` that is used to run the Agent. > in `pkg/collector/python/init.go`. ### "Could not initialize Python" -Out of the box, after an `inv agent.build`, you may see the following error on +Out of the box, after an `deva agent.build`, you may see the following error on Linux when trying to run the resulting `agent` binary: `Could not initialize Python: could not load runtime python for version 3: Unable to open three library: libdatadog-agent-three.so: cannot open shared object file: No such file or directory` diff --git a/docs/dev/checks/jmxfetch.md b/docs/dev/checks/jmxfetch.md index 674737bd891b1..34e6142fee9af 100644 --- a/docs/dev/checks/jmxfetch.md +++ b/docs/dev/checks/jmxfetch.md @@ -12,7 +12,7 @@ version of JMXFetch, follow the instructions below: 1. Download the `-jar-with-dependencies.jar` build of the latest version of JMXFetch from [`maven`](https://repo1.maven.org/maven2/com/datadoghq/jmxfetch/) 2. Copy the jar file and rename it to `$GOPATH/src/github.com/DataDog/datadog-agent/dev/dist/jmx/jmxfetch.jar`. -3. Run `inv agent.run`. +3. Run `deva agent.run`. 4. Validate that the JMXFetch section appears in `agent status`. If you have a JMX-based integration configured to run, it automatically @@ -22,7 +22,7 @@ runs in your local JMXFetch instance. ## Custom Build of JMXFetch 1. [Build JMXFetch](https://github.com/DataDog/jmxfetch/#building-from-source). 2. Copy the resulting jar into `$GOPATH/src/github.com/DataDog/datadog-agent/dev/dist/jmx/jmxfetch.jar`. -3. Run `inv agent.run`. +3. Run `deva agent.run`. 4. Validate that the JMXFetch section appears in `agent status`. If you have a JMX-based integration configured to run, it should automatically @@ -36,5 +36,5 @@ be run in your local JMXFetch instance. - An example for the above test server can be found [in the jmxfetch repo](https://github.com/DataDog/jmxfetch/blob/master/tools/misbehaving-jmx-server/misbehaving-jmxfetch-conf.yaml). - This config should live in `dev/dist/conf.d/jmx-test-server.d/conf.yaml`. -3. Run `inv agent.run`. +3. Run `deva agent.run`. 4. Validate that the check appears as scheduled in `agent status`. diff --git a/docs/dev/linters.md b/docs/dev/linters.md index f48734e8f53ed..ee1d44b488942 100644 --- a/docs/dev/linters.md +++ b/docs/dev/linters.md @@ -13,7 +13,7 @@ For Go, we're using [golangci-lint](https://golangci-lint.run/), a Go linters ag The `linters` key defines the list of linters we're using: https://github.com/DataDog/datadog-agent/blob/dffd3262934a5540b9bf8e4bd3a743732637ef37/.golangci.yml#L65-L79 -To run the linters locally, run `inv linter.go`. +To run the linters locally, run `deva linter.go`. > [!TIP] > In your code, you can ignore linter issues on a line by prepending it with [the nolint directive](https://golangci-lint.run/usage/false-positives/#nolint-directive), for example, `//nolint:linter_name`. @@ -28,7 +28,7 @@ For Python, we're using ([see invoke task](https://github.com/DataDog/datadog-ag Their configuration is defined in both the [setup.cfg](https://github.com/DataDog/datadog-agent/blob/dffd3262934a5540b9bf8e4bd3a743732637ef37/setup.cfg) and the [pyproject.toml](https://github.com/DataDog/datadog-agent/blob/dffd3262934a5540b9bf8e4bd3a743732637ef37/pyproject.toml) files. -To run the linters locally, run `inv linter.python`. +To run the linters locally, run `deva linter.python`. > [!TIP] > In your code, you can ignore linter issues on a line by prepending it with `# noqa: error_code`. @@ -45,7 +45,7 @@ A: This could have several causes: - `go version` should output the same as [the repository .go-version](https://github.com/DataDog/datadog-agent/blob/dffd3262934a5540b9bf8e4bd3a743732637ef37/.go-version). - `golangci-lint --version` should output the same as [the repository internal/tools/go.mod](https://github.com/DataDog/datadog-agent/blob/dffd3262934a5540b9bf8e4bd3a743732637ef37/internal/tools/go.mod/#L8). - You're testing OS specific code; running locally, the linters only get the results for your local OS. -- You didn't run `inv tidy-all` in the repository, making some dependencies in the remote env outdated compared to your local env. +- You didn't run `deva tidy` in the repository, making some dependencies in the remote env outdated compared to your local env. ### About the new-from-rev golangci-lint parameter diff --git a/docs/public/assets/images/deva.gif b/docs/public/assets/images/deva.gif new file mode 100644 index 0000000000000000000000000000000000000000..cfa6f393c61c596153bf6834cbf4bc0eb6e11719 GIT binary patch literal 291025 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%0DHd>w}IT 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 literal 0 HcmV?d00001 diff --git a/docs/public/components/creating-components.md b/docs/public/components/creating-components.md index a3552cd34def9..fc4af2e358ccc 100644 --- a/docs/public/components/creating-components.md +++ b/docs/public/components/creating-components.md @@ -53,7 +53,7 @@ This file hierarchy aims to solve a few problems: ## Bootstrapping components -You can use the [invoke](../setup.md#invoke) task `inv components.new-component comp/` to generate a scaffold for your new component. +You can use the [invoke](../setup.md#preface) task `deva components.new-component comp/` to generate a scaffold for your new component. Every public variable, function, struct, and interface of your component **must** be documented. Refer to the [Documentation](#documentation) section below for details. diff --git a/docs/public/guidelines/deprecated-components-documentation/defining-bundles.md b/docs/public/guidelines/deprecated-components-documentation/defining-bundles.md index f604e71de0b50..c272bbce5fa83 100644 --- a/docs/public/guidelines/deprecated-components-documentation/defining-bundles.md +++ b/docs/public/guidelines/deprecated-components-documentation/defining-bundles.md @@ -13,7 +13,7 @@ A bundle is defined in a dedicated package named `comp/`. The packag Typically, a bundle will automatically instantiate the top-level components that represent the bundle's purpose. For example, the trace-agent bundle `comp/trace` might automatically instantiate `comp/trace/agent`. -You can use the invoke task `inv components.new-bundle comp/` to generate a pre-filled `bundle.go` file for the given bundle. +You can use the invoke task `deva components.new-bundle comp/` to generate a pre-filled `bundle.go` file for the given bundle. ## Bundle Parameters diff --git a/docs/public/guidelines/docs.md b/docs/public/guidelines/docs.md index 67d9e0115bb30..f8efe306282a1 100644 --- a/docs/public/guidelines/docs.md +++ b/docs/public/guidelines/docs.md @@ -2,7 +2,7 @@ This site is built by [MkDocs](https://github.com/mkdocs/mkdocs) and uses the [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/) theme. -You can serve documentation locally with the `docs.serve` [invoke task](../setup.md#invoke). +You can serve documentation locally with the `docs.serve` [invoke task](../setup.md#preface). ## Organization diff --git a/docs/public/setup.md b/docs/public/setup.md index b874aa8e2b1ec..957c2ef7c14a0 100644 --- a/docs/public/setup.md +++ b/docs/public/setup.md @@ -33,10 +33,41 @@ On Windows, install Python 2.7 and/or 3.11 via the [official installer](https:// ##### Preface +[Invoke](http://www.pyinvoke.org) is a task runner written in Python that is extensively used in this project to orchestrate builds and test runs. To run the tasks, you need to have it installed on your machine. We offer two different ways to run our invoke tasks. + +##### `deva` (recommended) + +The `deva` CLI tool is a single binary that can be used to install and manage the development environment for the Agent, built by the Datadog team. It will install all the necessary Python dependencies for you. The development environment will be completely independent of your system Python installation. This tool leverages [PyApp](https://ofek.dev/pyapp/latest/), a wrapper for Python applications that bootstrap themselves at runtime. In our case, we wrap `invoke` itself and include the dependencies needed to work on the Agent. + +To install `deva`, you'll need to: + +1. Download the binary for your platform from the [releases page](https://github.com/DataDog/datadog-agent-devtools/releases/latest), +2. Make it executable (and optionally add it to your PATH), +3. Run the invoke command you need, using `deva` in place of `invoke` or `inv`. + +The Python environment will automatically be created on the first run. and will be reused for subsequent runs. For example: + +```shell +$ cd datadog-agent +$ curl -L -o deva https://github.com/DataDog/datadog-agent-devtools/releases/download/deva-v1.0.0/deva-aarch64-unknown-linux-gnu-1.0.0 +$ chmod +x deva +$ ./deva linter.go +``` + +Below a live demo of how the tool works: + +![deva_install](./assets/images/deva.gif) + +If you want to uninstall `deva`, you can simply run the `./deva self remove` command, which will remove the virtual environment from your system, and remove the binary. That's it. + +##### Manual Installation + +###### Virtual Environment + To protect and isolate your system-wide python installation, a python virtual environment is _highly_ recommended (though optional). It will help keep a self-contained development environment and ensure a clean system Python. !!! note - Due to the [way some virtual environments handle executable paths](https://bugs.python.org/issue22213) (e.g. `python -m venv`), not all virtual environment options will be able to run the built Agent correctly. At this time, the only confirmed virtual enviroment creator that is known for sure to work is `virtualenv`. + Due to the [way some virtual environments handle executable paths](https://bugs.python.org/issue22213) (e.g. `python -m venv`), not all virtual environment options will be able to run the built Agent correctly. At this time, the only confirmed virtual environment creator that is known for sure to work is `virtualenv`. - Install the virtualenv module: ``` @@ -56,9 +87,9 @@ PYTHONPATH="./venv/lib/python3.11/site-packages:$PYTHONPATH" ./agent run ... See also some notes in [./checks](https://github.com/DataDog/datadog-agent/tree/main/docs/dev/checks) about running custom python checks. -#### Invoke +###### Install Invoke and its dependencies -[Invoke](http://www.pyinvoke.org) is a task runner written in Python that is extensively used in this project to orchestrate builds and test runs. Our invoke tasks are only compatible with Python 3, thus you will need to use Python 3 to run them. +Our invoke tasks are only compatible with Python 3, thus you will need to use Python 3 to run them. Though you may install invoke in a variety of way we suggest you use the provided [requirements](https://github.com/DataDog/datadog-agent/blob/main/requirements.txt) file and `pip`: @@ -141,7 +172,7 @@ pre-commit install The `shellcheck` pre-commit hook requires having the `shellcheck` binary installed and in your `$PATH`. To install it, run: ```sh -inv install-shellcheck --destination +deva install-shellcheck --destination ``` (by default, the shellcheck binary is installed in `/usr/local/bin`). @@ -169,7 +200,7 @@ See `pre-commit run --help` for further options. To configure the vscode editor to use a container as remote development environment you need to: - Install the [devcontainer plugin](https://code.visualstudio.com/docs/remote/containers) and the [golang language plugin](https://code.visualstudio.com/docs/languages/go). -- Run the following invoke command `invoke vscode.setup-devcontainer --image ""`. This command will create the devcontainer configuration file `./devcontainer/devcontainer.json`. +- Run the following invoke command `deva vscode.setup-devcontainer --image ""`. This command will create the devcontainer configuration file `./devcontainer/devcontainer.json`. - Start or restart your vscode editor. - A pop-up should show-up to propose to "reopen in container" your workspace. - The first start, it might propose you to install the golang plugin dependencies/tooling. From 499f29fcc146e34da044468eddcb0c401f842884 Mon Sep 17 00:00:00 2001 From: Derek Brown Date: Thu, 30 May 2024 00:08:25 -0700 Subject: [PATCH 13/54] [windows][cws] Fix cleaning up discarders (#26112) This change fixes the issue where we're never removing the file handle from the discarded handles cache. So, once a file handle is discarded, it stays discarded even if that handle is closed, and then is reused on subsequent file open. Also, since we could miss the close operation, if we see a new handle, remove it from the discarded cache. --- pkg/security/probe/probe_kernel_file_windows.go | 3 +++ pkg/security/probe/probe_windows.go | 1 + 2 files changed, 4 insertions(+) diff --git a/pkg/security/probe/probe_kernel_file_windows.go b/pkg/security/probe/probe_kernel_file_windows.go index b3335a45ac3e3..ee34f239944e9 100644 --- a/pkg/security/probe/probe_kernel_file_windows.go +++ b/pkg/security/probe/probe_kernel_file_windows.go @@ -199,6 +199,9 @@ func (wp *WindowsProbe) parseCreateHandleArgs(e *etw.DDEventRecord) (*createHand if wp.filePathResolver.Add(ca.fileObject, fc) { wp.stats.fileNameCacheEvictions++ } + // if we get here, we have a new file handle. Remove it from the discarder cache in case + // we missed the close notification + wp.discardedFileHandles.Remove(fileObjectPointer(ca.fileObject)) return ca, nil } diff --git a/pkg/security/probe/probe_windows.go b/pkg/security/probe/probe_windows.go index bb01030515fa4..1c432f62829f3 100644 --- a/pkg/security/probe/probe_windows.go +++ b/pkg/security/probe/probe_windows.go @@ -403,6 +403,7 @@ func (p *WindowsProbe) setupEtw(ecb etwCallback) error { p.stats.fileProcessedNotifications[e.EventHeader.EventDescriptor.ID]++ ecb(ca, e.EventHeader.ProcessID) // lru is thread safe, has its own locking + p.discardedFileHandles.Remove(ca.fileObject) p.filePathResolver.Remove(ca.fileObject) } case idFlush: From 735f310c3d832e5f52d6e7987f58e248f5ab9cfa Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Thu, 30 May 2024 09:15:31 +0200 Subject: [PATCH 14/54] [CWS] dynamic ETW reconfiguration of providers (#26091) Co-authored-by: derekwbrown --- pkg/security/probe/probe_windows.go | 37 +++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/pkg/security/probe/probe_windows.go b/pkg/security/probe/probe_windows.go index 1c432f62829f3..8560b64f312e2 100644 --- a/pkg/security/probe/probe_windows.go +++ b/pkg/security/probe/probe_windows.go @@ -226,6 +226,14 @@ func (p *WindowsProbe) initEtwFIM() error { return err } + return p.reconfigureProvider() +} + +func (p *WindowsProbe) reconfigureProvider() error { + if !p.config.RuntimeSecurity.FIMEnabled { + return nil + } + pidsList := make([]uint32, 0) p.fimSession.ConfigureProvider(p.fileguid, func(cfg *etw.ProviderConfiguration) { @@ -255,16 +263,21 @@ func (p *WindowsProbe) initEtwFIM() error { idCreateNewFile, idCleanup, idClose, - idWrite, - idSetDelete, - idDeletePath, - idRename, - idRenamePath, - idRename29, + } + + if p.isWriteEnabled { + fileIds = append(fileIds, idWrite) + } + if p.isRenameEnabled { + fileIds = append(fileIds, idRename, idRenamePath, idRename29) + } + if p.isDeleteEnabled { + fileIds = append(fileIds, idSetDelete, idDeletePath) } cfg.EnabledIDs = fileIds }) + p.fimSession.ConfigureProvider(p.regguid, func(cfg *etw.ProviderConfiguration) { cfg.TraceLevel = etw.TRACE_LEVEL_VERBOSE cfg.PIDs = pidsList @@ -294,16 +307,16 @@ func (p *WindowsProbe) initEtwFIM() error { cfg.MatchAnyKeyword = 0xF7E3 }) - err = p.fimSession.EnableProvider(p.fileguid) - if err != nil { + if err := p.fimSession.EnableProvider(p.fileguid); err != nil { log.Warnf("Error enabling provider %v", err) return err } - err = p.fimSession.EnableProvider(p.regguid) - if err != nil { + + if err := p.fimSession.EnableProvider(p.regguid); err != nil { log.Warnf("Error enabling provider %v", err) return err } + return nil } @@ -1055,6 +1068,10 @@ func (p *WindowsProbe) ApplyRuleSet(rs *rules.RuleSet) (*kfilters.ApplyRuleSetRe } } + if err := p.reconfigureProvider(); err != nil { + return nil, err + } + return ars, nil } From d0aace43ba6a51c664c9bf87436ec75bdf9a8a5b Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 07:15:36 +0000 Subject: [PATCH 15/54] Optimize the tidy task (#26061) * Optimize the tidy task * comment --- .pre-commit-config.yaml | 5 +++-- tasks/go.py | 19 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9feb2c3fa503c..d6b6872b0b96f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -66,12 +66,13 @@ repos: exclude: '^pkg/ebpf/(c/bpf_endian|c/bpf_helpers|compiler/clang-stdarg).h$' - id: go-mod-tidy name: go-mod-tidy - description: check that go.mod files are tidy - entry: 'inv tidy --only-modified-packages' + description: check that all go.mod files are tidy + entry: 'inv tidy' language: system require_serial: true files: (\.go|^go\.mod|^go\.sum)$ pass_filenames: false + stages: [pre-push] - id: go-test name: go-test description: run go test on modified packages diff --git a/tasks/go.py b/tasks/go.py index 17fe7df9b2a42..4b1434f7c24c1 100644 --- a/tasks/go.py +++ b/tasks/go.py @@ -410,17 +410,16 @@ def tidy_all(ctx): @task -def tidy(ctx, only_modified_packages=False): - # TODO: if only_modified_packages then use `go mod tidy -diff` with golang 1.23, probably in a separate check-mod-tidy task - # https://github.com/golang/go/issues/27005 - from tasks import get_modified_packages - - # TODO: Also include packages that import them - modules = get_modified_packages(ctx) if only_modified_packages else DEFAULT_MODULES.values() - - for mod in modules: +def tidy(ctx): + # Note: It's currently faster to tidy everything than looking for exactly what we should tidy + promises = [] + for mod in DEFAULT_MODULES.values(): with ctx.cd(mod.full_path()): - ctx.run("go mod tidy") + # https://docs.pyinvoke.org/en/stable/api/runners.html#invoke.runners.Runner.run + promises.append(ctx.run("go mod tidy", asynchronous=True)) + + for promise in promises: + promise.join() @task From 8eb9fc097a94cd5d4861e311a1547ef155e20a52 Mon Sep 17 00:00:00 2001 From: pducolin <45568537+pducolin@users.noreply.github.com> Date: Thu, 30 May 2024 09:54:13 +0200 Subject: [PATCH 16/54] [gitlab] add links to CI Visibility from e2e jobs (#26098) --- .gitlab/e2e/e2e.yml | 7 +++++++ .gitlab/e2e/external_links.json.template | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .gitlab/e2e/external_links.json.template diff --git a/.gitlab/e2e/e2e.yml b/.gitlab/e2e/e2e.yml index 2dcee6794fecb..2d43b18b7f964 100644 --- a/.gitlab/e2e/e2e.yml +++ b/.gitlab/e2e/e2e.yml @@ -111,6 +111,10 @@ k8s-e2e-otlp-main: - touch $E2E_PRIVATE_KEY_PATH && chmod 600 $E2E_PRIVATE_KEY_PATH && $CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $SSH_KEY_RSA_SSM_NAME > $E2E_PRIVATE_KEY_PATH # Use S3 backend - pulumi login "s3://dd-pulumi-state?region=us-east-1&awssdk=v2&profile=$AWS_PROFILE" + # Generate external links to CI VISIBILITY, used by artifacts:reports:annotations + - cp .gitlab/e2e/external_links.json.template external_links_$CI_JOB_ID.json + - sed -i "s|{{CI_JOB_ID}}|$CI_JOB_ID|g" external_links_$CI_JOB_ID.json + - sed -i "s|{{CI_JOB_NAME}}|$CI_JOB_NAME|g" external_links_$CI_JOB_ID.json variables: KUBERNETES_MEMORY_REQUEST: 12Gi KUBERNETES_MEMORY_LIMIT: 16Gi @@ -133,6 +137,9 @@ k8s-e2e-otlp-main: - $E2E_OUTPUT_DIR # junit tarball, kept for investigations - junit-*.tgz + reports: + annotations: + - external_links_$CI_JOB_ID.json .new_e2e_template_needs_deb_x64: extends: .new_e2e_template diff --git a/.gitlab/e2e/external_links.json.template b/.gitlab/e2e/external_links.json.template new file mode 100644 index 0000000000000..ecc632679767c --- /dev/null +++ b/.gitlab/e2e/external_links.json.template @@ -0,0 +1,16 @@ +{ + "CI Visibility": [ + { + "external_link": { + "label": "🔗 CI Visibility: This job instance", + "url": "https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.job.id%3A{{CI_JOB_ID}}&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=cipipeline" + } + }, + { + "external_link": { + "label": "🔗 CI Visibility: This job on main", + "url": "https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.job.name%3A%22{{CI_JOB_NAME}}%22%20%40git.branch%3Amain" + } + } + ] +} From 02c9011e8dad4ba85c8f91eadc1c544eae47eaeb Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 08:30:23 +0000 Subject: [PATCH 17/54] Migrate the `copyrights` hook to `invoke` (#26083) * Migrate the copyrights hooks to invoke * only staged files --- .pre-commit-config.yaml | 3 ++- tasks/git-hooks/copyright.py | 13 ------------- tasks/gotest.py | 9 +-------- tasks/libs/common/git.py | 10 ++++++++++ tasks/linter.py | 16 +++++++++++++--- tasks/unit-tests/libs/common/git_tests.py | 17 +++++++++++++++++ 6 files changed, 43 insertions(+), 25 deletions(-) delete mode 100644 tasks/git-hooks/copyright.py create mode 100644 tasks/libs/common/git.py create mode 100644 tasks/unit-tests/libs/common/git_tests.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6b6872b0b96f..eeb5496647fb7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,10 +46,11 @@ repos: - id: copyright name: copyright description: copyright headers - entry: 'python3 -m tasks.git-hooks.copyright' + entry: 'inv linter.copyrights --only-staged-files' language: system require_serial: true files: \.go$ + pass_filenames: false - id: win-clang-format name: win-clang-format description: clang-format diff --git a/tasks/git-hooks/copyright.py b/tasks/git-hooks/copyright.py deleted file mode 100644 index d43e759067d10..0000000000000 --- a/tasks/git-hooks/copyright.py +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env python3 - -import sys - -from tasks.libs.types.copyright import CopyrightLinter, LintFailure # noqa: E402 - -# Exclude non go files -files = [path for path in sys.argv[1:] if path.endswith(".go")] -try: - CopyrightLinter().assert_compliance(files=files) -except LintFailure: - # the linter prints useful messages on its own, so no need to print the exception - sys.exit(1) diff --git a/tasks/gotest.py b/tasks/gotest.py index 1db4175e04c91..1977d37d15819 100644 --- a/tasks/gotest.py +++ b/tasks/gotest.py @@ -28,6 +28,7 @@ from tasks.flavor import AgentFlavor from tasks.libs.common.color import color_message from tasks.libs.common.datadog_api import create_count, send_metrics +from tasks.libs.common.git import get_modified_files from tasks.libs.common.junit_upload_core import enrich_junitxml, produce_junit_tar from tasks.libs.common.utils import clean_nested_paths, collapsed_section, get_build_flags, get_distro from tasks.modules import DEFAULT_MODULES, GoModule @@ -609,14 +610,6 @@ def get_modified_packages(ctx, build_tags=None, lint=False) -> list[GoModule]: return modules_to_test.values() -def get_modified_files(ctx): - last_main_commit = ctx.run("git merge-base HEAD origin/main", hide=True).stdout - print(f"Checking diff from {last_main_commit} commit on main branch") - - modified_files = ctx.run(f"git diff --name-only --no-renames {last_main_commit}", hide=True).stdout.splitlines() - return modified_files - - @task(iterable=["extra_tag"]) def send_unit_tests_stats(_, job_name, extra_tag=None): if extra_tag is None: diff --git a/tasks/libs/common/git.py b/tasks/libs/common/git.py new file mode 100644 index 0000000000000..6aef897289db2 --- /dev/null +++ b/tasks/libs/common/git.py @@ -0,0 +1,10 @@ +def get_staged_files(ctx, commit="HEAD") -> list[str]: + """ + Get the list of staged (to be committed) files in the repository compared to the `commit` commit. + """ + return ctx.run(f"git diff --name-only --staged {commit}", hide=True).stdout.strip().splitlines() + + +def get_modified_files(ctx) -> list[str]: + last_main_commit = ctx.run("git merge-base HEAD origin/main", hide=True).stdout + return ctx.run(f"git diff --name-only --no-renames {last_main_commit}", hide=True).stdout.splitlines() diff --git a/tasks/linter.py b/tasks/linter.py index 5413d3a7394d0..24e58e068ec5c 100644 --- a/tasks/linter.py +++ b/tasks/linter.py @@ -21,8 +21,9 @@ read_includes, ) from tasks.libs.common.check_tools_version import check_tools_version +from tasks.libs.common.git import get_staged_files from tasks.libs.common.utils import DEFAULT_BRANCH, GITHUB_REPO_NAME, color_message, is_pr_context, running_in_ci -from tasks.libs.types.copyright import CopyrightLinter +from tasks.libs.types.copyright import CopyrightLinter, LintFailure from tasks.modules import GoModule from tasks.test_core import ModuleLintResult, process_input_args, process_module_results, test_core from tasks.update_go import _update_go_mods, _update_references @@ -54,14 +55,23 @@ def python(ctx): @task -def copyrights(_, fix=False, dry_run=False, debug=False): +def copyrights(ctx, fix=False, dry_run=False, debug=False, only_staged_files=False): """ Checks that all Go files contain the appropriate copyright header. If '--fix' is provided as an option, it will try to fix problems as it finds them. If '--dry_run' is provided when fixing, no changes to the files will be applied. """ + files = None - CopyrightLinter(debug=debug).assert_compliance(fix=fix, dry_run=dry_run) + if only_staged_files: + staged_files = get_staged_files(ctx) + files = [path for path in staged_files if path.endswith(".go")] + + try: + CopyrightLinter(debug=debug).assert_compliance(fix=fix, dry_run=dry_run, files=files) + except LintFailure: + # the linter prints useful messages on its own, so no need to print the exception + sys.exit(1) @task diff --git a/tasks/unit-tests/libs/common/git_tests.py b/tasks/unit-tests/libs/common/git_tests.py new file mode 100644 index 0000000000000..951b472238ca4 --- /dev/null +++ b/tasks/unit-tests/libs/common/git_tests.py @@ -0,0 +1,17 @@ +import unittest +from unittest.mock import MagicMock + +from tasks.libs.common.git import get_staged_files + + +class TestGit(unittest.TestCase): + def setUp(self) -> None: + super().setUp() + self.ctx_mock = MagicMock() + + def test_get_staged_files(self): + self.ctx_mock.run.return_value.stdout = "file1\nfile2\nfile3" + files = get_staged_files(self.ctx_mock) + + self.assertEqual(files, ["file1", "file2", "file3"]) + self.ctx_mock.run.assert_called_once_with("git diff --name-only --staged HEAD", hide=True) From a4d03956c6d1f71344c7c18758c9bb9c2e2c09e8 Mon Sep 17 00:00:00 2001 From: Wassim Dhif Date: Thu, 30 May 2024 10:46:36 +0200 Subject: [PATCH 18/54] feat(health): implement startup healthcheck (#25973) * feat(health): implement startup healthcheck Signed-off-by: Wassim DHIF * fix(health): add test to validate that we stay healthy Signed-off-by: Wassim DHIF --------- Signed-off-by: Wassim DHIF --- comp/core/healthprobe/impl/healthprobe.go | 15 +++++++++++++ pkg/status/health/README.md | 6 +++++- pkg/status/health/global.go | 25 +++++++++++++++++++++- pkg/status/health/health.go | 6 ++++++ pkg/status/health/health_test.go | 26 +++++++++++++++++++++++ 5 files changed, 76 insertions(+), 2 deletions(-) diff --git a/comp/core/healthprobe/impl/healthprobe.go b/comp/core/healthprobe/impl/healthprobe.go index ccd6c1e95ee4a..c2a052156c190 100644 --- a/comp/core/healthprobe/impl/healthprobe.go +++ b/comp/core/healthprobe/impl/healthprobe.go @@ -111,6 +111,15 @@ func (rh readyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { healthHandler(rh.logsGoroutines, rh.log, health.GetReadyNonBlocking, w, r) } +type startupHandler struct { + logsGoroutines bool + log log.Component +} + +func (sh startupHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + healthHandler(sh.logsGoroutines, sh.log, health.GetStartupNonBlocking, w, r) +} + func buildServer(options healthprobeComponent.Options, log log.Component) *http.Server { r := mux.NewRouter() @@ -124,8 +133,14 @@ func buildServer(options healthprobeComponent.Options, log log.Component) *http. log: log, } + startupHandler := startupHandler{ + logsGoroutines: options.LogsGoroutines, + log: log, + } + r.Handle("/live", liveHandler) r.Handle("/ready", readyHandler) + r.Handle("/startup", startupHandler) // Default route for backward compatibility r.NewRoute().Handler(liveHandler) diff --git a/pkg/status/health/README.md b/pkg/status/health/README.md index 245cc52b00255..05bf075e946c2 100644 --- a/pkg/status/health/README.md +++ b/pkg/status/health/README.md @@ -11,6 +11,10 @@ For more information on the context, see the `agent-healthcheck.md` proposal. receive a `*health.Handle` to keep. As soon as `Register` is called, you need to start reading the channel to be considered healthy. +- If you want to register a component for the `Startup` probe, you need to call `RegisterStartup()`. +This is useful for components that need to perform some initialization before being considered healthy. +You will need to first register the component, then do the initialization, and finally read from the channel. + - In your main goroutine, you need to read from the `handle.C` channel, at least every 15 seconds. This is accomplished by using a `select` statement in your main goroutine. If the channel is full (after two tries), your component will be considered unhealthy, which might result in the agent @@ -25,6 +29,6 @@ It depends on your component lifecycle, but the check's purpose is to check that is able to process new input and act accordingly. For components that read input from a channel, you should read the channel from this logic. -This is usually hightly unprobable, but it's exactly the scope of this system: be able to +This is usually highly unlikely, but it's exactly the scope of this system: be able to detect if a component is frozen because of a bug / race condition. This is usually the only kind of issue that could be solved by the agent restarting. diff --git a/pkg/status/health/global.go b/pkg/status/health/global.go index e6ce74c5b8555..85586d504853a 100644 --- a/pkg/status/health/global.go +++ b/pkg/status/health/global.go @@ -12,6 +12,7 @@ import ( var readinessAndLivenessCatalog = newCatalog() var readinessOnlyCatalog = newCatalog() +var startupOnlyCatalog = newCatalog() // RegisterReadiness registers a component for readiness check with the default 30 seconds timeout, returns a token func RegisterReadiness(name string) *Handle { @@ -23,12 +24,24 @@ func RegisterLiveness(name string) *Handle { return readinessAndLivenessCatalog.register(name) } +// RegisterStartup registers a component for startup check, returns a token +func RegisterStartup(name string) *Handle { + startupOnlyCatalog.startup = true + return startupOnlyCatalog.register(name) +} + // Deregister a component from the healthcheck func Deregister(handle *Handle) error { if readinessAndLivenessCatalog.deregister(handle) == nil { return nil } - return readinessOnlyCatalog.deregister(handle) + if readinessOnlyCatalog.deregister(handle) == nil { + return nil + } + if startupOnlyCatalog.deregister(handle) == nil { + return nil + } + return errors.New("component not registered") } // GetLive returns health of all components registered for liveness @@ -45,6 +58,11 @@ func GetReady() (ret Status) { return } +// GetStartup returns health of all components registered for startup +func GetStartup() Status { + return startupOnlyCatalog.getStatus() +} + // getStatusNonBlocking allows to query the health status of the agent // and is guaranteed to return under 500ms. func getStatusNonBlocking(getStatus func() Status) (Status, error) { @@ -72,3 +90,8 @@ func GetLiveNonBlocking() (Status, error) { func GetReadyNonBlocking() (Status, error) { return getStatusNonBlocking(GetReady) } + +// GetStartupNonBlocking returns the health of all components registered for startup with a 500ms timeout +func GetStartupNonBlocking() (Status, error) { + return getStatusNonBlocking(GetStartup) +} diff --git a/pkg/status/health/health.go b/pkg/status/health/health.go index 4540034547f9a..712812aa83890 100644 --- a/pkg/status/health/health.go +++ b/pkg/status/health/health.go @@ -35,12 +35,14 @@ type catalog struct { sync.RWMutex components map[*Handle]*component latestRun time.Time + startup bool } func newCatalog() *catalog { return &catalog{ components: make(map[*Handle]*component), latestRun: time.Now(), // Start healthy + startup: false, } } @@ -97,6 +99,10 @@ func (c *catalog) pingComponents(healthDeadline time.Time) bool { c.Lock() defer c.Unlock() for _, component := range c.components { + // In startup mode, we skip already healthy components. + if c.startup && component.healthy { + continue + } select { case component.healthChan <- healthDeadline: component.healthy = true diff --git a/pkg/status/health/health_test.go b/pkg/status/health/health_test.go index 52854bd11ebf8..f08c2f1bf3957 100644 --- a/pkg/status/health/health_test.go +++ b/pkg/status/health/health_test.go @@ -123,3 +123,29 @@ func TestGetHealthy(t *testing.T) { assert.Len(t, status.Healthy, 2) assert.Len(t, status.Unhealthy, 0) } + +func TestStartupCatalog(t *testing.T) { + cat := newCatalog() + cat.startup = true + token := cat.register("test1") + + // Start unhealthy + status := cat.getStatus() + assert.Len(t, status.Healthy, 1) + assert.Contains(t, status.Healthy, "healthcheck") + assert.Len(t, status.Unhealthy, 1) + assert.Contains(t, status.Unhealthy, "test1") + + // Get healthy + <-token.C + cat.pingComponents(time.Time{}) // First ping will make component healthy and fill the channel again. + status = cat.getStatus() + assert.Len(t, status.Healthy, 2) + assert.Contains(t, status.Healthy, "test1") + + // Make sure that we stay health even if we don't ping. + cat.pingComponents(time.Time{}) + status = cat.getStatus() + assert.Len(t, status.Healthy, 2) + assert.Contains(t, status.Healthy, "test1") +} From c560274680e0b1d9b3d7740422eba4dbb0e94de9 Mon Sep 17 00:00:00 2001 From: maxime mouial Date: Thu, 30 May 2024 11:18:19 +0200 Subject: [PATCH 19/54] Add endpoint to the system-probe to get its config by sources (#26056) --- cmd/system-probe/api/config.go | 1 + .../subcommands/config/command.go | 25 ++++++++ .../subcommands/config/command_test.go | 8 +++ comp/core/settings/component.go | 3 + .../settings/settingsimpl/settings_mock.go | 5 ++ .../settings/settingsimpl/settingsimpl.go | 25 ++++++++ .../settingsimpl/settingsimpl_test.go | 17 +++++ pkg/config/settings/api.go | 1 + pkg/config/settings/http/client.go | 63 +++++++++---------- 9 files changed, 116 insertions(+), 32 deletions(-) diff --git a/cmd/system-probe/api/config.go b/cmd/system-probe/api/config.go index 178730ae808e2..646ae49fafa8c 100644 --- a/cmd/system-probe/api/config.go +++ b/cmd/system-probe/api/config.go @@ -16,6 +16,7 @@ import ( // setupConfigHandlers adds the specific handlers for /config endpoints func setupConfigHandlers(r *mux.Router, settings settings.Component) { r.HandleFunc("/config", settings.GetFullConfig(getAggregatedNamespaces()...)).Methods("GET") + r.HandleFunc("/config/by-source", settings.GetFullConfigBySource()).Methods("GET") r.HandleFunc("/config/list-runtime", settings.ListConfigurable).Methods("GET") r.HandleFunc("/config/{setting}", settings.GetValue).Methods("GET") r.HandleFunc("/config/{setting}", settings.SetValue).Methods("POST") diff --git a/cmd/system-probe/subcommands/config/command.go b/cmd/system-probe/subcommands/config/command.go index 2bc71dde5373a..4eefd3aefa65f 100644 --- a/cmd/system-probe/subcommands/config/command.go +++ b/cmd/system-probe/subcommands/config/command.go @@ -64,6 +64,15 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { RunE: oneShotRunE(showRuntimeConfiguration), } + cmd.AddCommand( + &cobra.Command{ + Use: "by-source", + Short: "Show the runtime configuration by source (ie: default, config file, env vars, ...)", + Long: ``, + RunE: oneShotRunE(showRuntimeConfigurationBySource), + }, + ) + cmd.AddCommand( &cobra.Command{ Use: "list-runtime", @@ -112,6 +121,22 @@ func showRuntimeConfiguration(sysprobeconfig sysprobeconfig.Component, _ *cliPar return nil } +func showRuntimeConfigurationBySource(sysprobeconfig sysprobeconfig.Component, _ *cliParams) error { + c, err := getClient(sysprobeconfig) + if err != nil { + return err + } + + config, err := c.FullConfigBySource() + if err != nil { + return err + } + + fmt.Println(config) + + return nil +} + func listRuntimeConfigurableValue(sysprobeconfig sysprobeconfig.Component, _ *cliParams) error { c, err := getClient(sysprobeconfig) if err != nil { diff --git a/cmd/system-probe/subcommands/config/command_test.go b/cmd/system-probe/subcommands/config/command_test.go index 32e6065dea601..661b5b9dcc7f5 100644 --- a/cmd/system-probe/subcommands/config/command_test.go +++ b/cmd/system-probe/subcommands/config/command_test.go @@ -44,3 +44,11 @@ func TestGetConfigValueCommand(t *testing.T) { getConfigValue, func() {}) } + +func TestShowConfigBySource(t *testing.T) { + fxutil.TestOneShotSubcommand(t, + Commands(&command.GlobalParams{}), + []string{"config", "by-source"}, + showRuntimeConfigurationBySource, + func() {}) +} diff --git a/comp/core/settings/component.go b/comp/core/settings/component.go index e16aef8e189fe..948323cea3947 100644 --- a/comp/core/settings/component.go +++ b/comp/core/settings/component.go @@ -53,8 +53,11 @@ type Component interface { // API related functions // Todo: (Components) Remove these functions once we can register routes using FX value groups + // GetFullConfig returns the full config GetFullConfig(namespaces ...string) http.HandlerFunc + // GetFullConfigBySource returns the full config by sources (config, default, env vars ...) + GetFullConfigBySource() http.HandlerFunc // GetValue allows to retrieve the runtime setting GetValue(w http.ResponseWriter, r *http.Request) // SetValue allows to modify the runtime setting diff --git a/comp/core/settings/settingsimpl/settings_mock.go b/comp/core/settings/settingsimpl/settings_mock.go index 0e7bd7b537a86..6b1b2743663d7 100644 --- a/comp/core/settings/settingsimpl/settings_mock.go +++ b/comp/core/settings/settingsimpl/settings_mock.go @@ -74,6 +74,11 @@ func (m mock) GetFullConfig(...string) http.HandlerFunc { return func(http.ResponseWriter, *http.Request) {} } +// GetFullConfigBySource returns the full config by sources +func (m mock) GetFullConfigBySource() http.HandlerFunc { + return func(http.ResponseWriter, *http.Request) {} +} + // GetValue allows to retrieve the runtime setting func (m mock) GetValue(http.ResponseWriter, *http.Request) {} diff --git a/comp/core/settings/settingsimpl/settingsimpl.go b/comp/core/settings/settingsimpl/settingsimpl.go index 660786b40d49a..c88c9feed8e25 100644 --- a/comp/core/settings/settingsimpl/settingsimpl.go +++ b/comp/core/settings/settingsimpl/settingsimpl.go @@ -142,6 +142,31 @@ func (s *settingsRegistry) GetFullConfig(namespaces ...string) http.HandlerFunc } } +func (s *settingsRegistry) GetFullConfigBySource() http.HandlerFunc { + return func(w http.ResponseWriter, _ *http.Request) { + settings := s.config.AllSettingsBySource() + w.Header().Set("Content-Type", "application/json") + + jsonData, err := json.Marshal(settings) + if err != nil { + s.log.Errorf("Unable to marshal config by layer: %s", err) + body, _ := json.Marshal(map[string]string{"error": err.Error()}) + http.Error(w, string(body), http.StatusInternalServerError) + return + } + + scrubbed, err := scrubber.ScrubJSON(jsonData) + if err != nil { + s.log.Errorf("Unable to scrub sensitive data from config by layer: %s", err) + body, _ := json.Marshal(map[string]string{"error": err.Error()}) + http.Error(w, string(body), http.StatusInternalServerError) + return + } + + _, _ = w.Write(scrubbed) + } +} + func (s *settingsRegistry) ListConfigurable(w http.ResponseWriter, _ *http.Request) { configurableSettings := make(map[string]settings.RuntimeSettingResponse) for name, setting := range s.RuntimeSettings() { diff --git a/comp/core/settings/settingsimpl/settingsimpl_test.go b/comp/core/settings/settingsimpl/settingsimpl_test.go index 4460c313ce1a0..4b987ad340060 100644 --- a/comp/core/settings/settingsimpl/settingsimpl_test.go +++ b/comp/core/settings/settingsimpl/settingsimpl_test.go @@ -116,6 +116,23 @@ func TestRuntimeSettings(t *testing.T) { assert.NotEqual(t, "", string(body)) }, }, + { + "GetFullConfigBySource", + func(t *testing.T, comp settings.Component) { + responseRecorder := httptest.NewRecorder() + request := httptest.NewRequest("GET", "http://agent.host/test/", nil) + + comp.GetFullConfigBySource()(responseRecorder, request) + resp := responseRecorder.Result() + defer resp.Body.Close() + body, _ := io.ReadAll(resp.Body) + + assert.Equal(t, 200, responseRecorder.Code) + // The full config is too big to assert against + // Ensure the response body is not empty to validate we wrote something + assert.NotEqual(t, "", string(body)) + }, + }, { "ListConfigurable", func(t *testing.T, comp settings.Component) { diff --git a/pkg/config/settings/api.go b/pkg/config/settings/api.go index 650fa8e319b62..c9e3fddf9067e 100644 --- a/pkg/config/settings/api.go +++ b/pkg/config/settings/api.go @@ -17,6 +17,7 @@ type Client interface { Set(key string, value string) (bool, error) List() (map[string]settings.RuntimeSettingResponse, error) FullConfig() (string, error) + FullConfigBySource() (string, error) } // ClientBuilder represents a function returning a runtime settings API client diff --git a/pkg/config/settings/http/client.go b/pkg/config/settings/http/client.go index 210c25b5d7878..748ad5834fbd4 100644 --- a/pkg/config/settings/http/client.go +++ b/pkg/config/settings/http/client.go @@ -30,35 +30,46 @@ func NewClient(c *http.Client, baseURL string, targetProcessName string, clientO return &runtimeSettingsHTTPClient{c, baseURL, targetProcessName, clientOptions} } -func (rc *runtimeSettingsHTTPClient) FullConfig() (string, error) { - r, err := util.DoGet(rc.c, rc.baseURL, rc.clientOptions.CloseConnection) +func (rc *runtimeSettingsHTTPClient) doGet(url string, formatError bool) (string, error) { + r, err := util.DoGet(rc.c, url, rc.clientOptions.CloseConnection) if err != nil { - var errMap = make(map[string]string) + errMap := make(map[string]string) _ = json.Unmarshal(r, &errMap) // If the error has been marshalled into a json object, check it and return it properly if e, found := errMap["error"]; found { return "", fmt.Errorf(e) } + if formatError { + return "", fmt.Errorf("Could not reach %s: %v \nMake sure the %s is running before requesting the runtime configuration and contact support if you continue having issues", rc.targetProcessName, err, rc.targetProcessName) + } + return "", err + } + return string(r), nil +} - return "", fmt.Errorf("Could not reach %s: %v \nMake sure the %s is running before requesting the runtime configuration and contact support if you continue having issues", rc.targetProcessName, err, rc.targetProcessName) +func (rc *runtimeSettingsHTTPClient) FullConfig() (string, error) { + r, err := rc.doGet(rc.baseURL, true) + if err != nil { + return "", err } + return string(r), nil +} +func (rc *runtimeSettingsHTTPClient) FullConfigBySource() (string, error) { + r, err := rc.doGet(fmt.Sprintf("%s/by-source", rc.baseURL), true) + if err != nil { + return "", err + } return string(r), nil } func (rc *runtimeSettingsHTTPClient) List() (map[string]settingsComponent.RuntimeSettingResponse, error) { - r, err := util.DoGet(rc.c, fmt.Sprintf("%s/%s", rc.baseURL, "list-runtime"), rc.clientOptions.CloseConnection) + r, err := rc.doGet(fmt.Sprintf("%s/list-runtime", rc.baseURL), false) if err != nil { - var errMap = make(map[string]string) - _ = json.Unmarshal(r, &errMap) - // If the error has been marshalled into a json object, check it and return it properly - if e, found := errMap["error"]; found { - return nil, fmt.Errorf(e) - } return nil, err } - var settingsList = make(map[string]settingsComponent.RuntimeSettingResponse) - err = json.Unmarshal(r, &settingsList) + settingsList := make(map[string]settingsComponent.RuntimeSettingResponse) + err = json.Unmarshal([]byte(r), &settingsList) if err != nil { return nil, err } @@ -67,19 +78,13 @@ func (rc *runtimeSettingsHTTPClient) List() (map[string]settingsComponent.Runtim } func (rc *runtimeSettingsHTTPClient) Get(key string) (interface{}, error) { - r, err := util.DoGet(rc.c, fmt.Sprintf("%s/%s", rc.baseURL, key), rc.clientOptions.CloseConnection) + r, err := rc.doGet(fmt.Sprintf("%s/%s", rc.baseURL, key), false) if err != nil { - var errMap = make(map[string]string) - _ = json.Unmarshal(r, &errMap) - // If the error has been marshalled into a json object, check it and return it properly - if e, found := errMap["error"]; found { - return nil, fmt.Errorf(e) - } return nil, err } - var setting = make(map[string]interface{}) - err = json.Unmarshal(r, &setting) + setting := make(map[string]interface{}) + err = json.Unmarshal([]byte(r), &setting) if err != nil { return nil, err } @@ -90,19 +95,13 @@ func (rc *runtimeSettingsHTTPClient) Get(key string) (interface{}, error) { } func (rc *runtimeSettingsHTTPClient) GetWithSources(key string) (map[string]interface{}, error) { - r, err := util.DoGet(rc.c, fmt.Sprintf("%s/%s?sources=true", rc.baseURL, key), rc.clientOptions.CloseConnection) + r, err := rc.doGet(fmt.Sprintf("%s/%s?sources=true", rc.baseURL, key), false) if err != nil { - var errMap = make(map[string]string) - _ = json.Unmarshal(r, &errMap) - // If the error has been marshalled into a json object, check it and return it properly - if e, found := errMap["error"]; found { - return nil, fmt.Errorf("%s", e) - } return nil, err } - var setting = make(map[string]interface{}) - err = json.Unmarshal(r, &setting) + setting := make(map[string]interface{}) + err = json.Unmarshal([]byte(r), &setting) if err != nil { return nil, err } @@ -127,7 +126,7 @@ func (rc *runtimeSettingsHTTPClient) Set(key string, value string) (bool, error) body := fmt.Sprintf("value=%s", html.EscapeString(value)) r, err := util.DoPost(rc.c, fmt.Sprintf("%s/%s", rc.baseURL, key), "application/x-www-form-urlencoded", bytes.NewBuffer([]byte(body))) if err != nil { - var errMap = make(map[string]string) + errMap := make(map[string]string) _ = json.Unmarshal(r, &errMap) // If the error has been marshalled into a json object, check it and return it properly if e, found := errMap["error"]; found { From 09aaad8eab2b60f8f06aca174b3a5581d58fd905 Mon Sep 17 00:00:00 2001 From: Alex Lopez Date: Thu, 30 May 2024 11:18:46 +0200 Subject: [PATCH 20/54] Disable publication of Agent 6 docker images (#26072) * Disable publication of Agent 6 docker images * Remove references to deleted a6 deploy file --- .../deploy_containers/deploy_containers.yml | 28 +------- .../deploy_containers_a6.yml | 69 ------------------- 2 files changed, 1 insertion(+), 96 deletions(-) delete mode 100644 .gitlab/deploy_containers/deploy_containers_a6.yml diff --git a/.gitlab/deploy_containers/deploy_containers.yml b/.gitlab/deploy_containers/deploy_containers.yml index dfc6b7dfd2dc4..b068839f0b6d5 100644 --- a/.gitlab/deploy_containers/deploy_containers.yml +++ b/.gitlab/deploy_containers/deploy_containers.yml @@ -1,32 +1,6 @@ --- # deploy containers stage -# Contains jobs which create child pipelines to deploy Agent 6 & 7 to staging repositories and to Dockerhub / GCR. - -# -# Agent v6 -# - -deploy_containers-a6: - stage: deploy_containers - rules: - !reference [.on_deploy] - variables: - PARENT_PIPELINE_ID: $CI_PIPELINE_ID - BUCKET_BRANCH: $BUCKET_BRANCH - trigger: - include: .gitlab/deploy_containers/deploy_containers_a6.yml - -deploy_containers-a6-on-failure: - stage: deploy_containers - rules: - !reference [.on_deploy_failure] - variables: - PARENT_PIPELINE_ID: $CI_PIPELINE_ID - BUCKET_BRANCH: $BUCKET_BRANCH - FORCE_MANUAL: "true" - trigger: - include: .gitlab/deploy_containers/deploy_containers_a6.yml - +# Contains jobs which create child pipelines to deploy Agent 7 to staging repositories and to Dockerhub / GCR. deploy_containers-a7: stage: deploy_containers diff --git a/.gitlab/deploy_containers/deploy_containers_a6.yml b/.gitlab/deploy_containers/deploy_containers_a6.yml deleted file mode 100644 index b2a4d604faf4f..0000000000000 --- a/.gitlab/deploy_containers/deploy_containers_a6.yml +++ /dev/null @@ -1,69 +0,0 @@ ---- -# deploy containers stage -# Contains jobs which deploy Agent 6 & 7 to staging repositories and to Dockerhub / GCR. - -stages: - - deploy_containers - -include: - - .gitlab/common/container_publish_job_templates.yml - - .gitlab/deploy_containers/conditions.yml - -# -# Image tagging & manifest publication -# - -# -# Agent v6 -# -.deploy_containers-a6-base: - extends: .docker_publish_job_definition - stage: deploy_containers - dependencies: [] - before_script: - - source /root/.bashrc - - if [[ "$VERSION" == "" ]]; then export VERSION="$(inv agent.version --major-version 6 --url-safe --pipeline-id $PARENT_PIPELINE_ID)"; fi - - export IMG_SOURCES="${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6${JMX}-amd64,${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6${JMX}-arm64" - - export IMG_DESTINATIONS="${AGENT_REPOSITORY}:${VERSION}${JMX}" - parallel: - matrix: - - JMX: - - "" - - "-jmx" - - -deploy_containers-a6: - extends: .deploy_containers-a6-base - rules: - !reference [.manual_on_deploy_auto_on_rc] - - - -deploy_containers-a6-rc: - extends: .deploy_containers-a6-base - variables: - AGENT_REPOSITORY: agent - DSD_REPOSITORY: dogstatsd - IMG_REGISTRIES: public - VERSION: 6-rc - rules: - !reference [.on_rc] - - -# -# Latest publication -# - -deploy_containers_latest-a6: - extends: .docker_publish_job_definition - stage: deploy_containers - rules: - !reference [.on_final] - dependencies: [] - parallel: - matrix: - - IMG_SOURCES: ${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6-amd64,${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6-arm64 - IMG_DESTINATIONS: ${AGENT_REPOSITORY}:6,${AGENT_REPOSITORY}:latest-py2 - - IMG_SOURCES: ${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6-jmx-amd64,${SRC_AGENT}:v${PARENT_PIPELINE_ID}-${CI_COMMIT_SHORT_SHA}-6-jmx-arm64 - IMG_DESTINATIONS: ${AGENT_REPOSITORY}:6-jmx,${AGENT_REPOSITORY}:latest-py2-jmx - From 84f3177f32df75818729deae436fd9edf3000a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9lian=20Raimbault?= <161456554+CelianR@users.noreply.github.com> Date: Thu, 30 May 2024 11:59:40 +0200 Subject: [PATCH 21/54] [Team notifications] Create job failing summary report within `datadog-agent-ops` (#25949) * [rfc-team-notifications-2] Added send failure summary notification + workflow [skip ci] * [rfc-team-notifications-2] Updated send failure summary notification message [skip ci] * [rfc-team-notifications-2] Updated send failure summary notification message [skip ci] * [rfc-team-notifications-2] Updated send failure summary notification message [skip ci] * [rfc-team-notifications-2] Updated send failure summary notification message [skip ci] * [rfc-team-notifications-2] WIP [skip ci] * [rfc-team-notifications-2] WIP [skip ci] * [rfc-team-notifications-2] WIP [skip ci] * [rfc-team-notifications-2] WIP [skip ci] * [rfc-team-notifications-2] Cleaned code * [rfc-team-notifications-2] Updated github workflow * [rfc-team-notifications-2] Updated github workflow * [rfc-team-notifications-2] Updated comment * [rfc-team-notifications-2] Updated comment * Update tasks/notify.py [skip ci] Co-authored-by: Nicolas Schweitzer * [rfc-team-notifications-2] Applied suggestions, added tests --------- Co-authored-by: Nicolas Schweitzer --- .../send-failure-summary-notification.yml | 34 +++++++++++++++ tasks/notify.py | 42 ++++++++++++++++++- tasks/unit-tests/notify_tests.py | 16 +++++++ 3 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/send-failure-summary-notification.yml diff --git a/.github/workflows/send-failure-summary-notification.yml b/.github/workflows/send-failure-summary-notification.yml new file mode 100644 index 0000000000000..5e602e70fb967 --- /dev/null +++ b/.github/workflows/send-failure-summary-notification.yml @@ -0,0 +1,34 @@ +name: Send failure summary notification + +on: + workflow_dispatch: + inputs: + # See notify_tests.py to see the format + job_failures: + description: 'Job failures' + required: true + type: string + +jobs: + send-failure-summary-team-notification: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + ref: main + - name: Setup python + uses: actions/setup-python@v5 + with: + python-version: 3.11 + cache: 'pip' + cache-dependency-path: '**/requirements*.txt' + - name: Install dependencies + run: pip install -r requirements.txt -r tasks/requirements.txt + - name: Create and send failure summary notification to team channel + id: send_failure_summary_team_notification + env: + # Too much data to be passed by arguments + JOB_FAILURES: ${{ inputs.job_failures }} + SLACK_API_TOKEN: ${{ secrets.SLACK_DATADOG_AGENT_BOT_TOKEN }} + run: inv -e notify.send-failure-summary-notification diff --git a/tasks/notify.py b/tasks/notify.py index 228ba50ed196f..edb3d4dd41e83 100644 --- a/tasks/notify.py +++ b/tasks/notify.py @@ -9,6 +9,7 @@ from collections import defaultdict from dataclasses import dataclass from datetime import datetime, timezone +from urllib.parse import quote from invoke import task from invoke.context import Context @@ -40,7 +41,7 @@ CONSECUTIVE_THRESHOLD = 3 CUMULATIVE_THRESHOLD = 5 CUMULATIVE_LENGTH = 10 -CI_VISIBILITY_JOB_URL = 'https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.pipeline.name%3ADataDog%2Fdatadog-agent%20%40git.branch%3Amain%20%40ci.job.name%3A{}&agg_m=count' +CI_VISIBILITY_JOB_URL = 'https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.pipeline.name%3ADataDog%2Fdatadog-agent%20%40git.branch%3Amain%20%40ci.job.name%3A"{}"&agg_m=count' @dataclass @@ -57,7 +58,7 @@ def to_dict(self): @staticmethod def ci_visibility_url(name): - return CI_VISIBILITY_JOB_URL.format(name) + return CI_VISIBILITY_JOB_URL.format(quote(name)) @staticmethod def from_dict(data): @@ -443,6 +444,43 @@ def send_notification(ctx: Context, alert_jobs): send_slack_message("#agent-platform-ops", message) +@task +def send_failure_summary_notification(_, jobs: dict[str, any] | None = None, list_max_len=10): + if jobs is None: + jobs = os.environ["JOB_FAILURES"] + jobs = json.loads(jobs) + + # List of (job_name, (failure_count, total_count)) ordered by failure_count + stats = sorted( + ((name, (fail, success)) for (name, (fail, success)) in jobs.items() if fail > 0), + key=lambda x: (x[1][0], x[1][1] if x[1][1] is not None else 0), + reverse=True, + )[:list_max_len] + + # Don't send message if no failure + if len(stats) == 0: + return + + # Create message + message = ['*Daily Job Failure Report*'] + message.append('These jobs had the most failures in the last 24 hours:') + for name, (fail, total) in stats: + link = CI_VISIBILITY_JOB_URL.format(quote(name)) + message.append(f"- <{link}|{name}>: *{fail} failures*{f' / {total} runs' if total else ''}") + + message.append( + 'Click for more details.' + ) + + # Send message + from slack_sdk import WebClient + + client = WebClient(os.environ["SLACK_API_TOKEN"]) + client.chat_postMessage(channel='#agent-platform-ops', text='\n'.join(message)) + + print('Message sent') + + @task def unit_tests(ctx, pipeline_id, pipeline_url, branch_name): from tasks.libs.ciproviders.github_api import GithubAPI diff --git a/tasks/unit-tests/notify_tests.py b/tasks/unit-tests/notify_tests.py index 11525eae0493a..a9c01547c6159 100644 --- a/tasks/unit-tests/notify_tests.py +++ b/tasks/unit-tests/notify_tests.py @@ -400,3 +400,19 @@ def test_none(self, mock_slack): alert_jobs = {"consecutive": notify.ConsecutiveJobAlert({}), "cumulative": notify.CumulativeJobAlert({})} notify.send_notification(MagicMock(), alert_jobs) mock_slack.assert_not_called() + + +class TestSendFailureSummaryNotification(unittest.TestCase): + @patch("slack_sdk.WebClient") + @patch("os.environ", new=MagicMock()) + def test_nominal(self, mock_slack): + # jobname: [total_failures, total_runs] + jobs = { + "myjob1": [45, None], + "myjob2": [42, 45], + "myjob3": [21, None], + "myjob4": [16, 89], + } + notify.send_failure_summary_notification(MockContext(), jobs) + mock_slack.assert_called() + mock_slack.return_value.chat_postMessage.assert_called() From 9cf6f5d2bb3a9627b5050b5bcabc78a3756e4f67 Mon Sep 17 00:00:00 2001 From: Kangyi LI Date: Thu, 30 May 2024 12:44:36 +0200 Subject: [PATCH 22/54] fix exitCode type (#25793) * fix exitCode type * feedback, use int64 * fix merge --- .../collectors/internal/containerd/containerd.go | 4 ++-- .../collectors/internal/containerd/event_builder.go | 5 +++-- .../internal/containerd/event_builder_test.go | 2 +- .../workloadmeta/collectors/internal/docker/docker.go | 6 +++--- comp/core/workloadmeta/merge_test.go | 4 ++-- comp/core/workloadmeta/types.go | 4 ++-- .../cluster/orchestrator/transformers/ecs/task.go | 2 +- .../cluster/orchestrator/transformers/ecs/task_test.go | 9 ++++----- .../corechecks/containerlifecycle/processor_test.go | 2 +- pkg/collector/corechecks/orchestrator/ecs/ecs_test.go | 9 ++++----- pkg/proto/datadog/workloadmeta/workloadmeta.proto | 2 +- pkg/proto/pbgo/core/workloadmeta.pb.go | 6 +++--- pkg/util/ecs/metadata/v3or4/client_test.go | 6 ++++-- .../ecs/metadata/v3or4/testdata/task_with_tags.json | 3 ++- pkg/util/ecs/metadata/v3or4/types.go | 10 +++++----- 15 files changed, 38 insertions(+), 36 deletions(-) diff --git a/comp/core/workloadmeta/collectors/internal/containerd/containerd.go b/comp/core/workloadmeta/collectors/internal/containerd/containerd.go index cc3bd072fcf89..943214a54f7a5 100644 --- a/comp/core/workloadmeta/collectors/internal/containerd/containerd.go +++ b/comp/core/workloadmeta/collectors/internal/containerd/containerd.go @@ -75,7 +75,7 @@ var containerdTopics = []string{ } type exitInfo struct { - exitCode *uint32 + exitCode *int64 exitTS time.Time } @@ -420,7 +420,7 @@ func (c *collector) deleteExitInfo(id string) { delete(c.contToExitInfo, id) } -func (c *collector) cacheExitInfo(id string, exitCode *uint32, exitTS time.Time) { +func (c *collector) cacheExitInfo(id string, exitCode *int64, exitTS time.Time) { c.contToExitInfo[id] = &exitInfo{ exitTS: exitTS, exitCode: exitCode, diff --git a/comp/core/workloadmeta/collectors/internal/containerd/event_builder.go b/comp/core/workloadmeta/collectors/internal/containerd/event_builder.go index af4d4a312ff8c..7faaed0026f58 100644 --- a/comp/core/workloadmeta/collectors/internal/containerd/event_builder.go +++ b/comp/core/workloadmeta/collectors/internal/containerd/event_builder.go @@ -18,6 +18,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/workloadmeta" cutil "github.com/DataDog/datadog-agent/pkg/util/containerd" + "github.com/DataDog/datadog-agent/pkg/util/pointer" ) var errNoContainer = errors.New("no container") @@ -45,7 +46,7 @@ func (c *collector) buildCollectorEvent( return workloadmeta.CollectorEvent{}, err } - c.cacheExitInfo(containerID, &exited.ExitStatus, exited.ExitedAt.AsTime()) + c.cacheExitInfo(containerID, pointer.Ptr(int64(exited.ExitStatus)), exited.ExitedAt.AsTime()) return createSetEvent(container, containerdEvent.Namespace, c.containerdClient, store) case TaskDeleteTopic: @@ -54,7 +55,7 @@ func (c *collector) buildCollectorEvent( return workloadmeta.CollectorEvent{}, err } - c.cacheExitInfo(containerID, &deleted.ExitStatus, deleted.ExitedAt.AsTime()) + c.cacheExitInfo(containerID, pointer.Ptr(int64(deleted.ExitStatus)), deleted.ExitedAt.AsTime()) return createSetEvent(container, containerdEvent.Namespace, c.containerdClient, store) case TaskStartTopic, TaskOOMTopic, TaskPausedTopic, TaskResumedTopic: diff --git a/comp/core/workloadmeta/collectors/internal/containerd/event_builder_test.go b/comp/core/workloadmeta/collectors/internal/containerd/event_builder_test.go index 71dc70e76fb1e..3614df60e38b0 100644 --- a/comp/core/workloadmeta/collectors/internal/containerd/event_builder_test.go +++ b/comp/core/workloadmeta/collectors/internal/containerd/event_builder_test.go @@ -52,7 +52,7 @@ func TestBuildCollectorEvent(t *testing.T) { }, } - exitCode := uint32(137) + exitCode := int64(137) exitTime := time.Now() fakeExitInfo := &exitInfo{exitCode: &exitCode, exitTS: exitTime} diff --git a/comp/core/workloadmeta/collectors/internal/docker/docker.go b/comp/core/workloadmeta/collectors/internal/docker/docker.go index 16c5de06d4ccc..42dcd7626d175 100644 --- a/comp/core/workloadmeta/collectors/internal/docker/docker.go +++ b/comp/core/workloadmeta/collectors/internal/docker/docker.go @@ -321,13 +321,13 @@ func (c *collector) buildCollectorEvent(ctx context.Context, ev *docker.Containe } case events.ActionDie, docker.ActionDied: - var exitCode *uint32 + var exitCode *int64 if exitCodeString, found := ev.Attributes["exitCode"]; found { - exitCodeInt, err := strconv.ParseInt(exitCodeString, 10, 32) + exitCodeInt, err := strconv.ParseInt(exitCodeString, 10, 64) if err != nil { log.Debugf("Cannot convert exit code %q: %v", exitCodeString, err) } else { - exitCode = pointer.Ptr(uint32(exitCodeInt)) + exitCode = pointer.Ptr(exitCodeInt) } } diff --git a/comp/core/workloadmeta/merge_test.go b/comp/core/workloadmeta/merge_test.go index 6932439307435..22ee10a53c2b9 100644 --- a/comp/core/workloadmeta/merge_test.go +++ b/comp/core/workloadmeta/merge_test.go @@ -86,7 +86,7 @@ func container2(testTime time.Time) Container { //nolint:revive // TODO fix revi CreatedAt: time.Time{}, StartedAt: time.Time{}, FinishedAt: time.Time{}, - ExitCode: pointer.Ptr(uint32(100)), + ExitCode: pointer.Ptr(int64(100)), }, CollectorTags: []string{"tag3"}, } @@ -109,7 +109,7 @@ func TestMerge(t *testing.T) { CreatedAt: testTime, StartedAt: testTime, FinishedAt: time.Time{}, - ExitCode: pointer.Ptr(uint32(100)), + ExitCode: pointer.Ptr(int64(100)), }, } diff --git a/comp/core/workloadmeta/types.go b/comp/core/workloadmeta/types.go index b0a5b6beb5e28..c7e4a90307c97 100644 --- a/comp/core/workloadmeta/types.go +++ b/comp/core/workloadmeta/types.go @@ -313,7 +313,7 @@ type ContainerState struct { CreatedAt time.Time StartedAt time.Time FinishedAt time.Time - ExitCode *uint32 + ExitCode *int64 } // String returns a string representation of ContainerState. @@ -395,7 +395,7 @@ func (c ContainerVolume) String(_ bool) string { type ContainerHealthStatus struct { Status string Since *time.Time - ExitCode *uint32 + ExitCode *int64 Output string } diff --git a/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task.go b/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task.go index 5789d716a5a3b..b8f899a7b26d7 100644 --- a/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task.go +++ b/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task.go @@ -140,7 +140,7 @@ func extractTimestamp(t time.Time) int64 { return t.Unix() } -func extractExitCode(exitCode *uint32) *model.ECSContainerExitCode { +func extractExitCode(exitCode *int64) *model.ECSContainerExitCode { if exitCode == nil { return nil } diff --git a/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task_test.go b/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task_test.go index 6c03f06b216e7..9a6c52bc81124 100644 --- a/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task_test.go +++ b/pkg/collector/corechecks/cluster/orchestrator/transformers/ecs/task_test.go @@ -15,6 +15,7 @@ import ( model "github.com/DataDog/agent-payload/v5/process" "github.com/DataDog/datadog-agent/comp/core/workloadmeta" + "github.com/DataDog/datadog-agent/pkg/util/pointer" ) func TestExtractECSTask(t *testing.T) { @@ -74,11 +75,9 @@ func TestExtractECSTask(t *testing.T) { ECSContainer: &workloadmeta.ECSContainer{ DisplayName: "log_router_container", Health: &workloadmeta.ContainerHealthStatus{ - Status: "HEALTHY", - Since: &now, - ExitCode: func(i uint32) *uint32 { - return &i - }(2), + Status: "HEALTHY", + Since: &now, + ExitCode: pointer.Ptr(int64(2)), }, Type: "NORMAL", }, diff --git a/pkg/collector/corechecks/containerlifecycle/processor_test.go b/pkg/collector/corechecks/containerlifecycle/processor_test.go index f0f1073bd2e2e..80be6628fa2ed 100644 --- a/pkg/collector/corechecks/containerlifecycle/processor_test.go +++ b/pkg/collector/corechecks/containerlifecycle/processor_test.go @@ -121,7 +121,7 @@ func TestProcessContainer(t *testing.T) { } now := time.Now() - exitCode := uint32(1) + exitCode := int64(1) podContainer := workloadmeta.Container{ EntityID: workloadmeta.EntityID{ ID: "cont1", diff --git a/pkg/collector/corechecks/orchestrator/ecs/ecs_test.go b/pkg/collector/corechecks/orchestrator/ecs/ecs_test.go index 2e8ddf447d4f7..997cf14f746f7 100644 --- a/pkg/collector/corechecks/orchestrator/ecs/ecs_test.go +++ b/pkg/collector/corechecks/orchestrator/ecs/ecs_test.go @@ -21,6 +21,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/orchestrator" oconfig "github.com/DataDog/datadog-agent/pkg/orchestrator/config" "github.com/DataDog/datadog-agent/pkg/serializer/types" + "github.com/DataDog/datadog-agent/pkg/util/pointer" ) func TestGetRegionAndAWSAccountID(t *testing.T) { @@ -245,10 +246,8 @@ func container1(v4 bool) *workloadmeta.Container { container.ECSContainer = &workloadmeta.ECSContainer{ DisplayName: "log_router_container", Health: &workloadmeta.ContainerHealthStatus{ - Status: "HEALTHY", - ExitCode: func(i uint32) *uint32 { - return &i - }(2), + Status: "HEALTHY", + ExitCode: pointer.Ptr(int64(-2)), }, Type: "NORMAL", } @@ -327,7 +326,7 @@ func expected(v4 bool, groupID int32, ids ...string) *process.CollectorECSTask { container1.Health = &process.ECSContainerHealth{ Status: "HEALTHY", ExitCode: &process.ECSContainerExitCode{ - ExitCode: 2, + ExitCode: -2, }, } diff --git a/pkg/proto/datadog/workloadmeta/workloadmeta.proto b/pkg/proto/datadog/workloadmeta/workloadmeta.proto index fd3c522ebe8b8..a02ade2a01b92 100644 --- a/pkg/proto/datadog/workloadmeta/workloadmeta.proto +++ b/pkg/proto/datadog/workloadmeta/workloadmeta.proto @@ -91,7 +91,7 @@ message ContainerState { int64 createdAt = 4; int64 startedAt = 5; int64 finishedAt = 6; - uint32 exitCode = 7; + int64 exitCode = 7; } message Container { diff --git a/pkg/proto/pbgo/core/workloadmeta.pb.go b/pkg/proto/pbgo/core/workloadmeta.pb.go index d59bb72eaed8d..12dc3ba9d464a 100644 --- a/pkg/proto/pbgo/core/workloadmeta.pb.go +++ b/pkg/proto/pbgo/core/workloadmeta.pb.go @@ -778,7 +778,7 @@ type ContainerState struct { CreatedAt int64 `protobuf:"varint,4,opt,name=createdAt,proto3" json:"createdAt,omitempty"` StartedAt int64 `protobuf:"varint,5,opt,name=startedAt,proto3" json:"startedAt,omitempty"` FinishedAt int64 `protobuf:"varint,6,opt,name=finishedAt,proto3" json:"finishedAt,omitempty"` - ExitCode uint32 `protobuf:"varint,7,opt,name=exitCode,proto3" json:"exitCode,omitempty"` + ExitCode int64 `protobuf:"varint,7,opt,name=exitCode,proto3" json:"exitCode,omitempty"` } func (x *ContainerState) Reset() { @@ -855,7 +855,7 @@ func (x *ContainerState) GetFinishedAt() int64 { return 0 } -func (x *ContainerState) GetExitCode() uint32 { +func (x *ContainerState) GetExitCode() int64 { if x != nil { return x.ExitCode } @@ -1598,7 +1598,7 @@ var file_datadog_workloadmeta_workloadmeta_proto_rawDesc = []byte{ 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x78, 0x69, - 0x74, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x65, 0x78, 0x69, + 0x74, 0x43, 0x6f, 0x64, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x22, 0x89, 0x06, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x46, 0x0a, 0x08, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, diff --git a/pkg/util/ecs/metadata/v3or4/client_test.go b/pkg/util/ecs/metadata/v3or4/client_test.go index 5805ed8a127c0..5530e1c87aaed 100644 --- a/pkg/util/ecs/metadata/v3or4/client_test.go +++ b/pkg/util/ecs/metadata/v3or4/client_test.go @@ -15,6 +15,7 @@ import ( "github.com/stretchr/testify/require" "github.com/DataDog/datadog-agent/pkg/util/ecs/metadata/testutil" + "github.com/DataDog/datadog-agent/pkg/util/pointer" ) func TestGetV4TaskWithTags(t *testing.T) { @@ -100,8 +101,9 @@ var expected = &Task{ StartedAt: "2023-11-20T12:10:44.404563253Z", Type: "NORMAL", Health: &HealthStatus{ - Status: "HEALTHY", - Since: "2023-11-20T12:11:16.383262018Z", + Status: "HEALTHY", + Since: "2023-11-20T12:11:16.383262018Z", + ExitCode: pointer.Ptr(int64(-1)), }, Volumes: []Volume{ { diff --git a/pkg/util/ecs/metadata/v3or4/testdata/task_with_tags.json b/pkg/util/ecs/metadata/v3or4/testdata/task_with_tags.json index d6930853e9fa6..e6a1798fbfd1b 100644 --- a/pkg/util/ecs/metadata/v3or4/testdata/task_with_tags.json +++ b/pkg/util/ecs/metadata/v3or4/testdata/task_with_tags.json @@ -63,7 +63,8 @@ "Type": "NORMAL", "Health": { "status": "HEALTHY", - "statusSince": "2023-11-20T12:11:16.383262018Z" + "statusSince": "2023-11-20T12:11:16.383262018Z", + "exitCode": -1 }, "Volumes": [ { diff --git a/pkg/util/ecs/metadata/v3or4/types.go b/pkg/util/ecs/metadata/v3or4/types.go index 5b6a62268322c..46a82c27f51a4 100644 --- a/pkg/util/ecs/metadata/v3or4/types.go +++ b/pkg/util/ecs/metadata/v3or4/types.go @@ -48,16 +48,16 @@ type Container struct { ContainerARN string `json:"ContainerARN,omitempty"` // present only in v4 Health *HealthStatus `json:"Health,omitempty"` Volumes []Volume `json:"Volumes,omitempty"` - ExitCode *uint32 `json:"ExitCode,omitempty"` + ExitCode *int64 `json:"ExitCode,omitempty"` Snapshotter string `json:"Snapshotter,omitempty"` } // HealthStatus represents the health status of a container type HealthStatus struct { - Status string `json:"status,omitempty"` - Since string `json:"statusSince,omitempty"` - ExitCode *uint32 `json:"exitCode,omitempty"` - Output string `json:"output,omitempty"` + Status string `json:"status,omitempty"` + Since string `json:"statusSince,omitempty"` + ExitCode *int64 `json:"exitCode,omitempty"` + Output string `json:"output,omitempty"` } // Network represents the network of a container From 78e4822f894513fd3edd591332df10b6ebdf52f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Thu, 30 May 2024 12:51:50 +0200 Subject: [PATCH 23/54] split dogstatsd build and packaging (#26045) * split dogstatsd build and packaging * remove reference to deleted file * use the same rules for both dogstatsd binaries jobs * rename packaging variable * don't setup package script if we're not packaging --- .gitlab/binary_build/linux.yml | 5 +- .gitlab/package_build/deb.yml | 66 --------------------------- .gitlab/package_build/include.yml | 3 -- .gitlab/package_build/linux.yml | 40 ++++++++++++++++ .gitlab/package_build/rpm.yml | 35 -------------- .gitlab/package_build/suse_rpm.yml | 37 --------------- .gitlab/packaging/deb.yml | 31 +++++++++++++ .gitlab/packaging/rpm.yml | 23 ++++++++++ omnibus/config/projects/dogstatsd.rb | 68 +++++++++++++++++----------- 9 files changed, 138 insertions(+), 170 deletions(-) delete mode 100644 .gitlab/package_build/deb.yml delete mode 100644 .gitlab/package_build/rpm.yml delete mode 100644 .gitlab/package_build/suse_rpm.yml diff --git a/.gitlab/binary_build/linux.yml b/.gitlab/binary_build/linux.yml index 329eee1a568aa..d8644d63a2c9c 100644 --- a/.gitlab/binary_build/linux.yml +++ b/.gitlab/binary_build/linux.yml @@ -53,9 +53,8 @@ build_dogstatsd-binary_x64: build_dogstatsd-binary_arm64: rules: - - !reference [.on_all_builds] - - !reference [.on_packaging_change] - - !reference [.on_go-version_change] + - !reference [.except_mergequeue] + - when: on_success stage: binary_build image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES tags: ["arch:arm64"] diff --git a/.gitlab/package_build/deb.yml b/.gitlab/package_build/deb.yml deleted file mode 100644 index 5283d79f23cbd..0000000000000 --- a/.gitlab/package_build/deb.yml +++ /dev/null @@ -1,66 +0,0 @@ -dogstatsd_deb-x64: - rules: - - !reference [.except_mergequeue] - - when: on_success - stage: package_build - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] - needs: ["go_mod_tidy_check", "build_dogstatsd-binary_x64", "go_deps"] - variables: - DD_PKG_ARCH: "x86_64" - before_script: - - source /root/.bashrc - - !reference [.retrieve_linux_go_deps] - - !reference [.cache_omnibus_ruby_deps, setup] - script: - # remove artifacts from previous pipelines that may come from the cache - - rm -rf $OMNIBUS_PACKAGE_DIR/* - - !reference [.setup_ruby_mirror_linux] - # Artifacts and cache must live within project directory but we run omnibus in a neutral directory. - # Thus, we move the artifacts at the end in a gitlab-friendly dir. - - !reference [.setup_deb_signing_key] - # Use --skip-deps since the deps are installed by `before_script`. - - inv -e omnibus.build --release-version "$RELEASE_VERSION_7" --major-version 7 --base-dir $OMNIBUS_BASE_DIR ${USE_S3_CACHING} --skip-deps --go-mod-cache="$GOPATH/pkg/mod" --target-project="dogstatsd" - - ls -la $OMNIBUS_PACKAGE_DIR - - !reference [.lint_linux_packages] - - $S3_CP_CMD $OMNIBUS_PACKAGE_DIR/datadog-dogstatsd*_amd64.deb $S3_ARTIFACTS_URI/datadog-dogstatsd_amd64.deb - - !reference [.upload_sbom_artifacts] - artifacts: - expire_in: 2 weeks - paths: - - $OMNIBUS_PACKAGE_DIR - cache: - - !reference [.cache_omnibus_ruby_deps, cache] - -dogstatsd_deb-arm64: - rules: - - !reference [.on_all_builds] - - !reference [.on_packaging_change] - - !reference [.on_go-version_change] - stage: package_build - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] - needs: ["go_mod_tidy_check", "build_dogstatsd-binary_arm64", "go_deps"] - variables: - DD_PKG_ARCH: "arm64" - before_script: - - source /root/.bashrc - - !reference [.retrieve_linux_go_deps] - script: - # remove artifacts from previous pipelines that may come from the cache - - rm -rf $OMNIBUS_PACKAGE_DIR/* - - !reference [.setup_ruby_mirror_linux] - # Artifacts and cache must live within project directory but we run omnibus in a neutral directory. - # Thus, we move the artifacts at the end in a gitlab-friendly dir. - - !reference [.setup_deb_signing_key] - # Use --skip-deps since the deps are installed by `before_script`. - - inv -e omnibus.build --release-version "$RELEASE_VERSION_7" --major-version 7 --base-dir $OMNIBUS_BASE_DIR ${USE_S3_CACHING} --skip-deps --go-mod-cache="$GOPATH/pkg/mod" --target-project="dogstatsd" - - ls -la $OMNIBUS_PACKAGE_DIR - - !reference [.lint_linux_packages] - - $S3_CP_CMD $OMNIBUS_PACKAGE_DIR/datadog-dogstatsd*_arm64.deb $S3_ARTIFACTS_URI/datadog-dogstatsd_arm64.deb - - !reference [.upload_sbom_artifacts] - artifacts: - expire_in: 2 weeks - paths: - - $OMNIBUS_PACKAGE_DIR - diff --git a/.gitlab/package_build/include.yml b/.gitlab/package_build/include.yml index 14f045683bed6..92279f4c2b88a 100644 --- a/.gitlab/package_build/include.yml +++ b/.gitlab/package_build/include.yml @@ -10,11 +10,8 @@ - If ($lastExitCode -ne "0") { throw "Previous command returned $lastExitCode" } include: - - .gitlab/package_build/deb.yml - .gitlab/package_build/heroku.yml - .gitlab/package_build/dmg.yml - .gitlab/package_build/installer.yml - - .gitlab/package_build/rpm.yml - - .gitlab/package_build/suse_rpm.yml - .gitlab/package_build/windows.yml - .gitlab/package_build/linux.yml diff --git a/.gitlab/package_build/linux.yml b/.gitlab/package_build/linux.yml index 92e91723dabf4..725dd53c5fd8a 100644 --- a/.gitlab/package_build/linux.yml +++ b/.gitlab/package_build/linux.yml @@ -134,3 +134,43 @@ iot-agent-armhf: # if we use too many compression threads or a too agressive level FORCED_PACKAGE_COMPRESSION_LEVEL: 5 +.dogstatsd_build_common: + rules: + - !reference [.except_mergequeue] + - when: on_success + stage: package_build + script: + - source /root/.bashrc + - echo "About to build for $RELEASE_VERSION" + - !reference [.setup_ruby_mirror_linux] + - !reference [.setup_python_mirror_linux] + - !reference [.retrieve_linux_go_deps] + - !reference [.cache_omnibus_ruby_deps, setup] + # remove artifacts from previous pipelines that may come from the cache + - rm -rf $OMNIBUS_PACKAGE_DIR/* + - inv -e omnibus.build --release-version $RELEASE_VERSION_7 --major-version 7 --python-runtimes 3 --base-dir $OMNIBUS_BASE_DIR ${USE_S3_CACHING} --skip-deps --go-mod-cache="$GOPATH/pkg/mod" --target-project dogstatsd + - ls -la $OMNIBUS_PACKAGE_DIR + - !reference [.upload_sbom_artifacts] + variables: + KUBERNETES_CPU_REQUEST: 16 + KUBERNETES_MEMORY_REQUEST: "32Gi" + KUBERNETES_MEMORY_LIMIT: "32Gi" + artifacts: + expire_in: 2 weeks + paths: + - $OMNIBUS_PACKAGE_DIR + cache: + - !reference [.cache_omnibus_ruby_deps, cache] + +dogstatsd-x64: + extends: .dogstatsd_build_common + needs: ["go_mod_tidy_check", "build_dogstatsd-binary_x64", "go_deps"] + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + +dogstatsd-arm64: + extends: .dogstatsd_build_common + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES + tags: ["arch:arm64"] + needs: ["go_mod_tidy_check", "build_dogstatsd-binary_arm64", "go_deps"] + diff --git a/.gitlab/package_build/rpm.yml b/.gitlab/package_build/rpm.yml deleted file mode 100644 index 669f4c48729bc..0000000000000 --- a/.gitlab/package_build/rpm.yml +++ /dev/null @@ -1,35 +0,0 @@ -dogstatsd_rpm-x64: - stage: package_build - rules: - - !reference [.except_mergequeue] - - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] - needs: ["go_mod_tidy_check", "build_dogstatsd-binary_x64", "go_deps"] - variables: - DD_PKG_ARCH: "x86_64" - before_script: - - source /root/.bashrc - - !reference [.retrieve_linux_go_deps] - - !reference [.cache_omnibus_ruby_deps, setup] - script: - # remove artifacts from previous pipelines that may come from the cache - - rm -rf $OMNIBUS_PACKAGE_DIR/* - - !reference [.setup_ruby_mirror_linux] - # Artifacts and cache must live within project directory but we run omnibus - # from the GOPATH (see above). We then call `invoke` passing --base-dir, - # pointing to a gitlab-friendly location. - - RPM_GPG_KEY=$($CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $RPM_GPG_KEY_SSM_NAME) - - printf -- "$RPM_GPG_KEY" | gpg --import --batch - - export RPM_SIGNING_PASSPHRASE=$($CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $RPM_SIGNING_PASSPHRASE_SSM_NAME) - # Use --skip-deps since the deps are installed by `before_script`. - - inv -e omnibus.build --release-version "$RELEASE_VERSION_7" --major-version 7 --base-dir $OMNIBUS_BASE_DIR ${USE_S3_CACHING} --skip-deps --go-mod-cache="$GOPATH/pkg/mod" --target-project="dogstatsd" - - ls -la $OMNIBUS_PACKAGE_DIR - - !reference [.lint_linux_packages] - - !reference [.upload_sbom_artifacts] - artifacts: - expire_in: 2 weeks - paths: - - $OMNIBUS_PACKAGE_DIR - cache: - - !reference [.cache_omnibus_ruby_deps, cache] diff --git a/.gitlab/package_build/suse_rpm.yml b/.gitlab/package_build/suse_rpm.yml deleted file mode 100644 index 964f138254106..0000000000000 --- a/.gitlab/package_build/suse_rpm.yml +++ /dev/null @@ -1,37 +0,0 @@ -dogstatsd_suse-x64: - stage: package_build - rules: - - !reference [.except_mergequeue] - - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/suse_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] - needs: ["go_mod_tidy_check", "build_dogstatsd-binary_x64", "go_deps"] - variables: - DD_PKG_ARCH: "x86_64" - before_script: - - source /root/.bashrc - - !reference [.retrieve_linux_go_deps] - - !reference [.cache_omnibus_ruby_deps, setup] - script: - # remove artifacts from previous pipelines that may come from the cache - - rm -rf $OMNIBUS_PACKAGE_DIR_SUSE/* - - !reference [.setup_ruby_mirror_linux] - # Artifacts and cache must live within project directory but we run omnibus - # from the GOPATH (see above). We then call `invoke` passing --base-dir, - # pointing to a gitlab-friendly location. - - RPM_GPG_KEY=$($CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $RPM_GPG_KEY_SSM_NAME) - - printf -- "$RPM_GPG_KEY" | gpg --import --batch - - export RPM_SIGNING_PASSPHRASE=$($CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $RPM_SIGNING_PASSPHRASE_SSM_NAME) - # Use --skip-deps since the deps are installed by `before_script`. - - inv -e omnibus.build --release-version "$RELEASE_VERSION_7" --major-version 7 --base-dir $OMNIBUS_BASE_DIR ${USE_S3_CACHING} --skip-deps --go-mod-cache="$GOPATH/pkg/mod" --target-project=dogstatsd - - ls -la $OMNIBUS_PACKAGE_DIR - - !reference [.lint_linux_packages] - # Copy to a different directory to avoid collisions if a job downloads both the RPM and SUSE RPM artifacts - - mkdir -p $OMNIBUS_PACKAGE_DIR_SUSE && cp $OMNIBUS_PACKAGE_DIR/* $OMNIBUS_PACKAGE_DIR_SUSE - - !reference [.upload_sbom_artifacts] - artifacts: - expire_in: 2 weeks - paths: - - $OMNIBUS_PACKAGE_DIR_SUSE - cache: - - !reference [.cache_omnibus_ruby_deps, cache] diff --git a/.gitlab/packaging/deb.yml b/.gitlab/packaging/deb.yml index 012725ad29f7c..fb2ddc7ca2330 100644 --- a/.gitlab/packaging/deb.yml +++ b/.gitlab/packaging/deb.yml @@ -177,4 +177,35 @@ iot_agent_deb-armhf: DD_PKG_ARCH: "arm64" FORCED_PACKAGE_COMPRESSION_LEVEL: 5 +dogstatsd_deb-x64: + extends: .package_deb_common + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + rules: + - !reference [.except_mergequeue] + - when: on_success + tags: ["arch:amd64"] + needs: ["dogstatsd-x64"] + variables: + DD_PROJECT: dogstatsd + PACKAGE_ARCH: amd64 + DD_PKG_ARCH: "x86_64" + DESTINATION_DEB: "datadog-dogstatsd_amd64.deb" + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 + +dogstatsd_deb-arm64: + extends: .package_deb_common + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES + rules: + - !reference [.except_mergequeue] + - when: on_success + tags: ["arch:arm64"] + needs: ["dogstatsd-arm64"] + variables: + DD_PROJECT: dogstatsd + PACKAGE_ARCH: arm64 + DD_PKG_ARCH: "arm64" + DESTINATION_DEB: "datadog-dogstatsd_arm64.deb" + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 diff --git a/.gitlab/packaging/rpm.yml b/.gitlab/packaging/rpm.yml index 33925c2c46f2e..7226183c766d3 100644 --- a/.gitlab/packaging/rpm.yml +++ b/.gitlab/packaging/rpm.yml @@ -259,3 +259,26 @@ iot_agent_suse-x64: cache: - !reference [.cache_omnibus_ruby_deps, cache] +dogstatsd_rpm-x64: + extends: .package_rpm_common + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + needs: ["dogstatsd-x64"] + variables: + PACKAGE_ARCH: x86_64 + DD_PKG_ARCH: "x86_64" + DD_PROJECT: dogstatsd + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 + +dogstatsd_suse-x64: + extends: .package_suse_rpm_common + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + needs: ["dogstatsd-x64"] + variables: + PACKAGE_ARCH: x86_64 + DD_PKG_ARCH: "x86_64" + DD_PROJECT: dogstatsd + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 diff --git a/omnibus/config/projects/dogstatsd.rb b/omnibus/config/projects/dogstatsd.rb index 49c76c4b558d2..126f4739f6de9 100644 --- a/omnibus/config/projects/dogstatsd.rb +++ b/omnibus/config/projects/dogstatsd.rb @@ -79,8 +79,38 @@ # Generic package information # ------------------------------------ +if ENV["OMNIBUS_PACKAGE_ARTIFACT_DIR"] + dependency "package-artifact" + do_package = true +else + do_package = false + # ------------------------------------ + # Dependencies + # ------------------------------------ + + # creates required build directories + dependency 'datadog-agent-prepare' + + # version manifest file + dependency 'version-manifest' + + # Dogstatsd + dependency 'datadog-dogstatsd' + + # this dependency puts few files out of the omnibus install dir and move them + # in the final destination. This way such files will be listed in the packages + # manifest and owned by the package manager. This is the only point in the build + # process where we operate outside the omnibus install dir, thus the need of + # the `extra_package_file` directive. + # This must be the last dependency in the project. + + dependency 'datadog-dogstatsd-finalize' +end + + # .deb specific flags package :deb do + skip_packager !do_package vendor 'Datadog ' epoch 1 license 'Apache License Version 2.0' @@ -99,6 +129,7 @@ # .rpm specific flags package :rpm do + skip_packager !do_package vendor 'Datadog ' epoch 1 dist_tag '' @@ -123,7 +154,9 @@ end package :xz do - skip_packager true + skip_packager do_package + compression_threads COMPRESSION_THREADS + compression_level COMPRESSION_LEVEL end package :msi do @@ -167,34 +200,17 @@ pkg_position '10, 10' end -# ------------------------------------ -# Dependencies -# ------------------------------------ - -# creates required build directories -dependency 'datadog-agent-prepare' - -# version manifest file -dependency 'version-manifest' - -# Dogstatsd -dependency 'datadog-dogstatsd' - -# this dependency puts few files out of the omnibus install dir and move them -# in the final destination. This way such files will be listed in the packages -# manifest and owned by the package manager. This is the only point in the build -# process where we operate outside the omnibus install dir, thus the need of -# the `extra_package_file` directive. -# This must be the last dependency in the project. - -dependency 'datadog-dogstatsd-finalize' - # package scripts if linux_target? - if debian_target? - package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-deb" + if !do_package + extra_package_file "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-deb" + extra_package_file "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-rpm" else - package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-rpm" + if debian_target? + package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-deb" + else + package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/dogstatsd-rpm" + end end end From 344ecd725ed39019a4b04c212934cf98ca5f9427 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Thu, 30 May 2024 13:42:56 +0200 Subject: [PATCH 24/54] Fix junit_upload marking flaky tests (#26051) * Fix junit_upload marking flaky tests * Add flaky test to check * Try to fix flaky test detection * Debug * Revert debug prints * Stop breaking gohai test [skip cancel] * Delete junit [skip cancel] * Fix flake [skip cancel] * Fix unit tests * Update tasks/libs/common/junit_upload_core.py Co-authored-by: Nicolas Schweitzer --------- Co-authored-by: Nicolas Schweitzer --- tasks/libs/common/junit_upload_core.py | 24 ++++++++++++++++-------- tasks/unit-tests/junit_tests.py | 6 +++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/tasks/libs/common/junit_upload_core.py b/tasks/libs/common/junit_upload_core.py index 0fdb5ef7a7e8d..d727be6e0bad9 100644 --- a/tasks/libs/common/junit_upload_core.py +++ b/tasks/libs/common/junit_upload_core.py @@ -23,6 +23,7 @@ GITHUB_JIRA_MAP, GITHUB_SLACK_MAP, ) +from tasks.modules import DEFAULT_MODULES E2E_INTERNAL_ERROR_STRING = "E2E INTERNAL ERROR" CODEOWNERS_ORG_PREFIX = "@DataDog/" @@ -104,18 +105,23 @@ def get_flaky_from_test_output(): Read the test output file generated by gotestsum which contains a list of json for each unit test. We catch all tests marked as flaky in source code: they contain a certain message in the output field. """ - TEST_OUTPUT_FILES = ["test_output.json", "test_output_fast.json"] + TEST_OUTPUT_FILE = "module_test_output.json" FLAKE_MESSAGE = "flakytest: this is a known flaky test" test_output = [] - for test_file in TEST_OUTPUT_FILES: - if os.path.isfile(test_file): - with open(test_file) as f: + flaky_tests = set() + for module in DEFAULT_MODULES: + test_file = Path(module, TEST_OUTPUT_FILE) + if test_file.is_file(): + with test_file.open() as f: for line in f.readlines(): test_output.append(json.loads(line)) - break - flaky_tests = [ - "/".join([test["Package"], test["Test"]]) for test in test_output if FLAKE_MESSAGE in test.get("Output", "") - ] + flaky_tests.update( + [ + "/".join([test["Package"], test["Test"]]) + for test in test_output + if FLAKE_MESSAGE in test.get("Output", "") + ] + ) print(f"[INFO] Found {len(flaky_tests)} flaky tests.") return flaky_tests @@ -266,6 +272,8 @@ def set_tags(owner, flavor, flag: str, additional_tags, file_name): f"jira_project:{jira_project}", "--tags", f"gitlab.pipeline_source:{pipeline.source}", + "--xpath-tag", + "test.agent_is_known_flaky=/testcase/@agent_is_known_flaky", ] if 'e2e' in flag: tags.extend(["--tags", "e2e_internal_error:true"]) diff --git a/tasks/unit-tests/junit_tests.py b/tasks/unit-tests/junit_tests.py index 7733b5f07d700..f7173c22dc326 100644 --- a/tasks/unit-tests/junit_tests.py +++ b/tasks/unit-tests/junit_tests.py @@ -92,7 +92,7 @@ def test_default(self, mock_gitlab): mock_instance.pipelines.get.return_value = MagicMock() mock_gitlab.return_value = mock_instance tags = junit.set_tags("agent-ci-experience", "base", "", {}, "") - self.assertEqual(len(tags), 12) + self.assertEqual(len(tags), 14) self.assertIn("slack_channel:agent-developer-experience", tags) @patch.dict("os.environ", {"CI_PIPELINE_ID": "1664"}) @@ -108,7 +108,7 @@ def test_flag(self, mock_gitlab): ["upload_option.os_version_from_name"], "kitchen-rspec-win2016-azure-x86_64.xml", ) - self.assertEqual(len(tags), 16) + self.assertEqual(len(tags), 18) self.assertIn("e2e_internal_error:true", tags) self.assertIn("version:win2016", tags) self.assertNotIn("upload_option.os_version_from_name", tags) @@ -120,7 +120,7 @@ def test_additional_tags(self, mock_gitlab): mock_instance.pipelines.get.return_value = MagicMock() mock_gitlab.return_value = mock_instance tags = junit.set_tags("agent-ci-experience", "base", "", ["--tags", "simple:basique"], "") - self.assertEqual(len(tags), 14) + self.assertEqual(len(tags), 16) self.assertIn("simple:basique", tags) From 8a200ac251b276761652646974a1f7195fec29b2 Mon Sep 17 00:00:00 2001 From: David du Colombier Date: Thu, 30 May 2024 15:06:24 +0200 Subject: [PATCH 25/54] Remove agentless-scanner package (#26095) This change removes the datadog-agentless-scanner package. The agentless-scanner finally moved to its own repository. --- .github/CODEOWNERS | 1 - cmd/agentless-scanner/main.go | 10 -- omnibus/config/projects/agentless-scanner.rb | 146 ---------------- .../datadog-agentless-scanner-finalize.rb | 35 ---- .../software/datadog-agentless-scanner.rb | 66 ------- .../systemd.service.erb | 17 -- .../sysvinit_debian.agentless-scanner.erb | 163 ------------------ .../upstart_debian.conf.erb | 17 -- .../upstart_redhat.conf.erb | 18 -- .../agentless-scanner-deb/postinst | 64 ------- .../agentless-scanner-deb/postrm | 23 --- .../agentless-scanner-deb/preinst | 24 --- .../agentless-scanner-deb/prerm | 43 ----- .../agentless-scanner-rpm/postinst | 17 -- .../agentless-scanner-rpm/postrm | 22 --- .../agentless-scanner-rpm/posttrans | 43 ----- .../agentless-scanner-rpm/preinst | 23 --- .../agentless-scanner-rpm/prerm | 39 ----- tasks/__init__.py | 2 - tasks/agentless_scanner.py | 95 ---------- tasks/build_tags.py | 9 - tasks/components.py | 4 - tasks/flavor.py | 1 - tasks/libs/common/omnibus.py | 2 - tasks/unit-tests/testdata/fake_gitlab-ci.yml | 1 - 25 files changed, 885 deletions(-) delete mode 100644 cmd/agentless-scanner/main.go delete mode 100644 omnibus/config/projects/agentless-scanner.rb delete mode 100644 omnibus/config/software/datadog-agentless-scanner-finalize.rb delete mode 100644 omnibus/config/software/datadog-agentless-scanner.rb delete mode 100644 omnibus/config/templates/datadog-agentless-scanner/systemd.service.erb delete mode 100644 omnibus/config/templates/datadog-agentless-scanner/sysvinit_debian.agentless-scanner.erb delete mode 100644 omnibus/config/templates/datadog-agentless-scanner/upstart_debian.conf.erb delete mode 100644 omnibus/config/templates/datadog-agentless-scanner/upstart_redhat.conf.erb delete mode 100755 omnibus/package-scripts/agentless-scanner-deb/postinst delete mode 100755 omnibus/package-scripts/agentless-scanner-deb/postrm delete mode 100755 omnibus/package-scripts/agentless-scanner-deb/preinst delete mode 100755 omnibus/package-scripts/agentless-scanner-deb/prerm delete mode 100755 omnibus/package-scripts/agentless-scanner-rpm/postinst delete mode 100755 omnibus/package-scripts/agentless-scanner-rpm/postrm delete mode 100755 omnibus/package-scripts/agentless-scanner-rpm/posttrans delete mode 100755 omnibus/package-scripts/agentless-scanner-rpm/preinst delete mode 100755 omnibus/package-scripts/agentless-scanner-rpm/prerm delete mode 100644 tasks/agentless_scanner.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 38685aaf29908..5b025af7ce4bc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -166,7 +166,6 @@ /cmd/agent/dist/conf.d/snmp.d/ @DataDog/network-device-monitoring /cmd/agent/dist/conf.d/win32_event_log.d/ @DataDog/windows-agent /cmd/agent/install*.sh @DataDog/agent-build-and-releases -/cmd/agentless-scanner/ @DataDog/agent-cspm /cmd/cluster-agent/ @DataDog/container-platform /cmd/cluster-agent/commands/ @DataDog/container-platform /cmd/cluster-agent-cloudfoundry/ @DataDog/platform-integrations diff --git a/cmd/agentless-scanner/main.go b/cmd/agentless-scanner/main.go deleted file mode 100644 index 98a166ae6932d..0000000000000 --- a/cmd/agentless-scanner/main.go +++ /dev/null @@ -1,10 +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 2023-present Datadog, Inc. - -// Package main implements the agentless-scanner command. -package main - -func main() { -} diff --git a/omnibus/config/projects/agentless-scanner.rb b/omnibus/config/projects/agentless-scanner.rb deleted file mode 100644 index 59f6129cc02b9..0000000000000 --- a/omnibus/config/projects/agentless-scanner.rb +++ /dev/null @@ -1,146 +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. - -require "./lib/ostools.rb" - -name 'agentless-scanner' -package_name 'datadog-agentless-scanner' -homepage 'http://www.datadoghq.com' -license "Apache-2.0" -license_file "../LICENSE" - -if ENV.has_key?("OMNIBUS_WORKERS_OVERRIDE") - COMPRESSION_THREADS = ENV["OMNIBUS_WORKERS_OVERRIDE"].to_i -else - COMPRESSION_THREADS = 1 -end -if ENV.has_key?("DEPLOY_AGENT") && ENV["DEPLOY_AGENT"] == "true" - COMPRESSION_LEVEL = 9 -else - COMPRESSION_LEVEL = 5 -end - -install_dir '/opt/datadog/agentless-scanner' -if redhat_target? || suse_target? - maintainer 'Datadog, Inc ' - - # NOTE: with script dependencies, we only care about preinst/postinst/posttrans, - # because these would be used in a kickstart during package installation phase. - # All of the packages that we depend on in prerm/postrm scripts always have to be - # installed on all distros that we support, so we don't have to depend on them - # explicitly. - - # postinst and posttrans scripts use a subset of preinst script deps, so we don't - # have to list them, because they'll already be there because of preinst - runtime_script_dependency :pre, "coreutils" - runtime_script_dependency :pre, "grep" - if redhat_target? - runtime_script_dependency :pre, "glibc-common" - runtime_script_dependency :pre, "shadow-utils" - else - runtime_script_dependency :pre, "glibc" - runtime_script_dependency :pre, "shadow" - end -else - maintainer 'Datadog Packages ' -end - -if debian_target? - runtime_recommended_dependency 'datadog-signing-keys (>= 1:1.3.1)' -end - -# build_version is computed by an invoke command/function. -# We can't call it directly from there, we pass it through the environment instead. -build_version ENV['PACKAGE_VERSION'] - -build_iteration 1 - -description 'Datadog Agentless Scanner - The Datadog Agentless Scanner scans your cloud environment for vulnerabilities, compliance and security issues. - . - This package installs and runs the Agentless Scanner. - . - See http://www.datadoghq.com/ for more information -' - -# ------------------------------------ -# Generic package information -# ------------------------------------ - -# .deb specific flags -package :deb do - vendor 'Datadog ' - epoch 1 - license 'Apache License Version 2.0' - section 'utils' - priority 'extra' - compression_threads COMPRESSION_THREADS - compression_level COMPRESSION_LEVEL - compression_algo "xz" - if ENV.has_key?('DEB_SIGNING_PASSPHRASE') and not ENV['DEB_SIGNING_PASSPHRASE'].empty? - signing_passphrase "#{ENV['DEB_SIGNING_PASSPHRASE']}" - if ENV.has_key?('DEB_GPG_KEY_NAME') and not ENV['DEB_GPG_KEY_NAME'].empty? - gpg_key_name "#{ENV['DEB_GPG_KEY_NAME']}" - end - end -end - -# .rpm specific flags -package :rpm do - vendor 'Datadog ' - epoch 1 - dist_tag '' - license 'Apache License Version 2.0' - category 'System Environment/Daemons' - priority 'extra' - compression_threads COMPRESSION_THREADS - compression_level COMPRESSION_LEVEL - compression_algo "xz" - if ENV.has_key?('RPM_SIGNING_PASSPHRASE') and not ENV['RPM_SIGNING_PASSPHRASE'].empty? - signing_passphrase "#{ENV['RPM_SIGNING_PASSPHRASE']}" - if ENV.has_key?('RPM_GPG_KEY_NAME') and not ENV['RPM_GPG_KEY_NAME'].empty? - gpg_key_name "#{ENV['RPM_GPG_KEY_NAME']}" - end - end -end - -# ------------------------------------ -# Dependencies -# ------------------------------------ - -# creates required build directories -dependency 'datadog-agent-prepare' - -# version manifest file -dependency 'version-manifest' - -# Agentless-scanner -dependency 'datadog-agentless-scanner' - -# this dependency puts few files out of the omnibus install dir and move them -# in the final destination. This way such files will be listed in the packages -# manifest and owned by the package manager. This is the only point in the build -# process where we operate outside the omnibus install dir, thus the need of -# the `extra_package_file` directive. -# This must be the last dependency in the project. - -dependency 'datadog-agentless-scanner-finalize' - -# package scripts -if linux_target? - if debian_target? - package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/agentless-scanner-deb" - else - package_scripts_path "#{Omnibus::Config.project_root}/package-scripts/agentless-scanner-rpm" - end -end - -if linux_target? - extra_package_file '/lib/systemd/system/datadog-agentless-scanner.service' - extra_package_file '/var/log/datadog/' -end - -exclude '\.git*' -exclude 'bundler\/git' diff --git a/omnibus/config/software/datadog-agentless-scanner-finalize.rb b/omnibus/config/software/datadog-agentless-scanner-finalize.rb deleted file mode 100644 index b2a9049e75070..0000000000000 --- a/omnibus/config/software/datadog-agentless-scanner-finalize.rb +++ /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. - -# This software definition doesn"t build anything, it"s the place where we create -# files outside the omnibus installation directory, so that we can add them to -# the package manifest using `extra_package_file` in the project definition. -require './lib/ostools.rb' - -name "datadog-agentless-scanner-finalize" -description "steps required to finalize the build" -default_version "1.0.0" - -skip_transitive_dependency_licensing true - -build do - license :project_license - - # Move system service files - mkdir "/etc/init" - move "#{install_dir}/scripts/datadog-agentless-scanner.conf", "/etc/init" - if debian_target? - # sysvinit support for debian only for now - mkdir "/etc/init.d" - move "#{install_dir}/scripts/datadog-agentless-scanner", "/etc/init.d" - end - mkdir "/lib/systemd/system" - move "#{install_dir}/scripts/datadog-agentless-scanner.service", "/lib/systemd/system" - - mkdir "/var/log/datadog" - - # cleanup clutter - delete "#{install_dir}/etc" -end diff --git a/omnibus/config/software/datadog-agentless-scanner.rb b/omnibus/config/software/datadog-agentless-scanner.rb deleted file mode 100644 index b002c7b6932d5..0000000000000 --- a/omnibus/config/software/datadog-agentless-scanner.rb +++ /dev/null @@ -1,66 +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. -require 'pathname' - -name 'datadog-agentless-scanner' - -skip_transitive_dependency_licensing true - -source path: '..' -relative_path 'src/github.com/DataDog/datadog-agent' - -build do - license :project_license - - # set GOPATH on the omnibus source dir for this software - gopath = Pathname.new(project_dir) + '../../../..' - etc_dir = "/etc/datadog-agent" - env = { - 'GOPATH' => gopath.to_path, - 'PATH' => "#{gopath.to_path}/bin:#{ENV['PATH']}", - } - - unless ENV["OMNIBUS_GOMODCACHE"].nil? || ENV["OMNIBUS_GOMODCACHE"].empty? - gomodcache = Pathname.new(ENV["OMNIBUS_GOMODCACHE"]) - env["GOMODCACHE"] = gomodcache.to_path - end - - # we assume the go deps are already installed before running omnibus - command "invoke agentless-scanner.build --rebuild --major-version $MAJOR_VERSION", env: env - - mkdir "#{install_dir}/etc/datadog-agent" - mkdir "#{install_dir}/run/" - mkdir "#{install_dir}/scripts/" - - # move around bin and config files - copy 'bin/agentless-scanner/agentless-scanner', "#{install_dir}/bin" - - if debian_target? - erb source: "upstart_debian.conf.erb", - dest: "#{install_dir}/scripts/datadog-agentless-scanner.conf", - mode: 0644, - vars: { install_dir: install_dir, etc_dir: etc_dir } - erb source: "sysvinit_debian.agentless-scanner.erb", - dest: "#{install_dir}/scripts/datadog-agentless-scanner", - mode: 0755, - vars: { install_dir: install_dir, etc_dir: etc_dir } - elsif redhat_target? || suse_target? - # Ship a different upstart job definition on RHEL to accommodate the old - # version of upstart (0.6.5) that RHEL 6 provides. - erb source: "upstart_redhat.conf.erb", - dest: "#{install_dir}/scripts/datadog-agentless-scanner.conf", - mode: 0644, - vars: { install_dir: install_dir, etc_dir: etc_dir } - end - erb source: "systemd.service.erb", - dest: "#{install_dir}/scripts/datadog-agentless-scanner.service", - mode: 0644, - vars: { install_dir: install_dir, etc_dir: etc_dir } - - # The file below is touched by software builds that don't put anything in the installation - # directory (libgcc right now) so that the git_cache gets updated let's remove it from the - # final package - delete "#{install_dir}/uselessfile" -end diff --git a/omnibus/config/templates/datadog-agentless-scanner/systemd.service.erb b/omnibus/config/templates/datadog-agentless-scanner/systemd.service.erb deleted file mode 100644 index 8be82b2392376..0000000000000 --- a/omnibus/config/templates/datadog-agentless-scanner/systemd.service.erb +++ /dev/null @@ -1,17 +0,0 @@ -[Unit] -Description=Datadog Agentless Scanner -After=network.target datadog-agent.service -BindsTo=datadog-agent.service -ConditionPathExists=<%= etc_dir %>/datadog.yaml -StartLimitInterval=10 -StartLimitBurst=5 - -[Service] -Type=simple -PIDFile=<%= install_dir %>/run/agentless-scanner.pid -Restart=on-failure -ExecStart=<%= install_dir %>/bin/agentless-scanner run -c <%= etc_dir %>/datadog.yaml -p <%= install_dir %>/run/agentless-scanner.pid -TimeoutStopSec=60 - -[Install] -WantedBy=multi-user.target diff --git a/omnibus/config/templates/datadog-agentless-scanner/sysvinit_debian.agentless-scanner.erb b/omnibus/config/templates/datadog-agentless-scanner/sysvinit_debian.agentless-scanner.erb deleted file mode 100644 index be34ae76c1b65..0000000000000 --- a/omnibus/config/templates/datadog-agentless-scanner/sysvinit_debian.agentless-scanner.erb +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/sh - -### BEGIN INIT INFO -# Provides: datadog-agentless-scanner -# Short-Description: Start and stop datadog agentless scanner -# Description: Datadog Agentless Scanner -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -### END INIT INFO - -. /lib/lsb/init-functions - -ETC_DIR="<%= etc_dir %>" -INSTALL_DIR="<%= install_dir %>" -AGENTPATH="$INSTALL_DIR/bin/agentless-scanner" -PIDFILE="$INSTALL_DIR/run/agentless-scanner.pid" -AGENT_ARGS="--config=$ETC_DIR/datadog.yaml --pidfile=$PIDFILE" -AGENT_USER="root" -NAME="datadog-agentless-scanner" -DESC="Datadog Agentless Scanner" - - - -if [ ! -x $AGENTPATH ]; then - echo "$AGENTPATH not found. Exiting $NAME" - exit 0 -fi - -if [ -r "/etc/default/${NAME}" ]; then - . "/etc/default/${NAME}" -fi - - -# -# Function that starts the daemon/service -# -do_start() -{ - # Return - # 0 if daemon has been started - # 1 if daemon was already running - # 2 if daemon could not be started - start-stop-daemon --start --test --quiet --pidfile $PIDFILE --user $AGENT_USER --startas $AGENTPATH > /dev/null \ - || return 1 - start-stop-daemon --start --background --chuid $AGENT_USER --quiet --pidfile $PIDFILE --user $AGENT_USER --startas $AGENTPATH -- \ - $AGENT_ARGS \ - || return 2 - # Add code here, if necessary, that waits for the process to be ready - # to handle requests from services started subsequently which depend - # on this one. As a last resort, sleep for some time. -} - - -# -# Start the agent and wait for it to be up -# -start_and_wait() -{ - log_daemon_msg "Starting $DESC" "$NAME" - do_start - case "$?" in - 1) log_end_msg 0 ;; - 2) log_end_msg 1 ;; - *) - # check if the agent is running once per second for 5 seconds - retries=5 - while [ $retries -gt 1 ]; do - start-stop-daemon --start --test --quiet --pidfile $PIDFILE --user $AGENT_USER --startas $AGENTPATH - if [ "$?" -eq "1" ]; then - # We've started up successfully. Exit cleanly - log_end_msg 0 - return 0 - else - retries=$(($retries - 1)) - sleep 1 - fi - done - # After 5 tries the agent didn't start. Report an error - log_end_msg 1 - - ;; - esac -} - -# -# Function that stops the daemon/service -# -do_stop() -{ - # Return - # 0 if daemon has been stopped - # 1 if daemon was already stopped - # 2 if daemon could not be stopped - # other if a failure occurred - start-stop-daemon --stop --quiet --retry=30 --pidfile $PIDFILE --user $AGENT_USER --startas $AGENTPATH - RETVAL="$?" - rm -f $PIDFILE - return $RETVAL -} - -# -# Stop the agent and wait for it to be down -# -stop_and_wait() -{ - log_daemon_msg "Stopping $DESC" "$NAME" - do_stop - case "$?" in - 0|1) log_end_msg 0 ;; - *) log_end_msg 1 ;; # Failed to stop - esac -} - -# Action to take -case "$1" in - start) - if init_is_upstart; then - exit 1 - fi - if [ "$DATADOG_ENABLED" = "no" ]; then - echo "Disabled via /etc/default/$NAME. Exiting." - exit 0 - fi - - start_and_wait - - ;; - - stop) - if init_is_upstart; then - exit 0 - fi - - stop_and_wait - - ;; - - status) - status_of_proc -p $PIDFILE $AGENTPATH $NAME && exit 0 || exit $? - ;; - - restart|force-reload) - if init_is_upstart; then - exit 1 - fi - - echo "Restarting $DESC" - - stop_and_wait - start_and_wait - - ;; - - *) - N=/etc/init.d/$NAME - echo "Usage: $N {start|stop|restart|status}" - exit 1 - ;; -esac - -exit $? diff --git a/omnibus/config/templates/datadog-agentless-scanner/upstart_debian.conf.erb b/omnibus/config/templates/datadog-agentless-scanner/upstart_debian.conf.erb deleted file mode 100644 index 489981aaf7692..0000000000000 --- a/omnibus/config/templates/datadog-agentless-scanner/upstart_debian.conf.erb +++ /dev/null @@ -1,17 +0,0 @@ -description "Datadog Agentless Scanner" - -start on started networking or started network -stop on runlevel [!2345] - -respawn -respawn limit 4 25 -normal exit 0 - -console log # redirect daemon's outputs to `/var/log/upstart/agentless-scanner.log` -env DD_LOG_TO_CONSOLE=false - -script - # setuid is not available in versions of upstart before 1.4. CentOS/RHEL6 use an earlier version of upstart. - # This is the best way to set the user in the absence of setuid. - exec su -s /bin/sh -c 'exec "$0" "$@"' dd-agent -- <%= install_dir %>/bin/agentless-scanner run -c <%= etc_dir %>/datadog.yaml -p <%= install_dir %>/run/agentless-scanner.pid -end script diff --git a/omnibus/config/templates/datadog-agentless-scanner/upstart_redhat.conf.erb b/omnibus/config/templates/datadog-agentless-scanner/upstart_redhat.conf.erb deleted file mode 100644 index 06c813d73f465..0000000000000 --- a/omnibus/config/templates/datadog-agentless-scanner/upstart_redhat.conf.erb +++ /dev/null @@ -1,18 +0,0 @@ -description "Datadog Agentless Scanner" - -start on started networking or started network -stop on runlevel [!2345] - -respawn -respawn limit 4 25 -normal exit 0 - -# console log is not available before upstart 1.4. CentOS/RHEL6 use an earlier version of upstart. -console output -env DD_LOG_TO_CONSOLE=false - -script - # setuid is not available in versions of upstart before 1.4. CentOS/RHEL6 use an earlier version of upstart. - # This is the best way to set the user in the absence of setuid. - exec su -s /bin/sh -c 'exec "$0" "$@"' dd-agent -- <%= install_dir %>/bin/agentless-scanner run -c <%= etc_dir %>/datadog.yaml -p <%= install_dir %>/run/agentless-scanner.pid -end script diff --git a/omnibus/package-scripts/agentless-scanner-deb/postinst b/omnibus/package-scripts/agentless-scanner-deb/postinst deleted file mode 100755 index f541b574cf578..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-deb/postinst +++ /dev/null @@ -1,64 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps after package is installed. -# -# .deb: STEP 5 of 5 - -INSTALL_DIR=/opt/datadog/agentless-scanner -CONFIG_DIR=/etc/datadog-agent -SERVICE_NAME=datadog-agentless-scanner - -# If we are inside the Docker container, do nothing -if [ -n "$DOCKER_DD_AGENT" ]; then - echo "Installation from docker-dd-agent, nothing to do in postinst" - exit 0 -fi - -set -e -case "$1" in - configure) - # Create a symlink to the agent's binary - ln -sf $INSTALL_DIR/bin/agentless-scanner /usr/bin/datadog-agentless-scanner - ;; - abort-upgrade|abort-remove|abort-deconfigure) - ;; - - *) - ;; -esac - -# Enable and restart the agentless-scanner service here on Debian platforms -# On RHEL, this is done in the posttrans script -# supports systemd, upstart and sysvinit -echo "Enabling service $SERVICE_NAME" -if command -v systemctl >/dev/null 2>&1; then - systemctl enable $SERVICE_NAME || echo "[ WARNING ]\tCannot enable $SERVICE_NAME with systemctl" -elif command -v initctl >/dev/null 2>&1; then - # Nothing to do, this is defined directly in the upstart job file - : -elif command -v update-rc.d >/dev/null 2>&1; then - update-rc.d $SERVICE_NAME defaults || echo "[ WARNING ]\tCannot enable $SERVICE_NAME with update-rc.d" -else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd, upstart and sysvinit." -fi - -# TODO: Use a configcheck command on the agent to determine if it's safe to restart, -# and avoid restarting when a check conf is invalid -if [ -f "$CONFIG_DIR/datadog.yaml" ]; then - echo "(Re)starting $SERVICE_NAME now..." - if command -v systemctl >/dev/null 2>&1; then - systemctl restart $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl start $SERVICE_NAME || initctl restart $SERVICE_NAME || true - elif command -v service >/dev/null 2>&1; then - service $SERVICE_NAME restart || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -else - # No datadog.yaml file is present. This is probably a clean install made with the - # step-by-step instructions/an automation tool, and the config file will be added next. - echo "No datadog.yaml file detected, not starting the agentless-scanner" -fi - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-deb/postrm b/omnibus/package-scripts/agentless-scanner-deb/postrm deleted file mode 100755 index 7975f03c85e49..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-deb/postrm +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner removal steps after package is uninstalled. -# -# .deb: STEP 3 of 5 - -INSTALL_DIR=/opt/datadog/agentless-scanner - -# Remove the symlink to the binary. -rm -f "/usr/bin/datadog-agentless-scanner" - -set -e - -case "$1" in - purge) - echo "Force-deleting $INSTALL_DIR" - rm -rf $INSTALL_DIR - ;; - *) - ;; -esac - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-deb/preinst b/omnibus/package-scripts/agentless-scanner-deb/preinst deleted file mode 100755 index ba1c1f7779f69..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-deb/preinst +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps before package is installed. -# -# .deb: STEP 2 of 5 - -SERVICE_NAME=datadog-agentless-scanner - -set -e -if [ -f "/lib/systemd/system/$SERVICE_NAME.service" ] || [ -f "/usr/lib/systemd/system/$SERVICE_NAME.service" ]; then - # Stop an already running agent - # supports systemd, upstart and sysvinit - if command -v systemctl >/dev/null 2>&1; then - systemctl stop $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl stop $SERVICE_NAME || true - elif command -v service >/dev/null 2>&1; then - service $SERVICE_NAME stop || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd, upstart and sysvinit." - fi -fi - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-deb/prerm b/omnibus/package-scripts/agentless-scanner-deb/prerm deleted file mode 100755 index 6c69a98aca5c2..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-deb/prerm +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps prior to remove the old package. -# -# .deb: STEP 1 of 5 - -SERVICE_NAME=datadog-agentless-scanner - -stop_agent() -{ - # Stop an already running agentless-scanner - # supports systemd, upstart and sysvinit - if command -v systemctl >/dev/null 2>&1; then - systemctl stop $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl stop $SERVICE_NAME || true - elif command -v service >/dev/null 2>&1; then - service $SERVICE_NAME stop || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -} - -deregister_agent() -{ - # Disable agentless-scanner start on system boot - # supports systemd, upstart and sysvinit - if command -v systemctl >/dev/null 2>&1; then - systemctl disable $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - # Nothing to do, this is defined directly in the upstart job file - : - elif command -v update-rc.d >/dev/null 2>&1; then - update-rc.d -f $SERVICE_NAME remove || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd, upstart and sysvinit." - fi -} - -stop_agent -deregister_agent - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-rpm/postinst b/omnibus/package-scripts/agentless-scanner-rpm/postinst deleted file mode 100755 index f077f17e9e583..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-rpm/postinst +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps after package is installed. -# NOTE: part of the setup is done in the posttrans (rpm-only) script -# -# .rpm: STEP 3 of 6 - -INSTALL_DIR=/opt/datadog/agentless-scanner -LOG_DIR=/var/log/datadog -CONFIG_DIR=/etc/datadog-agent - -# Set proper rights to the dd-agent user -chown -R dd-agent:dd-agent ${CONFIG_DIR} -chown -R dd-agent:dd-agent ${LOG_DIR} -chown -R dd-agent:dd-agent ${INSTALL_DIR} - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-rpm/postrm b/omnibus/package-scripts/agentless-scanner-rpm/postrm deleted file mode 100755 index 09afd77e196da..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-rpm/postrm +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner removal steps after package is uninstalled. -# -# .rpm: STEP 5 of 6 - -# Remove the symlink to the binary. -rm -f "/usr/bin/datadog-agentless-scanner" - -case "$*" in - 0) - # We're uninstalling. - # We don't delete the dd-agent user/group (see https://fedoraproject.org/wiki/Packaging:UsersAndGroups#Allocation_Strategies) - ;; - 1) - # We're upgrading. - ;; - *) - ;; -esac - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-rpm/posttrans b/omnibus/package-scripts/agentless-scanner-rpm/posttrans deleted file mode 100755 index 6bf740508dc15..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-rpm/posttrans +++ /dev/null @@ -1,43 +0,0 @@ -#! /bin/sh -# -# This script is RPM-specific -# It is run at the very end of an install/upgrade of the package -# It is NOT run on removal of the package -# -# .rpm: STEP 6 of 6 - -INSTALL_DIR=/opt/datadog/agentless-scanner -CONFIG_DIR=/etc/datadog-agent -SERVICE_NAME=datadog-agentless-scanner - -# Create a symlink to the agent's binary -ln -sf $INSTALL_DIR/bin/agentless-scanner /usr/bin/datadog-agentless-scanner - -echo "Enabling service $SERVICE_NAME" -if command -v systemctl >/dev/null 2>&1; then - systemctl enable $SERVICE_NAME || echo "[ WARNING ]\tCannot enable $SERVICE_NAME with systemctl" -elif command -v initctl >/dev/null 2>&1; then - # start/stop policy is already defined in the upstart job file - : -else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." -fi - -# TODO: Use a configcheck command on the agent to determine if it's safe to restart, -# and avoid restarting when a check conf is invalid -if [ -f "$CONFIG_DIR/datadog.yaml" ]; then - echo "(Re)starting $SERVICE_NAME now..." - if command -v systemctl >/dev/null 2>&1; then - systemctl restart $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl start $SERVICE_NAME || initctl restart $SERVICE_NAME || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -else - # No datadog.yaml file is present. This is probably a clean install made with the - # step-by-step instructions/an automation tool, and the config file will be added next. - echo "No datadog.yaml file detected, not starting the agent" -fi - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-rpm/preinst b/omnibus/package-scripts/agentless-scanner-rpm/preinst deleted file mode 100755 index 112ace3695355..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-rpm/preinst +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps before package is installed. -# -# .rpm: STEP 2 of 6 - -SERVICE_NAME=datadog-agentless-scanner - -# Linux installation -set -e -if [ -f "/lib/systemd/system/$SERVICE_NAME.service" ] || [ -f "/usr/lib/systemd/system/$SERVICE_NAME.service" ]; then - # Stop an already running agent - # Only supports systemd and upstart - if command -v systemctl >/dev/null 2>&1; then - systemctl stop $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl stop $SERVICE_NAME || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -fi - -exit 0 diff --git a/omnibus/package-scripts/agentless-scanner-rpm/prerm b/omnibus/package-scripts/agentless-scanner-rpm/prerm deleted file mode 100755 index 5bff0e3a8521b..0000000000000 --- a/omnibus/package-scripts/agentless-scanner-rpm/prerm +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -# -# Perform necessary datadog-agentless-scanner setup steps prior to remove the old package. -# -# .rpm: STEP 4 of 6 - -SERVICE_NAME=datadog-agentless-scanner - -stop_agent() -{ - # Stop an already running agentless-scanner - # Only supports systemd and upstart - if command -v systemctl >/dev/null 2>&1; then - systemctl stop $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - initctl stop $SERVICE_NAME || true - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -} - -deregister_agent() -{ - # Disable agentless-scanner start on system boot - # Only supports systemd and upstart - if command -v systemctl >/dev/null 2>&1; then - systemctl disable $SERVICE_NAME || true - elif command -v initctl >/dev/null 2>&1; then - # Nothing to do, this is defined directly in the upstart job file - : - else - echo "[ WARNING ]\tCannot detect a supported init system. The datadog-agentless-scanner package only provides service files for systemd and upstart." - fi -} - -stop_agent -deregister_agent - -exit 0 diff --git a/tasks/__init__.py b/tasks/__init__.py index 01489d7b35d2d..b08d859797dc3 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -6,7 +6,6 @@ from tasks import ( agent, - agentless_scanner, bench, buildimages, cluster_agent, @@ -134,7 +133,6 @@ # add namespaced tasks to the root ns.add_collection(agent) -ns.add_collection(agentless_scanner) ns.add_collection(buildimages) ns.add_collection(cluster_agent) ns.add_collection(cluster_agent_cloudfoundry) diff --git a/tasks/agentless_scanner.py b/tasks/agentless_scanner.py deleted file mode 100644 index 0fb6cc19ef4d9..0000000000000 --- a/tasks/agentless_scanner.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Agentless-scanner tasks -""" - -import os -import sys - -from invoke import task -from invoke.exceptions import Exit - -from tasks.build_tags import filter_incompatible_tags, get_build_tags, get_default_build_tags -from tasks.flavor import AgentFlavor -from tasks.libs.common.utils import REPO_PATH, bin_name, get_build_flags - -# constants -AGENTLESS_SCANNER_BIN_PATH = os.path.join(".", "bin", "agentless-scanner") -STATIC_BIN_PATH = os.path.join(".", "bin", "static") - - -@task -def build( - ctx, - rebuild=False, - race=False, - static=False, - build_include=None, - build_exclude=None, - major_version='7', - go_mod="mod", -): - """ - Build Agentless-scanner - """ - build_include = ( - get_default_build_tags(build="agentless-scanner", flavor=AgentFlavor.agentless_scanner) - if build_include is None - else filter_incompatible_tags(build_include.split(",")) - ) - build_exclude = [] if build_exclude is None else build_exclude.split(",") - build_tags = get_build_tags(build_include, build_exclude) - ldflags, gcflags, env = get_build_flags(ctx, static=static, major_version=major_version) - bin_path = AGENTLESS_SCANNER_BIN_PATH - - if static: - bin_path = STATIC_BIN_PATH - - # NOTE: consider stripping symbols to reduce binary size - cmd = "go build -mod={go_mod} {race_opt} {build_type} -tags \"{build_tags}\" -o {bin_name} " - cmd += "-gcflags=\"{gcflags}\" -ldflags=\"{ldflags}\" {REPO_PATH}/cmd/agentless-scanner" - args = { - "go_mod": go_mod, - "race_opt": "-race" if race else "", - "build_type": "-a" if rebuild else "", - "build_tags": " ".join(build_tags), - "bin_name": os.path.join(bin_path, bin_name("agentless-scanner")), - "gcflags": gcflags, - "ldflags": ldflags, - "REPO_PATH": REPO_PATH, - } - ctx.run(cmd.format(**args), env=env) - - # Render the configuration file template - # - # We need to remove cross compiling bits if any because go generate must - # build and execute in the native platform - env = { - "GOOS": "", - "GOARCH": "", - } - cmd = "go generate -mod={} {}/cmd/agentless-scanner" - ctx.run(cmd.format(go_mod, REPO_PATH), env=env) - - if static and sys.platform.startswith("linux"): - cmd = "file {bin_name} " - args = { - "bin_name": os.path.join(bin_path, bin_name("agentless-scanner")), - } - result = ctx.run(cmd.format(**args)) - if "statically linked" not in result.stdout: - print("agentless-scanner binary is not static, exiting...") - raise Exit(code=1) - - -@task -def clean(ctx): - """ - Remove temporary objects and binary artifacts - """ - # go clean - print("Executing go clean") - ctx.run("go clean") - - # remove the bin/agentless-scanner folder - print("Remove agentless-scanner binary folder") - ctx.run("rm -rf ./bin/agentless-scanner") diff --git a/tasks/build_tags.py b/tasks/build_tags.py index 0c5b71e4a5354..e4c50a2ed7741 100644 --- a/tasks/build_tags.py +++ b/tasks/build_tags.py @@ -99,9 +99,6 @@ } ) -# AGENTLESS_SCANNER_TAGS lists the tags needed when building the agentless-scanner -AGENTLESS_SCANNER_TAGS = {""} - # CLUSTER_AGENT_TAGS lists the tags needed when building the cluster-agent CLUSTER_AGENT_TAGS = {"clusterchecks", "datadog.no_waf", "kubeapiserver", "orchestrator", "zlib", "zstd", "ec2", "gce"} @@ -216,12 +213,6 @@ "lint": DOGSTATSD_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), "unit-tests": DOGSTATSD_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), }, - AgentFlavor.agentless_scanner: { - "dogstatsd": AGENTLESS_SCANNER_TAGS, - "system-tests": AGENT_TAGS, - "lint": AGENTLESS_SCANNER_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), - "unit-tests": AGENTLESS_SCANNER_TAGS.union(UNIT_TEST_TAGS).difference(UNIT_TEST_EXCLUDE_TAGS), - }, } diff --git a/tasks/components.py b/tasks/components.py index 1bf94d91b44c8..3ba670bff997c 100644 --- a/tasks/components.py +++ b/tasks/components.py @@ -466,10 +466,6 @@ def lint_fxutil_oneshot_test(_): if str(file).endswith("_test.go"): continue - excluded_cmds = ["agentless-scanner"] - if file.parts[0] == "cmd" and file.parts[1] in excluded_cmds: - continue - one_shot_count = file.read_text().count("fxutil.OneShot(") run_count = file.read_text().count("fxutil.Run(") diff --git a/tasks/flavor.py b/tasks/flavor.py index 24e377f3f8a0e..16399f84821d3 100644 --- a/tasks/flavor.py +++ b/tasks/flavor.py @@ -7,7 +7,6 @@ class AgentFlavor(enum.Enum): iot = 2 heroku = 3 dogstatsd = 4 - agentless_scanner = 5 def is_iot(self): return self == type(self).iot diff --git a/tasks/libs/common/omnibus.py b/tasks/libs/common/omnibus.py index 86d930f92546e..d274ef87c1d8d 100644 --- a/tasks/libs/common/omnibus.py +++ b/tasks/libs/common/omnibus.py @@ -358,8 +358,6 @@ def install_dir_for_project(project): folder = 'datadog-agent' elif project == 'dogstatsd': folder = 'datadog-dogstatsd' - elif project == 'agentless-scanner': - folder = os.path.join('datadog', 'agentless-scanner') elif project == 'installer': folder = 'datadog-installer' else: diff --git a/tasks/unit-tests/testdata/fake_gitlab-ci.yml b/tasks/unit-tests/testdata/fake_gitlab-ci.yml index 22b8619387e90..714ed99e66363 100644 --- a/tasks/unit-tests/testdata/fake_gitlab-ci.yml +++ b/tasks/unit-tests/testdata/fake_gitlab-ci.yml @@ -113,7 +113,6 @@ variables: STATIC_BINARIES_DIR: bin/static DOGSTATSD_BINARIES_DIR: bin/dogstatsd AGENT_BINARIES_DIR: bin/agent - AGENTLESS_SCANNER_BINARIES_DIR: bin/agentless-scanner CLUSTER_AGENT_BINARIES_DIR: bin/datadog-cluster-agent CWS_INSTRUMENTATION_BINARIES_DIR: bin/cws-instrumentation CLUSTER_AGENT_CLOUDFOUNDRY_BINARIES_DIR: bin/datadog-cluster-agent-cloudfoundry From 35cd62f984396bd18200d56b91a5b874685b2fc3 Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 13:14:14 +0000 Subject: [PATCH 26/54] Bump the ulimit when running the `tidy` task if needed (#26127) * Bump the ulimit when running the 'tidy' task if needed * windows * comment --- tasks/go.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks/go.py b/tasks/go.py index 4b1434f7c24c1..ef89c4bb83078 100644 --- a/tasks/go.py +++ b/tasks/go.py @@ -411,6 +411,16 @@ def tidy_all(ctx): @task def tidy(ctx): + if os.name != 'nt': # not windows + import resource + + # Some people might face ulimit issues, so we bump it up if needed. + # It won't change it globally, only for this process and child processes. + # TODO: if this is working fine, let's do it during the init so all tasks can benefit from it if needed. + current_ulimit = resource.getrlimit(resource.RLIMIT_NOFILE) + if current_ulimit[0] < 1024: + resource.setrlimit(resource.RLIMIT_NOFILE, (1024, current_ulimit[1])) + # Note: It's currently faster to tidy everything than looking for exactly what we should tidy promises = [] for mod in DEFAULT_MODULES.values(): From d256cc3fc0b1c381194cf36922b9799f16443732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9lian=20Raimbault?= <161456554+CelianR@users.noreply.github.com> Date: Thu, 30 May 2024 15:15:48 +0200 Subject: [PATCH 27/54] [notify] Fix invalid links (#26128) * [fix-ci-visibility-job-url] Added get_ci_visibility_job_url function * [fix-ci-visibility-job-url] Updated pr link --- tasks/notify.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tasks/notify.py b/tasks/notify.py index edb3d4dd41e83..12e57ab2382a2 100644 --- a/tasks/notify.py +++ b/tasks/notify.py @@ -36,12 +36,26 @@ Please check for typos in the JOBOWNERS file and/or add them to the Github <-> Slack map. """ PROJECT_NAME = "DataDog/datadog-agent" +PROJECT_TITLE = PROJECT_NAME.removeprefix("DataDog/") AWS_S3_CP_CMD = "aws s3 cp --only-show-errors --region us-east-1 --sse AES256" S3_CI_BUCKET_URL = "s3://dd-ci-artefacts-build-stable/datadog-agent/failed_jobs" CONSECUTIVE_THRESHOLD = 3 CUMULATIVE_THRESHOLD = 5 CUMULATIVE_LENGTH = 10 -CI_VISIBILITY_JOB_URL = 'https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.pipeline.name%3ADataDog%2Fdatadog-agent%20%40git.branch%3Amain%20%40ci.job.name%3A"{}"&agg_m=count' +CI_VISIBILITY_JOB_URL = 'https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.pipeline.name%3ADataDog%2Fdatadog-agent%20%40git.branch%3Amain%20%40ci.job.name%3A{}&agg_m=count' + + +def get_ci_visibility_job_url(name: str, prefix=True) -> str: + # Escape (https://docs.datadoghq.com/logs/explorer/search_syntax/#escape-special-characters-and-spaces) + name = re.sub(r"([-+=&|> str: # Find initial PR initial_pr_sha = next(iter(self.failures.values()))[0].commit initial_pr_title = ctx.run(f'git show -s --format=%s {initial_pr_sha}', hide=True).stdout.strip() - initial_pr_info = get_pr_from_commit(initial_pr_title, PROJECT_NAME) + initial_pr_info = get_pr_from_commit(initial_pr_title, PROJECT_TITLE) if initial_pr_info: pr_id, pr_url = initial_pr_info - initial_pr = f'<{pr_url}|{pr_id}>' + initial_pr = f'<{pr_url}|#{pr_id}>' else: # Cannot find PR, display the commit sha initial_pr = initial_pr_sha[:8] @@ -465,7 +479,7 @@ def send_failure_summary_notification(_, jobs: dict[str, any] | None = None, lis message = ['*Daily Job Failure Report*'] message.append('These jobs had the most failures in the last 24 hours:') for name, (fail, total) in stats: - link = CI_VISIBILITY_JOB_URL.format(quote(name)) + link = get_ci_visibility_job_url(name) message.append(f"- <{link}|{name}>: *{fail} failures*{f' / {total} runs' if total else ''}") message.append( From db29679b30e44be7ca828aeb20a971ef56e63aba Mon Sep 17 00:00:00 2001 From: pducolin <45568537+pducolin@users.noreply.github.com> Date: Thu, 30 May 2024 15:15:54 +0200 Subject: [PATCH 28/54] Revert "[gitlab] add links to CI Visibility from e2e jobs (#26098)" (#26130) This reverts commit 8eb9fc097a94cd5d4861e311a1547ef155e20a52. --- .gitlab/e2e/e2e.yml | 7 ------- .gitlab/e2e/external_links.json.template | 16 ---------------- 2 files changed, 23 deletions(-) delete mode 100644 .gitlab/e2e/external_links.json.template diff --git a/.gitlab/e2e/e2e.yml b/.gitlab/e2e/e2e.yml index 2d43b18b7f964..2dcee6794fecb 100644 --- a/.gitlab/e2e/e2e.yml +++ b/.gitlab/e2e/e2e.yml @@ -111,10 +111,6 @@ k8s-e2e-otlp-main: - touch $E2E_PRIVATE_KEY_PATH && chmod 600 $E2E_PRIVATE_KEY_PATH && $CI_PROJECT_DIR/tools/ci/aws_ssm_get_wrapper.sh $SSH_KEY_RSA_SSM_NAME > $E2E_PRIVATE_KEY_PATH # Use S3 backend - pulumi login "s3://dd-pulumi-state?region=us-east-1&awssdk=v2&profile=$AWS_PROFILE" - # Generate external links to CI VISIBILITY, used by artifacts:reports:annotations - - cp .gitlab/e2e/external_links.json.template external_links_$CI_JOB_ID.json - - sed -i "s|{{CI_JOB_ID}}|$CI_JOB_ID|g" external_links_$CI_JOB_ID.json - - sed -i "s|{{CI_JOB_NAME}}|$CI_JOB_NAME|g" external_links_$CI_JOB_ID.json variables: KUBERNETES_MEMORY_REQUEST: 12Gi KUBERNETES_MEMORY_LIMIT: 16Gi @@ -137,9 +133,6 @@ k8s-e2e-otlp-main: - $E2E_OUTPUT_DIR # junit tarball, kept for investigations - junit-*.tgz - reports: - annotations: - - external_links_$CI_JOB_ID.json .new_e2e_template_needs_deb_x64: extends: .new_e2e_template diff --git a/.gitlab/e2e/external_links.json.template b/.gitlab/e2e/external_links.json.template deleted file mode 100644 index ecc632679767c..0000000000000 --- a/.gitlab/e2e/external_links.json.template +++ /dev/null @@ -1,16 +0,0 @@ -{ - "CI Visibility": [ - { - "external_link": { - "label": "🔗 CI Visibility: This job instance", - "url": "https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.job.id%3A{{CI_JOB_ID}}&agg_m=count&agg_m_source=base&agg_t=count&fromUser=false&index=cipipeline" - } - }, - { - "external_link": { - "label": "🔗 CI Visibility: This job on main", - "url": "https://app.datadoghq.com/ci/pipeline-executions?query=ci_level%3Ajob%20%40ci.job.name%3A%22{{CI_JOB_NAME}}%22%20%40git.branch%3Amain" - } - } - ] -} From ef82c5c69283ad7097d3faaddf83bec2ae742008 Mon Sep 17 00:00:00 2001 From: Adel Haj Hassan <41540817+adel121@users.noreply.github.com> Date: Thu, 30 May 2024 15:17:42 +0200 Subject: [PATCH 29/54] [CONTP-55] generic k8s metadata collection (#25901) * Add Kubernetes Metadata entity to workloadmeta store * implement metadata lister-watcher factory * define metadata collection configs and initialize reflector stores * fix linting * run inv generate-licenses * PR review --- LICENSE-3rdparty.csv | 1 + .../internal/kubeapiserver/kubeapiserver.go | 35 ++- .../kubeapiserver/kubeapiserver_test.go | 249 ++++++++++++++++++ .../internal/kubeapiserver/metadata.go | 82 ++++++ .../internal/kubeapiserver/metadata_test.go | 154 +++++++++++ .../internal/kubeapiserver/test_helpers.go | 84 +++++- .../internal/kubeapiserver/utils.go | 49 ++++ comp/core/workloadmeta/component.go | 4 + comp/core/workloadmeta/dump.go | 2 + comp/core/workloadmeta/store.go | 10 + comp/core/workloadmeta/store_test.go | 43 +++ comp/core/workloadmeta/types.go | 50 ++++ comp/core/workloadmeta/workloadmeta_mock.go | 10 + pkg/config/setup/config.go | 2 + pkg/util/kubernetes/apiserver/apiserver.go | 13 + 15 files changed, 783 insertions(+), 5 deletions(-) create mode 100644 comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata.go create mode 100644 comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata_test.go diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index cabb2fc4688ec..5a90ce299135c 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -4262,6 +4262,7 @@ core,k8s.io/client-go/listers/storage/v1,Apache-2.0,Copyright 2014 The Kubernete core,k8s.io/client-go/listers/storage/v1alpha1,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/client-go/listers/storage/v1beta1,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/client-go/metadata,Apache-2.0,Copyright 2014 The Kubernetes Authors. +core,k8s.io/client-go/metadata/fake,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/client-go/openapi,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/client-go/openapi/cached,Apache-2.0,Copyright 2014 The Kubernetes Authors. core,k8s.io/client-go/pkg/apis/clientauthentication,Apache-2.0,Copyright 2014 The Kubernetes Authors. diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver.go index f8b7e49a9b0c9..49292be61c2b8 100644 --- a/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver.go +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver.go @@ -14,11 +14,14 @@ import ( "github.com/DataDog/datadog-agent/comp/core/workloadmeta" "github.com/DataDog/datadog-agent/pkg/config" + "github.com/DataDog/datadog-agent/pkg/config/model" "github.com/DataDog/datadog-agent/pkg/status/health" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver" "github.com/DataDog/datadog-agent/pkg/util/log" "go.uber.org/fx" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/discovery" "k8s.io/client-go/kubernetes" "k8s.io/client-go/tools/cache" ) @@ -32,7 +35,7 @@ const ( // storeGenerator returns a new store specific to a given resource type storeGenerator func(context.Context, workloadmeta.Component, kubernetes.Interface) (*cache.Reflector, *reflectorStore) -func storeGenerators(cfg config.Reader) []storeGenerator { +func storeGenerators(cfg model.Reader) []storeGenerator { generators := []storeGenerator{newNodeStore} if cfg.GetBool("cluster_agent.collect_kubernetes_tags") || cfg.GetBool("autoscaling.workload.enabled") { @@ -50,6 +53,17 @@ func storeGenerators(cfg config.Reader) []storeGenerator { return generators } +func metadataCollectionGVRs(cfg model.Reader, discoveryClient discovery.DiscoveryInterface) ([]schema.GroupVersionResource, error) { + if !cfg.GetBool("cluster_agent.kube_metadata_collection.enabled") { + return []schema.GroupVersionResource{}, nil + } + + requestedResources := cfg.GetStringSlice("cluster_agent.kube_metadata_collection.resources") + + discoveredResourcesGVs, err := discoverGVRs(discoveryClient, requestedResources) + return discoveredResourcesGVs, err +} + type collector struct { id string catalog workloadmeta.AgentType @@ -79,6 +93,25 @@ func (c *collector) Start(ctx context.Context, wlmetaStore workloadmeta.Componen } client := apiserverClient.InformerCl + metadataclient, err := apiserverClient.MetadataClient() + if err != nil { + return err + } + + // Initialize metadata collection informers + // TODO(components): do not use the config.Datadog reference, use a component instead + gvrs, err := metadataCollectionGVRs(config.Datadog(), client.Discovery()) + + if err != nil { + log.Errorf("failed to discover Group and Version of requested resources: %v", err) + } else { + for _, gvr := range gvrs { + reflector, store := newMetadataStore(ctx, wlmetaStore, metadataclient, gvr) + objectStores = append(objectStores, store) + go reflector.Run(ctx.Done()) + } + } + // TODO(components): do not use the config.Datadog reference, use a component instead for _, storeBuilder := range storeGenerators(config.Datadog()) { reflector, store := storeBuilder(ctx, wlmetaStore, client) diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver_test.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver_test.go index f10b23bda0aa2..33b66cb25a5a9 100644 --- a/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver_test.go +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/kubeapiserver_test.go @@ -8,10 +8,16 @@ package kubeapiserver import ( + "reflect" "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + fakediscovery "k8s.io/client-go/discovery/fake" + fakeclientset "k8s.io/client-go/kubernetes/fake" "github.com/DataDog/datadog-agent/pkg/config" ) @@ -109,3 +115,246 @@ func collectResultStoreGenerator(funcs []storeGenerator) []*reflectorStore { } return stores } + +func Test_metadataCollectionGVRs_WithFunctionalDiscovery(t *testing.T) { + tests := []struct { + name string + apiServerResourceList []*metav1.APIResourceList + expectedGVRs []schema.GroupVersionResource + cfg map[string]interface{} + }{ + { + name: "no requested resources, no resources at all!", + apiServerResourceList: []*metav1.APIResourceList{}, + expectedGVRs: []schema.GroupVersionResource{}, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "", + }, + }, + { + name: "requested resources, but no resources at all!", + apiServerResourceList: []*metav1.APIResourceList{}, + expectedGVRs: []schema.GroupVersionResource{}, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "deployments", + }, + }, + { + name: "only one resource (deployments), only one version, correct resource requested", + apiServerResourceList: []*metav1.APIResourceList{ + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + }, + expectedGVRs: []schema.GroupVersionResource{{Resource: "deployments", Group: "apps", Version: "v1"}}, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "deployments", + }, + }, + { + name: "only one resource (deployments), only one version, wrong resource requested", + apiServerResourceList: []*metav1.APIResourceList{ + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + }, + expectedGVRs: []schema.GroupVersionResource{}, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "daemonsets", + }, + }, + { + name: "multiple resources (deployments, statefulsets), multiple versions, all resources requested", + apiServerResourceList: []*metav1.APIResourceList{ + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + }, + expectedGVRs: []schema.GroupVersionResource{ + {Resource: "deployments", Group: "apps", Version: "v1"}, + {Resource: "statefulsets", Group: "apps", Version: "v1"}, + }, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "deployments statefulsets", + }, + }, + { + name: "multiple resources (deployments, statefulsets), multiple versions, only one resource requested", + apiServerResourceList: []*metav1.APIResourceList{ + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + }, + expectedGVRs: []schema.GroupVersionResource{{Resource: "deployments", Group: "apps", Version: "v1"}}, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "deployments", + }, + }, + { + name: "multiple resources (deployments, statefulsets), multiple versions, two resources requested (one with a typo)", + apiServerResourceList: []*metav1.APIResourceList{ + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "deployments", + Kind: "Deployment", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + { + GroupVersion: "apps/v1beta1", + APIResources: []metav1.APIResource{ + { + Name: "statefulsets", + Kind: "StatefulSet", + Namespaced: true, + }, + }, + }, + }, + expectedGVRs: []schema.GroupVersionResource{ + {Resource: "deployments", Group: "apps", Version: "v1"}, + }, + cfg: map[string]interface{}{ + "cluster_agent.kube_metadata_collection.enabled": true, + "cluster_agent.kube_metadata_collection.resources": "deployments statefulsetsy", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(tt *testing.T) { + cfg := config.NewConfig("datadog", "DD", strings.NewReplacer(".", "_")) + for k, v := range test.cfg { + cfg.SetWithoutSource(k, v) + } + + client := fakeclientset.NewSimpleClientset() + fakeDiscoveryClient, ok := client.Discovery().(*fakediscovery.FakeDiscovery) + assert.Truef(t, ok, "Failed to initialise fake discovery client") + + fakeDiscoveryClient.Resources = test.apiServerResourceList + + discoveredGVRs, err := metadataCollectionGVRs(cfg, fakeDiscoveryClient) + require.NoErrorf(t, err, "Function should not have returned an error") + + assert.Truef(t, reflect.DeepEqual(discoveredGVRs, test.expectedGVRs), "Expected %v but got %v.", test.expectedGVRs, discoveredGVRs) + }) + } +} diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata.go new file mode 100644 index 0000000000000..b3a0c0aceb32c --- /dev/null +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata.go @@ -0,0 +1,82 @@ +// 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 + +package kubeapiserver + +import ( + "context" + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/metadata" + "k8s.io/client-go/tools/cache" + + "github.com/DataDog/datadog-agent/comp/core/workloadmeta" +) + +func newMetadataStore(ctx context.Context, wlmetaStore workloadmeta.Component, metadataclient metadata.Interface, gvr schema.GroupVersionResource) (*cache.Reflector, *reflectorStore) { + metadataListerWatcher := &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + return metadataclient.Resource(gvr).List(ctx, options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + return metadataclient.Resource(gvr).Watch(ctx, options) + }, + } + + metadataStore := &reflectorStore{ + wlmetaStore: wlmetaStore, + seen: make(map[string]workloadmeta.EntityID), + parser: newMetadataParser(gvr), + filter: nil, + } + metadataReflector := cache.NewNamedReflector( + componentName, + metadataListerWatcher, + &metav1.PartialObjectMetadata{}, + metadataStore, + noResync, + ) + return metadataReflector, metadataStore +} + +type metadataParser struct { + gvr schema.GroupVersionResource +} + +func newMetadataParser(gvr schema.GroupVersionResource) objectParser { + return metadataParser{gvr} +} + +// generateEntityID generates and returns a unique entity id for KubernetesMetadata entity +// for namespaced objects, the id will have the format {resourceType}/{namespace}/{name} (e.g. deployments/default/app ) +// for cluster scoped objects, the id will have the format {resourceType}//{name} (e.g. node//master-node) +func (p metadataParser) generateEntityID(resource, namespace, name string) string { + return fmt.Sprintf("%s/%s/%s", resource, namespace, name) +} + +func (p metadataParser) Parse(obj interface{}) workloadmeta.Entity { + partialObjectMetadata := obj.(*metav1.PartialObjectMetadata) + id := p.generateEntityID(p.gvr.Resource, partialObjectMetadata.Namespace, partialObjectMetadata.Name) + + return &workloadmeta.KubernetesMetadata{ + EntityID: workloadmeta.EntityID{ + Kind: workloadmeta.KindKubernetesMetadata, + ID: id, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: partialObjectMetadata.Name, + Namespace: partialObjectMetadata.Namespace, + Labels: partialObjectMetadata.Labels, + Annotations: partialObjectMetadata.Annotations, + }, + GVR: p.gvr, + } +} diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata_test.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata_test.go new file mode 100644 index 0000000000000..a33561cdc3f0b --- /dev/null +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/metadata_test.go @@ -0,0 +1,154 @@ +// 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 && test + +package kubeapiserver + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/DataDog/datadog-agent/comp/core/workloadmeta" + "github.com/DataDog/datadog-agent/pkg/util/kubernetes" +) + +func TestParse_ParsePartialObjectMetadata(t *testing.T) { + + testcases := []struct { + name string + gvr schema.GroupVersionResource + partialObjectMetadata *metav1.PartialObjectMetadata + expected *workloadmeta.KubernetesMetadata + }{ + { + name: "deployments [namespace scoped]", + gvr: schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + }, + partialObjectMetadata: &metav1.PartialObjectMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-app", + Namespace: "default", + Labels: map[string]string{"l1": "v1", "l2": "v2", "l3": "v3"}, + Annotations: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}, + }, + }, + expected: &workloadmeta.KubernetesMetadata{ + EntityID: workloadmeta.EntityID{ + Kind: workloadmeta.KindKubernetesMetadata, + ID: "deployments/default/test-app", + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: "test-app", + Namespace: "default", + Labels: map[string]string{"l1": "v1", "l2": "v2", "l3": "v3"}, + Annotations: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}, + }, + GVR: schema.GroupVersionResource{ + Group: "apps", + Version: "v1", + Resource: "deployments", + }, + }, + }, + { + name: "namespaces [cluster scoped]", + gvr: schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "namespaces", + }, + partialObjectMetadata: &metav1.PartialObjectMetadata{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-namespace", + Namespace: "", + Labels: map[string]string{"l1": "v1", "l2": "v2", "l3": "v3"}, + Annotations: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}, + }, + }, + expected: &workloadmeta.KubernetesMetadata{ + EntityID: workloadmeta.EntityID{ + Kind: workloadmeta.KindKubernetesMetadata, + ID: "namespaces//test-namespace", + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: "test-namespace", + Namespace: "", + Labels: map[string]string{"l1": "v1", "l2": "v2", "l3": "v3"}, + Annotations: map[string]string{"k1": "v1", "k2": "v2", "k3": "v3"}, + }, + GVR: schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "namespaces", + }, + }, + }, + } + + for _, test := range testcases { + t.Run(test.name, func(tt *testing.T) { + parser := newMetadataParser(test.gvr) + entity := parser.Parse(test.partialObjectMetadata) + storedMetadata, ok := entity.(*workloadmeta.KubernetesMetadata) + require.True(t, ok) + assert.Equal(t, test.expected, storedMetadata) + }) + } +} + +func Test_MetadataFakeClient(t *testing.T) { + ns := "default" + gvr := schema.GroupVersionResource{Group: "apps", Version: "v1", Resource: "deployments"} + objectMeta := metav1.ObjectMeta{ + Name: "test-app", + Namespace: ns, + Labels: map[string]string{"test-label": "test-value"}, + Annotations: map[string]string{"k": "v"}, + } + + createObjects := func() []runtime.Object { + return []runtime.Object{ + &metav1.PartialObjectMetadata{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: kubernetes.DeploymentKind, + }, + ObjectMeta: objectMeta, + }, + } + } + + expected := workloadmeta.EventBundle{ + Events: []workloadmeta.Event{ + { + Type: workloadmeta.EventTypeSet, + Entity: &workloadmeta.KubernetesMetadata{ + EntityID: workloadmeta.EntityID{ + ID: "deployments/default/test-app", + Kind: workloadmeta.KindKubernetesMetadata, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: objectMeta.Name, + Namespace: "default", + Labels: objectMeta.Labels, + Annotations: objectMeta.Annotations, + }, + GVR: gvr, + }, + }, + }, + } + + testCollectMetadataEvent(t, createObjects, gvr, expected) +} diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/test_helpers.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/test_helpers.go index 5aa166cbfb80f..c4e4e353276d1 100644 --- a/comp/core/workloadmeta/collectors/internal/kubeapiserver/test_helpers.go +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/test_helpers.go @@ -9,17 +9,22 @@ package kubeapiserver import ( "context" + "fmt" "testing" "time" + "github.com/stretchr/testify/assert" + "go.uber.org/fx" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/kubernetes/fake" + metafake "k8s.io/client-go/metadata/fake" + "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/workloadmeta" "github.com/DataDog/datadog-agent/pkg/util/fxutil" - - "github.com/stretchr/testify/assert" - "go.uber.org/fx" - "k8s.io/client-go/kubernetes/fake" ) const dummySubscriber = "dummy-subscriber" @@ -88,3 +93,74 @@ func testCollectEvent(t *testing.T, createResource func(*fake.Clientset) error, close(stopStore) wlm.Unsubscribe(ch) } + +func testCollectMetadataEvent(t *testing.T, createObjects func() []runtime.Object, gvr schema.GroupVersionResource, expected workloadmeta.EventBundle) { + + // Create a resource before starting the reflector store or workloadmeta so that if the reflector calls `List()` then + // this resource can't be skipped + + // Create test scheme + testScheme := runtime.NewScheme() + // Register Metadata objects types to the test scheme + err := v1.AddMetaToScheme(testScheme) + assert.NoError(t, err) + + objects := createObjects() + + metadataclient := metafake.NewSimpleMetadataClient(testScheme, objects...) + + wlm := fxutil.Test[workloadmeta.Mock](t, fx.Options( + core.MockBundle(), + fx.Supply(context.Background()), + fx.Supply(workloadmeta.NewParams()), + workloadmeta.MockModuleV2(), + )) + ctx := context.TODO() + + // Create a fake metadata client to mock API calls. + + response, err := metadataclient.Resource(gvr).List(ctx, v1.ListOptions{}) + assert.NoError(t, err) + fmt.Println("metadata client listing: ", response.String()) + store, _ := newMetadataStore(ctx, wlm, metadataclient, gvr) + + stopStore := make(chan struct{}) + go store.Run(stopStore) + + // Subscribe to the kubeapiserver events. Two cases are possible: + // - The reflector has already populated wlm with the resource, in that case the first call to <-ch will contain the event + // - The reflector is still initializing. In that case the second call to <-ch will contain the event + + time.Sleep(5 * time.Second) + + ch := wlm.Subscribe(dummySubscriber, workloadmeta.NormalPriority, nil) + var bundle workloadmeta.EventBundle + read := assert.Eventually(t, func() bool { + select { + case bundle = <-ch: + bundle.Acknowledge() + if len(bundle.Events) == 0 { + return false + } + // If bundle finally has an event, we can return from this + return true + + default: + return false + } + }, 30*time.Second, 500*time.Millisecond) + + // Retrieving the resource in an event bundle + if !read { + bundle = <-ch + bundle.Acknowledge() + } + + // nil the bundle's Ch so we can + // deep-equal just the events later + bundle.Ch = nil + actual := bundle + assert.Equal(t, expected, actual) + close(stopStore) + wlm.Unsubscribe(ch) +} diff --git a/comp/core/workloadmeta/collectors/internal/kubeapiserver/utils.go b/comp/core/workloadmeta/collectors/internal/kubeapiserver/utils.go index 6583cdfb51631..f9fb1261e489b 100644 --- a/comp/core/workloadmeta/collectors/internal/kubeapiserver/utils.go +++ b/comp/core/workloadmeta/collectors/internal/kubeapiserver/utils.go @@ -11,7 +11,11 @@ import ( "fmt" "regexp" + "k8s.io/apimachinery/pkg/runtime/schema" utilserror "k8s.io/apimachinery/pkg/util/errors" + "k8s.io/client-go/discovery" + + "github.com/DataDog/datadog-agent/pkg/util/log" ) func filterMapStringKey(mapInput map[string]string, keyFilters []*regexp.Regexp) map[string]string { @@ -52,3 +56,48 @@ func filterToRegex(filter string) (*regexp.Regexp, error) { } return r, nil } + +func discoverGVRs(discoveryClient discovery.DiscoveryInterface, resources []string) ([]schema.GroupVersionResource, error) { + discoveredResources, err := discoverResources(discoveryClient) + if err != nil { + return nil, err + } + + gvrs := make([]schema.GroupVersionResource, 0, len(resources)) + for _, resource := range resources { + gv, found := discoveredResources[resource] + if found { + gvrs = append(gvrs, schema.GroupVersionResource{Group: gv.Group, Version: gv.Version, Resource: resource}) + } else { + log.Errorf("failed to auto-discover group/version of resource %s,", resource) + } + } + + return gvrs, nil +} + +func discoverResources(discoveryClient discovery.DiscoveryInterface) (map[string]schema.GroupVersion, error) { + apiGroups, apiResourceLists, err := discoveryClient.ServerGroupsAndResources() + if err != nil { + return nil, err + } + + preferredGroupVersions := make(map[string]struct{}) + for _, group := range apiGroups { + preferredGroupVersions[group.PreferredVersion.GroupVersion] = struct{}{} + } + + discoveredResources := map[string]schema.GroupVersion{} + for _, resourceList := range apiResourceLists { + _, found := preferredGroupVersions[resourceList.GroupVersion] + if found { + for _, resource := range resourceList.APIResources { + // No need to handle error because we are sure it is correctly formatted + gv, _ := schema.ParseGroupVersion(resourceList.GroupVersion) + discoveredResources[resource.Name] = gv + } + } + } + + return discoveredResources, nil +} diff --git a/comp/core/workloadmeta/component.go b/comp/core/workloadmeta/component.go index fe5fdd9418808..7a4ac4a0002ba 100644 --- a/comp/core/workloadmeta/component.go +++ b/comp/core/workloadmeta/component.go @@ -82,6 +82,10 @@ type Component interface { // the entity with kind KindKubernetesNamespace and the given ID. GetKubernetesNamespace(id string) (*KubernetesNamespace, error) + // GetKubernetesMetadata returns metadata about a Kubernetes resource. It fetches + // the entity with kind KubernetesMetadata and the given ID. + GetKubernetesMetadata(id string) (*KubernetesMetadata, error) + // ListECSTasks returns metadata about all ECS tasks, equivalent to all // entities with kind KindECSTask. ListECSTasks() []*ECSTask diff --git a/comp/core/workloadmeta/dump.go b/comp/core/workloadmeta/dump.go index b136f64ca3f63..4da23e04ba51b 100644 --- a/comp/core/workloadmeta/dump.go +++ b/comp/core/workloadmeta/dump.go @@ -65,6 +65,8 @@ func (w *workloadmeta) Dump(verbose bool) WorkloadDumpResponse { info = e.String(verbose) case *KubernetesNamespace: info = e.String(verbose) + case *KubernetesMetadata: + info = e.String(verbose) default: return "", fmt.Errorf("unsupported type %T", e) } diff --git a/comp/core/workloadmeta/store.go b/comp/core/workloadmeta/store.go index c2f6eb0c897ad..66fe02f9b5498 100644 --- a/comp/core/workloadmeta/store.go +++ b/comp/core/workloadmeta/store.go @@ -406,6 +406,16 @@ func (w *workloadmeta) GetImage(id string) (*ContainerImageMetadata, error) { return entity.(*ContainerImageMetadata), nil } +// GetKubernetesMetadata implements Store#GetKubernetesMetadata. +func (w *workloadmeta) GetKubernetesMetadata(id string) (*KubernetesMetadata, error) { + entity, err := w.getEntityByKind(KindKubernetesMetadata, id) + if err != nil { + return nil, err + } + + return entity.(*KubernetesMetadata), nil +} + // Notify implements Store#Notify func (w *workloadmeta) Notify(events []CollectorEvent) { if len(events) > 0 { diff --git a/comp/core/workloadmeta/store_test.go b/comp/core/workloadmeta/store_test.go index 3836f0661bc06..f049e6b10b308 100644 --- a/comp/core/workloadmeta/store_test.go +++ b/comp/core/workloadmeta/store_test.go @@ -1420,6 +1420,49 @@ func TestResetProcesses(t *testing.T) { } +func TestGetKubernetesMetadata(t *testing.T) { + deps := fxutil.Test[dependencies](t, fx.Options( + logimpl.MockModule(), + config.MockModule(), + fx.Supply(NewParams()), + )) + + s := newWorkloadmetaObject(deps) + + kubemetadata := &KubernetesMetadata{ + EntityID: EntityID{ + Kind: KindKubernetesMetadata, + ID: "deployments/default/app", + }, + } + + s.handleEvents([]CollectorEvent{ + { + Type: EventTypeSet, + Source: fooSource, + Entity: kubemetadata, + }, + }) + + retrievedMetadata, err := s.GetKubernetesMetadata("deployments/default/app") + tassert.NoError(t, err) + + if !reflect.DeepEqual(kubemetadata, retrievedMetadata) { + t.Errorf("expected metadata %q to match the one in the store", retrievedMetadata.ID) + } + + s.handleEvents([]CollectorEvent{ + { + Type: EventTypeUnset, + Source: fooSource, + Entity: kubemetadata, + }, + }) + + _, err = s.GetKubernetesMetadata("deployments/default/app") + tassert.True(t, errors.IsNotFound(err)) +} + func TestReset(t *testing.T) { fooContainer := &Container{ EntityID: EntityID{ diff --git a/comp/core/workloadmeta/types.go b/comp/core/workloadmeta/types.go index c7e4a90307c97..5e8d175e954d4 100644 --- a/comp/core/workloadmeta/types.go +++ b/comp/core/workloadmeta/types.go @@ -11,6 +11,8 @@ import ( "strings" "time" + "k8s.io/apimachinery/pkg/runtime/schema" + langUtil "github.com/DataDog/datadog-agent/pkg/languagedetection/util" "github.com/CycloneDX/cyclonedx-go" @@ -41,6 +43,7 @@ const ( KindContainer Kind = "container" KindKubernetesPod Kind = "kubernetes_pod" KindKubernetesNode Kind = "kubernetes_node" + KindKubernetesMetadata Kind = "kubernetes_metadata" KindKubernetesDeployment Kind = "kubernetes_deployment" KindKubernetesNamespace Kind = "kubernetes_namespace" KindECSTask Kind = "ecs_task" @@ -785,6 +788,53 @@ func (o KubernetesPodOwner) String(verbose bool) string { return sb.String() } +// KubernetesMetadata is an Entity representing kubernetes resource metadata +type KubernetesMetadata struct { + EntityID + EntityMeta + GVR schema.GroupVersionResource +} + +// GetID implements Entity#GetID. +func (m *KubernetesMetadata) GetID() EntityID { + return m.EntityID +} + +// Merge implements Entity#Merge. +func (m *KubernetesMetadata) Merge(e Entity) error { + mm, ok := e.(*KubernetesMetadata) + if !ok { + return fmt.Errorf("cannot merge KubernetesMetadata with different kind %T", e) + } + + return merge(m, mm) +} + +// DeepCopy implements Entity#DeepCopy. +func (m KubernetesMetadata) DeepCopy() Entity { + cm := deepcopy.Copy(m).(KubernetesMetadata) + return &cm +} + +// String implements Entity#String +func (m *KubernetesMetadata) String(verbose bool) string { + var sb strings.Builder + _, _ = fmt.Fprintln(&sb, "----------- Entity ID -----------") + _, _ = fmt.Fprintln(&sb, m.EntityID.String(verbose)) + + _, _ = fmt.Fprintln(&sb, "----------- Entity Meta -----------") + _, _ = fmt.Fprint(&sb, m.EntityMeta.String(verbose)) + + if verbose { + _, _ = fmt.Fprintln(&sb, "----------- Resource -----------") + _, _ = fmt.Fprint(&sb, m.GVR.String()) + } + + return sb.String() +} + +var _ Entity = &KubernetesMetadata{} + // KubernetesNode is an Entity representing a Kubernetes Node. type KubernetesNode struct { EntityID diff --git a/comp/core/workloadmeta/workloadmeta_mock.go b/comp/core/workloadmeta/workloadmeta_mock.go index 0cd07c19e0870..1b56a8295be22 100644 --- a/comp/core/workloadmeta/workloadmeta_mock.go +++ b/comp/core/workloadmeta/workloadmeta_mock.go @@ -57,6 +57,16 @@ func (w *workloadMetaMock) GetContainer(id string) (*Container, error) { return entity.(*Container), nil } +// GetKubernetesMetadata implements workloadMetaMock#GetKubernetesMetadata. +func (w *workloadMetaMock) GetKubernetesMetadata(id string) (*KubernetesMetadata, error) { + entity, err := w.getEntityByKind(KindKubernetesMetadata, id) + if err != nil { + return nil, err + } + + return entity.(*KubernetesMetadata), nil +} + // ListContainers returns metadata about all known containers. func (w *workloadMetaMock) ListContainers() []*Container { entities := w.listEntitiesByKind(KindContainer) diff --git a/pkg/config/setup/config.go b/pkg/config/setup/config.go index 7c60bd3930564..3b4d6dbd52788 100644 --- a/pkg/config/setup/config.go +++ b/pkg/config/setup/config.go @@ -470,6 +470,8 @@ func InitConfig(config pkgconfigmodel.Config) { config.BindEnvAndSetDefault("cluster_agent.language_detection.cleanup.language_ttl", "30m") // language annotation cleanup period config.BindEnvAndSetDefault("cluster_agent.language_detection.cleanup.period", "10m") + config.BindEnvAndSetDefault("cluster_agent.kube_metadata_collection.enabled", false) + config.BindEnvAndSetDefault("cluster_agent.kube_metadata_collection.resources", []string{}) // Metadata endpoints diff --git a/pkg/util/kubernetes/apiserver/apiserver.go b/pkg/util/kubernetes/apiserver/apiserver.go index c5e18928ae327..8a0668a76d4ff 100644 --- a/pkg/util/kubernetes/apiserver/apiserver.go +++ b/pkg/util/kubernetes/apiserver/apiserver.go @@ -27,10 +27,12 @@ import ( vpa "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned" "k8s.io/client-go/discovery" "k8s.io/client-go/discovery/cached/memory" + "k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic/dynamicinformer" "k8s.io/client-go/informers" "k8s.io/client-go/kubernetes" + "k8s.io/client-go/metadata" "k8s.io/client-go/rest" "k8s.io/client-go/restmapper" "k8s.io/client-go/scale" @@ -660,6 +662,17 @@ func (c *APIClient) RESTClient(apiPath string, groupVersion *schema.GroupVersion return rest.RESTClientFor(clientConfig) } +// MetadataClient returns a new kubernetes metadata client +func (c *APIClient) MetadataClient() (metadata.Interface, error) { + clientConfig, err := getClientConfig(c.defaultInformerTimeout) + if err != nil { + return nil, err + } + + metaclient := metadata.NewForConfigOrDie(clientConfig) + return metaclient, nil +} + // NewSPDYExecutor returns a new SPDY executor for the provided method and URL func (c *APIClient) NewSPDYExecutor(apiPath string, groupVersion *schema.GroupVersion, negotiatedSerializer runtime.NegotiatedSerializer, method string, url *url.URL) (remotecommand.Executor, error) { clientConfig, err := getClientConfig(c.defaultClientTimeout) From ac461cdf813e13d86f24960a57a77f1661abf458 Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 13:39:23 +0000 Subject: [PATCH 30/54] Migrate the `check-protected-branch` hook to invoke (#26120) * Migrate the 'check-protected-branch' hook to invoke * Update tasks/unit-tests/git_tests.py Co-authored-by: Alexandre Menasria <47357713+amenasria@users.noreply.github.com> --------- Co-authored-by: Alexandre Menasria <47357713+amenasria@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- tasks/__init__.py | 2 ++ tasks/git-hooks/protected-branches.py | 23 ----------------- tasks/git.py | 20 +++++++++++++++ tasks/libs/common/git.py | 4 +++ tasks/unit-tests/git_tests.py | 30 +++++++++++++++++++++++ tasks/unit-tests/libs/common/git_tests.py | 9 ++++++- 7 files changed, 65 insertions(+), 25 deletions(-) delete mode 100644 tasks/git-hooks/protected-branches.py create mode 100644 tasks/git.py create mode 100644 tasks/unit-tests/git_tests.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eeb5496647fb7..07cfdf919868c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -33,7 +33,7 @@ repos: - id: protected-branches name: protected-branches description: checks that the commit isn't created on a protected branch - entry: 'python3 -m tasks.git-hooks.protected-branches' + entry: 'inv git.check-protected-branch' language: system pass_filenames: false - id: govet diff --git a/tasks/__init__.py b/tasks/__init__.py index b08d859797dc3..eb48c9bee323b 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -21,6 +21,7 @@ emacs, epforwarder, fakeintake, + git, github_tasks, go_deps, installer, @@ -148,6 +149,7 @@ ns.add_collection(go_deps) ns.add_collection(linter) ns.add_collection(msi) +ns.add_collection(git) ns.add_collection(github_tasks, "github") ns.add_collection(package) ns.add_collection(pipeline) diff --git a/tasks/git-hooks/protected-branches.py b/tasks/git-hooks/protected-branches.py deleted file mode 100644 index 992b1794178c9..0000000000000 --- a/tasks/git-hooks/protected-branches.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/python3 - -import re -import subprocess -import sys - - -def main(): - try: - local_branch = subprocess.check_output('git rev-parse --abbrev-ref HEAD', shell=True).decode('utf-8').strip() - if local_branch == 'main': - print("You're about to commit on main, are you sure this is what you want?") - sys.exit(1) - if re.fullmatch(r'^[0-9]+\.[0-9]+\.x$', local_branch): - print("You're about to commit on a release branch, are you sure this is what you want?") - sys.exit(1) - except OSError as e: - print(e) - sys.exit(1) - - -if __name__ == '__main__': - sys.exit(main()) diff --git a/tasks/git.py b/tasks/git.py new file mode 100644 index 0000000000000..003fb8e2f919a --- /dev/null +++ b/tasks/git.py @@ -0,0 +1,20 @@ +import re + +from invoke import task +from invoke.exceptions import Exit + +from tasks.libs.common.color import color_message +from tasks.libs.common.git import get_current_branch + + +@task +def check_protected_branch(ctx): + local_branch = get_current_branch(ctx) + + if local_branch == 'main': + print(color_message("You're about to commit on main, are you sure this is what you want?", "red")) + raise Exit(code=1) + + if re.fullmatch(r'^[0-9]+\.[0-9]+\.x$', local_branch): + print(color_message("You're about to commit on a release branch, are you sure this is what you want?", "red")) + raise Exit(code=1) diff --git a/tasks/libs/common/git.py b/tasks/libs/common/git.py index 6aef897289db2..a2954f5fbc497 100644 --- a/tasks/libs/common/git.py +++ b/tasks/libs/common/git.py @@ -8,3 +8,7 @@ def get_staged_files(ctx, commit="HEAD") -> list[str]: def get_modified_files(ctx) -> list[str]: last_main_commit = ctx.run("git merge-base HEAD origin/main", hide=True).stdout return ctx.run(f"git diff --name-only --no-renames {last_main_commit}", hide=True).stdout.splitlines() + + +def get_current_branch(ctx) -> str: + return ctx.run("git rev-parse --abbrev-ref HEAD", hide=True).stdout.strip() diff --git a/tasks/unit-tests/git_tests.py b/tasks/unit-tests/git_tests.py new file mode 100644 index 0000000000000..3bbccc59e6356 --- /dev/null +++ b/tasks/unit-tests/git_tests.py @@ -0,0 +1,30 @@ +import unittest + +from invoke.context import Context +from invoke.exceptions import Exit + +from tasks.git import check_protected_branch + + +class TestGit(unittest.TestCase): + def setUp(self) -> None: + super().setUp() + self.ctx = Context() + + @unittest.mock.patch('tasks.git.get_current_branch') + def test_check_protected_branch(self, get_current_branch_mock): + get_current_branch_mock.return_value = 'my/branch' + check_protected_branch(self.ctx) + + @unittest.mock.patch('tasks.git.get_current_branch') + def test_check_protected_branch_error(self, get_current_branch_mock): + protected_branches = ( + 'main', + '7.54.x', + '6.54.x', + ) + + for branch_name in protected_branches: + with self.subTest(branch=branch_name): + get_current_branch_mock.return_value = branch_name + self.assertRaises(Exit, check_protected_branch, self.ctx) diff --git a/tasks/unit-tests/libs/common/git_tests.py b/tasks/unit-tests/libs/common/git_tests.py index 951b472238ca4..2a43395394ee3 100644 --- a/tasks/unit-tests/libs/common/git_tests.py +++ b/tasks/unit-tests/libs/common/git_tests.py @@ -1,7 +1,7 @@ import unittest from unittest.mock import MagicMock -from tasks.libs.common.git import get_staged_files +from tasks.libs.common.git import get_current_branch, get_staged_files class TestGit(unittest.TestCase): @@ -15,3 +15,10 @@ def test_get_staged_files(self): self.assertEqual(files, ["file1", "file2", "file3"]) self.ctx_mock.run.assert_called_once_with("git diff --name-only --staged HEAD", hide=True) + + def test_get_current_branch(self): + self.ctx_mock.run.return_value.stdout = " main \n" + branch = get_current_branch(self.ctx_mock) + + self.assertEqual(branch, "main") + self.ctx_mock.run.assert_called_once_with("git rev-parse --abbrev-ref HEAD", hide=True) From 6da86505abfc974a1425a434874e353ecf543e43 Mon Sep 17 00:00:00 2001 From: Mackenzie <63265430+mackjmr@users.noreply.github.com> Date: Thu, 30 May 2024 15:47:28 +0200 Subject: [PATCH 31/54] OASIS-14: Make configprovider a component (#25713) * OASIS-14: Make configprovider a component * go version * go ver * Address Feedback * bump viper to match root go.mod * add copyright headers * versions * versions * lint * add package comment * go mod tidy * address feedback --- cmd/otel-agent/subcommands/run/command.go | 11 +- comp/README.md | 4 + comp/otelcol/provider/def/component.go | 24 ++ comp/otelcol/provider/def/go.mod | 92 +++++ comp/otelcol/provider/def/go.sum | 320 ++++++++++++++++++ comp/otelcol/provider/fx/fx.go | 19 ++ .../provider => provider/impl}/go.mod | 44 +-- .../provider => provider/impl}/go.sum | 80 ++--- .../provider => provider/impl}/provider.go | 23 +- .../impl}/provider_test.go | 27 +- .../impl}/testdata/dd/config-dd-result.yaml | 0 .../impl}/testdata/dd/config-dd.yaml | 0 .../impl}/testdata/nop/config-result.yaml | 0 .../impl}/testdata/nop/config.yaml | 0 go.mod | 39 ++- go.sum | 63 ++-- 16 files changed, 610 insertions(+), 136 deletions(-) create mode 100644 comp/otelcol/provider/def/component.go create mode 100644 comp/otelcol/provider/def/go.mod create mode 100644 comp/otelcol/provider/def/go.sum create mode 100644 comp/otelcol/provider/fx/fx.go rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/go.mod (91%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/go.sum (97%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/provider.go (89%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/provider_test.go (87%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/testdata/dd/config-dd-result.yaml (100%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/testdata/dd/config-dd.yaml (100%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/testdata/nop/config-result.yaml (100%) rename comp/otelcol/{otlp/components/pipeline/provider => provider/impl}/testdata/nop/config.yaml (100%) diff --git a/cmd/otel-agent/subcommands/run/command.go b/cmd/otel-agent/subcommands/run/command.go index 41c61dac6ff4d..6d3ce3a3c663b 100644 --- a/cmd/otel-agent/subcommands/run/command.go +++ b/cmd/otel-agent/subcommands/run/command.go @@ -32,7 +32,10 @@ import ( collectorfx "github.com/DataDog/datadog-agent/comp/otelcol/collector/fx" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline" "github.com/DataDog/datadog-agent/comp/otelcol/logsagentpipeline/logsagentpipelineimpl" - configprovider "github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/pipeline/provider" + "go.opentelemetry.io/collector/otelcol" + + provider "github.com/DataDog/datadog-agent/comp/otelcol/provider/def" + providerfx "github.com/DataDog/datadog-agent/comp/otelcol/provider/fx" "github.com/DataDog/datadog-agent/comp/serializer/compression" "github.com/DataDog/datadog-agent/comp/serializer/compression/compressionimpl/strategy" "github.com/DataDog/datadog-agent/pkg/config/env" @@ -40,7 +43,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/optional" "github.com/spf13/cobra" - "go.opentelemetry.io/collector/otelcol" "go.uber.org/fx" ) @@ -89,9 +91,8 @@ func runOTelAgentCommand(_ context.Context, params *subcommands.GlobalParams, op fetchonlyimpl.Module(), collectorfx.Module(), collectorcontribFx.Module(), - fx.Provide(configprovider.NewConfigProvider), - // For FX to provide the otelcol.ConfigProvider from the configprovider.ExtendedConfigProvider - fx.Provide(func(cp configprovider.ExtendedConfigProvider) otelcol.ConfigProvider { + providerfx.Module(), + fx.Provide(func(cp provider.Component) otelcol.ConfigProvider { return cp }), fx.Provide(func() (config.Component, error) { diff --git a/comp/README.md b/comp/README.md index f1a2a767bdcb7..51b02a7fe1d4d 100644 --- a/comp/README.md +++ b/comp/README.md @@ -368,6 +368,10 @@ Package collectorcontrib defines the OTel collector-contrib component Package logsagentpipeline contains logs agent pipeline component +### [comp/otelcol/provider](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/otelcol/provider) + + + ## [comp/process](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/process) (Component Bundle) *Datadog Team*: processes diff --git a/comp/otelcol/provider/def/component.go b/comp/otelcol/provider/def/component.go new file mode 100644 index 0000000000000..ec59df2ddb224 --- /dev/null +++ b/comp/otelcol/provider/def/component.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 2024-present Datadog, Inc. + +package provider + +import ( + "go.opentelemetry.io/collector/otelcol" +) + +// team: opentelemetry + +// Component implements the otelcol.ConfigProvider interface and +// provides extra functions to expose the provided and enhanced configs. +type Component interface { + otelcol.ConfigProvider + GetProvidedConf() string + GetEnhancedConf() string +} + +type Requires struct { + URIs []string +} diff --git a/comp/otelcol/provider/def/go.mod b/comp/otelcol/provider/def/go.mod new file mode 100644 index 0000000000000..bfca5c3d539a2 --- /dev/null +++ b/comp/otelcol/provider/def/go.mod @@ -0,0 +1,92 @@ +module github.com/DataDog/datadog-agent/comp/otelcol/provider/def + +go 1.21.0 + +require go.opentelemetry.io/collector/otelcol v0.100.0 + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/knadh/koanf/maps v0.1.1 // indirect + github.com/knadh/koanf/providers/confmap v0.1.0 // indirect + github.com/knadh/koanf/v2 v2.1.1 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.53.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/collector v0.100.0 // indirect + go.opentelemetry.io/collector/component v0.100.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.101.0 // indirect + go.opentelemetry.io/collector/confmap v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/converter/expandconverter v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/provider/envprovider v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/provider/fileprovider v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpprovider v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.100.0 // indirect + go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.100.0 // indirect + go.opentelemetry.io/collector/connector v0.100.0 // indirect + go.opentelemetry.io/collector/consumer v0.101.0 // indirect + go.opentelemetry.io/collector/exporter v0.100.0 // indirect + go.opentelemetry.io/collector/extension v0.100.0 // indirect + go.opentelemetry.io/collector/featuregate v1.8.0 // indirect + go.opentelemetry.io/collector/pdata v1.8.0 // indirect + go.opentelemetry.io/collector/processor v0.100.0 // indirect + go.opentelemetry.io/collector/receiver v0.100.0 // indirect + go.opentelemetry.io/collector/semconv v0.101.0 // indirect + go.opentelemetry.io/collector/service v0.100.0 // indirect + go.opentelemetry.io/contrib/config v0.6.0 // indirect + go.opentelemetry.io/contrib/propagators/b3 v1.26.0 // indirect + go.opentelemetry.io/otel v1.26.0 // indirect + go.opentelemetry.io/otel/bridge/opencensus v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.48.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.26.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 // indirect + go.opentelemetry.io/otel/metric v1.26.0 // indirect + go.opentelemetry.io/otel/sdk v1.26.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.26.0 // indirect + go.opentelemetry.io/otel/trace v1.26.0 // indirect + go.opentelemetry.io/proto/otlp v1.2.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.24.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/text v0.14.0 // indirect + gonum.org/v1/gonum v0.15.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/comp/otelcol/provider/def/go.sum b/comp/otelcol/provider/def/go.sum new file mode 100644 index 0000000000000..1235540b6a15c --- /dev/null +++ b/comp/otelcol/provider/def/go.sum @@ -0,0 +1,320 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= +github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= +github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= +github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= +github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= +github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= +github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= +github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/collector v0.100.0 h1:Q6IAGjMzjkZ7WepuwyCa6UytDPP0O88GemonQOUjP2s= +go.opentelemetry.io/collector v0.100.0/go.mod h1:QlVjQWlrPtBwVRm8tr+3P4FzNZSlYEfuUSaWoAwK+ko= +go.opentelemetry.io/collector/component v0.100.0 h1:3Y6dl3uDkDzilaikYrPxbZDOlzrDijrF1cIPzfyTwWA= +go.opentelemetry.io/collector/component v0.100.0/go.mod h1:HLEqEBFzPW2umagnVC3gY8yogOBhbzvuzTBFUqH54HY= +go.opentelemetry.io/collector/config/confignet v0.100.0 h1:SW8IMK+9GwFa1cNZdXw6wMkbCsqUaRtj0fgP8/yG6oI= +go.opentelemetry.io/collector/config/confignet v0.100.0/go.mod h1:3naWoPss70RhDHhYjGACi7xh4NcVRvs9itzIRVWyu1k= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0 h1:G9RerNdBUm6rYW6wrJoKzleBiDsCGaCjtQx5UYr0hzw= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= +go.opentelemetry.io/collector/confmap v0.100.0 h1:r70znwLWUMFRWL4LRcWLhdFfzmTvehXgbnlHFCDm0Tc= +go.opentelemetry.io/collector/confmap v0.100.0/go.mod h1:BWKPIpYeUzSG6ZgCJMjF7xsLvyrvJCfYURl57E5vhiQ= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.100.0 h1:xXPI9QzvwhefmVHNlSuq3WqmgXOrAVdaQAAdkAoMaEU= +go.opentelemetry.io/collector/confmap/converter/expandconverter v0.100.0/go.mod h1:zkADuTzmkmbsr281Xxw1jTy+YFrWr28pPIStt4nTZpo= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.100.0 h1:ORj/EN7HCuTl5MD8GoirShtz7ieIbAdseGroeRdgNDM= +go.opentelemetry.io/collector/confmap/provider/envprovider v0.100.0/go.mod h1:MMrNqxsveOPN/l6xifY8KK3kqfXdFOANIJQ4hkgNxN0= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.100.0 h1:H2YtC6mOFuaY7/ht16iUDYSHS6jM2ufNqQatmKAbf7U= +go.opentelemetry.io/collector/confmap/provider/fileprovider v0.100.0/go.mod h1:JEYr74rqYYsgKc5bDtsqy8TFHmmhHnEc0Hm5ekZSj/8= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.100.0 h1:GPFfS9BmuF0bzeW/lUElvI2NzHjAKq5U1E/lXLy/7pc= +go.opentelemetry.io/collector/confmap/provider/httpprovider v0.100.0/go.mod h1:s8YCUcFOOnY4NKeQjVTup6XiA3Oj3yqs8NB10kbwuAU= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.100.0 h1:vcOsDXQRdf/TUcD0vYTGkzvaPmoplG8Fkv8IoJ5cB58= +go.opentelemetry.io/collector/confmap/provider/httpsprovider v0.100.0/go.mod h1:GQ7LqkJKOcUFm8nxdV0P7eHPjLzVtNoBgqnwBGL4RCc= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.100.0 h1:a7qqKfczlB8wGQNeiGiSMiwCYRwTVdlIIUG98Jn5ydY= +go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.100.0/go.mod h1:IlcEhPYl3JDbl+5Bd9kWeDACHnvctdEnA7zlZCdtWk8= +go.opentelemetry.io/collector/connector v0.100.0 h1:6LN8LXhv553SjipKl7EbvlAECIZ9/v+vNxGiePp8dDQ= +go.opentelemetry.io/collector/connector v0.100.0/go.mod h1:yF4fkEtDrZOpwGOcnDTjkGjMCCQTPL2VCCBe9R43Jbg= +go.opentelemetry.io/collector/consumer v0.101.0 h1:9tDxaeHe1+Uovf3fhdx7T4pV5mo/Dc0hniH7O5H3RBA= +go.opentelemetry.io/collector/consumer v0.101.0/go.mod h1:ud5k64on9m7hHTrhjEeLhWbLkd8+Gp06rDt3p86TKNs= +go.opentelemetry.io/collector/exporter v0.100.0 h1:eyPb93tQwdft5Eboo8O5LDdaM1eXAQbtbXKBEYQlwh4= +go.opentelemetry.io/collector/exporter v0.100.0/go.mod h1:5UrDewyFp5yIQHyV7HUFAPdhHKJGbz1/uaTunm7X54I= +go.opentelemetry.io/collector/extension v0.100.0 h1:HT3h5JE+5xK3CCwF7VJKCOuZkLBMaUtm4T/BnEMpdWc= +go.opentelemetry.io/collector/extension v0.100.0/go.mod h1:B7jsEl6HAZB79NU41AdoMwLgXn4yTTO5NTlxRrsORoo= +go.opentelemetry.io/collector/extension/zpagesextension v0.100.0 h1:4bHq1NBg8hU+NSd9DBVFAl4vxi40J0tqClmU3IknrEg= +go.opentelemetry.io/collector/extension/zpagesextension v0.100.0/go.mod h1:mcLfCcjq0/yZoieuyO0H5rVL3u7Why0/MRezNV2E7AU= +go.opentelemetry.io/collector/featuregate v1.8.0 h1:p/bAuk5LiSfdYS88yFl/Jzao9bHEYqCh7YvZJ+L+IZg= +go.opentelemetry.io/collector/featuregate v1.8.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= +go.opentelemetry.io/collector/otelcol v0.100.0 h1:5NWoo9T5tHP0oWt3OHetYpTRaQCJuef8KDDe5tLi+BA= +go.opentelemetry.io/collector/otelcol v0.100.0/go.mod h1:pdPObjfQqP2pdE70jqQiJlZdEyZ0jA1euoRdHtgZjiA= +go.opentelemetry.io/collector/pdata v1.8.0 h1:d/QQgZxB4Y+d3mqLVh2ozvzujUhloD3P/fk7X+In764= +go.opentelemetry.io/collector/pdata v1.8.0/go.mod h1:/W7clu0wFC4WSRp94Ucn6Vm36Wkrt+tmtlDb1aiNZCY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0 h1:JzeUtg5RN1iIFgY8DakGlqBkGxOTJlkaYlLausnEGKY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0/go.mod h1:ZGobfCus4fWo5RduZ7ENI0+HD9BewgKuO6qU2rBVnUg= +go.opentelemetry.io/collector/processor v0.100.0 h1:8Zcd3v77SCSM5mAJbGes5aR/Yof3aY1csiwFhKFpLEQ= +go.opentelemetry.io/collector/processor v0.100.0/go.mod h1:ZqUC8WWVYyPkaLUT1JXUCNpCpde8zXgSaFfJq2FXuVU= +go.opentelemetry.io/collector/receiver v0.100.0 h1:RFeOVhS7o39G562w0H0hqfh1o2QvK71ViHQuWnnfglI= +go.opentelemetry.io/collector/receiver v0.100.0/go.mod h1:Qo3xkorbUy0VXHh7WxMQyphIWiqxI3ZOG0O4YqQ2mCE= +go.opentelemetry.io/collector/semconv v0.101.0 h1:tOe9iTe9dDCnvz/bqgfNRr4w80kXG8505tQJ5h5v08Q= +go.opentelemetry.io/collector/semconv v0.101.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= +go.opentelemetry.io/collector/service v0.100.0 h1:2LFvNQNSs2NnwElyqkyhAiqaGoDdiMnTQeFPCLZNgg0= +go.opentelemetry.io/collector/service v0.100.0/go.mod h1:65NPZ6THkR/e7fd8vh+tw4Lh6iDJ1twNXVzL76a3VNk= +go.opentelemetry.io/contrib/config v0.6.0 h1:M1SRD1Z15XHPGk61tMLI1up77XT5FdrqQSRrlH0fYuk= +go.opentelemetry.io/contrib/config v0.6.0/go.mod h1:t+/kzmRWLN7J+4F/dD4fFvlYCmCO63WYwy/B00IC++c= +go.opentelemetry.io/contrib/propagators/b3 v1.26.0 h1:wgFbVA+bK2k+fGVfDOCOG4cfDAoppyr5sI2dVlh8MWM= +go.opentelemetry.io/contrib/propagators/b3 v1.26.0/go.mod h1:DDktFXxA+fyItAAM0Sbl5OBH7KOsCTjvbBdPKtoIf/k= +go.opentelemetry.io/contrib/zpages v0.51.0 h1:psVr4JTWd0qtISPj9EA6AODGJ09bvsOxWiuKqiGdSCA= +go.opentelemetry.io/contrib/zpages v0.51.0/go.mod h1:PKtp+NEp1gTTLmFHpynYgYCSkKtisPntOb9S1mQjFKg= +go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= +go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= +go.opentelemetry.io/otel/bridge/opencensus v1.26.0 h1:DZzxj9QjznMVoehskOJnFP2gsTCWtDTFBDvFhPAY7nc= +go.opentelemetry.io/otel/bridge/opencensus v1.26.0/go.mod h1:rJiX0KrF5m8Tm1XE8jLczpAv5zUaDcvhKecFG0ZoFG4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0 h1:+hm+I+KigBy3M24/h1p/NHkUx/evbLH0PNcjpMyCHc4= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.26.0/go.mod h1:NjC8142mLvvNT6biDpaMjyz78kyEHIwAJlSX0N9P5KI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0 h1:HGZWGmCVRCVyAs2GQaiHQPbDHo+ObFWeUEOd+zDnp64= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.26.0/go.mod h1:SaH+v38LSCHddyk7RGlU9uZyQoRrKao6IBnJw6Kbn+c= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0 h1:1wp/gyxsuYtuE/JFxsQRtcCDtMrO2qMvlfXALU5wkzI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.26.0/go.mod h1:gbTHmghkGgqxMomVQQMur1Nba4M0MQ8AYThXDUjsJ38= +go.opentelemetry.io/otel/exporters/prometheus v0.48.0 h1:sBQe3VNGUjY9IKWQC6z2lNqa5iGbDSxhs60ABwK4y0s= +go.opentelemetry.io/otel/exporters/prometheus v0.48.0/go.mod h1:DtrbMzoZWwQHyrQmCfLam5DZbnmorsGbOtTbYHycU5o= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.26.0 h1:5fnmgteaar1VcAA69huatudPduNFz7guRtCmfZCooZI= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.26.0/go.mod h1:lsPccfZiz1cb1AhBPmicWM2E4F1VynFXEvD8SEBS4TM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0 h1:0W5o9SzoR15ocYHEQfvfipzcNog1lBxOLfnex91Hk6s= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.26.0/go.mod h1:zVZ8nz+VSggWmnh6tTsJqXQ7rU4xLwRtna1M4x5jq58= +go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30= +go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4= +go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8= +go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs= +go.opentelemetry.io/otel/sdk/metric v1.26.0 h1:cWSks5tfriHPdWFnl+qpX3P681aAYqlZHcAyHw5aU9Y= +go.opentelemetry.io/otel/sdk/metric v1.26.0/go.mod h1:ClMFFknnThJCksebJwz7KIyEDHO+nTB6gK8obLy8RyE= +go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= +go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= +go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94= +go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ= +gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de h1:F6qOa9AZTYJXOUEr4jDysRDLrm4PHePlge4v4TGAlxY= +google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:VUhTRKeHn9wwcdrk73nvdC9gF178Tzhmt/qyaFcPLSo= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0= +google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/comp/otelcol/provider/fx/fx.go b/comp/otelcol/provider/fx/fx.go new file mode 100644 index 0000000000000..391e7eaeb3635 --- /dev/null +++ b/comp/otelcol/provider/fx/fx.go @@ -0,0 +1,19 @@ +// 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 2024-present Datadog, Inc. + +// Package providerfx provides fx access for the provider component +package providerfx + +import ( + providerimpl "github.com/DataDog/datadog-agent/comp/otelcol/provider/impl" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +// Module defines the fx options for this component. +func Module() fxutil.Module { + return fxutil.Component( + fxutil.ProvideComponentConstructor(providerimpl.NewConfigProvider), + ) +} diff --git a/comp/otelcol/otlp/components/pipeline/provider/go.mod b/comp/otelcol/provider/impl/go.mod similarity index 91% rename from comp/otelcol/otlp/components/pipeline/provider/go.mod rename to comp/otelcol/provider/impl/go.mod index dd36344c5d0e2..bdcf7cb9ddd43 100644 --- a/comp/otelcol/otlp/components/pipeline/provider/go.mod +++ b/comp/otelcol/provider/impl/go.mod @@ -1,11 +1,13 @@ -module provider +module github.com/DataDog/datadog-agent/comp/otelcol/provider/impl go 1.21.0 +replace github.com/DataDog/datadog-agent/comp/otelcol/provider/def => ../def + require ( + github.com/DataDog/datadog-agent/comp/otelcol/provider/def v0.0.0-00010101000000-000000000000 github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter v0.100.0 github.com/stretchr/testify v1.9.0 - go.opentelemetry.io/collector/component v0.100.0 go.opentelemetry.io/collector/confmap v0.100.0 go.opentelemetry.io/collector/confmap/converter/expandconverter v0.100.0 go.opentelemetry.io/collector/confmap/provider/envprovider v0.100.0 @@ -87,7 +89,7 @@ require ( github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 // indirect github.com/DataDog/sketches-go v1.4.4 // indirect - github.com/DataDog/viper v1.13.2 // indirect + github.com/DataDog/viper v1.13.4 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.22.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect @@ -169,13 +171,13 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect - github.com/prometheus/client_golang v1.19.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.53.0 // indirect github.com/prometheus/procfs v0.14.0 // indirect github.com/rs/cors v1.10.1 // indirect github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect - github.com/shirou/gopsutil/v3 v3.24.3 // indirect + github.com/shirou/gopsutil/v3 v3.24.4 // indirect github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/spf13/afero v1.10.0 // indirect github.com/spf13/cast v1.5.1 // indirect @@ -190,21 +192,22 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/collector v0.100.0 // indirect + go.opentelemetry.io/collector/component v0.100.0 // indirect go.opentelemetry.io/collector/config/configauth v0.100.0 // indirect go.opentelemetry.io/collector/config/configcompression v1.7.0 // indirect go.opentelemetry.io/collector/config/confighttp v0.100.0 // indirect - go.opentelemetry.io/collector/config/confignet v0.100.0 // indirect + go.opentelemetry.io/collector/config/confignet v0.101.0 // indirect go.opentelemetry.io/collector/config/configopaque v1.7.0 // indirect - go.opentelemetry.io/collector/config/configretry v0.100.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.100.0 // indirect + go.opentelemetry.io/collector/config/configretry v0.101.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.101.0 // indirect go.opentelemetry.io/collector/config/configtls v0.100.0 // indirect go.opentelemetry.io/collector/config/internal v0.100.0 // indirect - go.opentelemetry.io/collector/consumer v0.100.0 // indirect + go.opentelemetry.io/collector/consumer v0.101.0 // indirect go.opentelemetry.io/collector/extension/auth v0.100.0 // indirect - go.opentelemetry.io/collector/featuregate v1.7.0 // indirect - go.opentelemetry.io/collector/pdata v1.7.0 // indirect - go.opentelemetry.io/collector/pdata/testdata v0.100.0 // indirect - go.opentelemetry.io/collector/semconv v0.100.0 // indirect + go.opentelemetry.io/collector/featuregate v1.8.0 // indirect + go.opentelemetry.io/collector/pdata v1.8.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.101.0 // indirect + go.opentelemetry.io/collector/semconv v0.101.0 // indirect go.opentelemetry.io/collector/service v0.100.0 // indirect go.opentelemetry.io/contrib/config v0.6.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect @@ -229,20 +232,21 @@ require ( go.uber.org/fx v1.18.2 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.24.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.19.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/term v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/time v0.5.0 // indirect - golang.org/x/tools v0.19.0 // indirect + golang.org/x/tools v0.21.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/grpc v1.63.2 // indirect - google.golang.org/protobuf v1.34.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/zorkian/go-datadog-api.v2 v2.30.0 // indirect diff --git a/comp/otelcol/otlp/components/pipeline/provider/go.sum b/comp/otelcol/provider/impl/go.sum similarity index 97% rename from comp/otelcol/otlp/components/pipeline/provider/go.sum rename to comp/otelcol/provider/impl/go.sum index df3e8ab7bb381..734630bf05831 100644 --- a/comp/otelcol/otlp/components/pipeline/provider/go.sum +++ b/comp/otelcol/provider/impl/go.sum @@ -193,8 +193,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.16.0 h1:Jl7/oQQ github.com/DataDog/opentelemetry-mapping-go/pkg/inframetadata v0.16.0/go.mod h1:P/l++2cDCeeq21KSmCEdXdMH9/WMdXP7uA/vjnxhtz8= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0 h1:VJT1Jjlz/ca999FEqaAS+He7S4eB14a+PJjczgRdgAY= github.com/DataDog/opentelemetry-mapping-go/pkg/internal/sketchtest v0.16.0/go.mod h1:66XlN7QpQKqIvw8e2UbCXV5X8wGnEw851nT9BjJ75dY= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0 h1:g/ztrLYZNfkpW6Bt8kMnLed5DaKRHEtiKE0opHXLHJk= -github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3CgdMXpDplrf9/gIr13+/g/tUzUcBMk2ZhXgzLE= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0 h1:NbKlfbjR2joF52jEBLs3MEnT6l5zM3MCyhUFkqARZpk= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0/go.mod h1:+LijQ2LdlocAQ4WB+7KsoIGe90bfogkRslubd9swVow= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 h1:H5DzD3rwgQCX0VI3A16KgsdmC5grUCyDFflaZDpfgMc= @@ -203,8 +203,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 h1:BdfRSuCoHyKa github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0/go.mod h1:6eYyd+lJYH+uRuZqhyW/u+9ykaXBWetDGj44+txz6jU= github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= -github.com/DataDog/viper v1.13.2 h1:GrYzwGiaEoliIXA4wPkx8MHIRY5sNi8frV1Fsv7VCJU= -github.com/DataDog/viper v1.13.2/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= +github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.22.0 h1:PWcDbDjrcT/ZHLn4Bc/FuglaZZVPP8bWO/YRmJBbe38= @@ -755,8 +755,8 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= -github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -793,8 +793,8 @@ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.25/go.mod h1:fCa7OJZ/9DRTnOKmxvT github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= github.com/shirou/gopsutil/v3 v3.22.12/go.mod h1:Xd7P1kwZcp5VW52+9XsirIKd/BROzbb2wdX3Kqlz9uI= -github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= -github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= @@ -899,14 +899,14 @@ go.opentelemetry.io/collector/config/configgrpc v0.100.0 h1:+RuWrysXLbjaf/+I7dU9 go.opentelemetry.io/collector/config/configgrpc v0.100.0/go.mod h1:NDKPjtEVL7TJMfByR/D5MYyjveUU3D0GkM96jU0u494= go.opentelemetry.io/collector/config/confighttp v0.100.0 h1:bkB8ZkkRL+N75QofuIosf2ZzkEYaBAA5C+eQpL4fOis= go.opentelemetry.io/collector/config/confighttp v0.100.0/go.mod h1:AaugDfPoHeOmFT2BICuGNp3ja3Sq1AcTxxw4WysFZsI= -go.opentelemetry.io/collector/config/confignet v0.100.0 h1:SW8IMK+9GwFa1cNZdXw6wMkbCsqUaRtj0fgP8/yG6oI= -go.opentelemetry.io/collector/config/confignet v0.100.0/go.mod h1:3naWoPss70RhDHhYjGACi7xh4NcVRvs9itzIRVWyu1k= +go.opentelemetry.io/collector/config/confignet v0.101.0 h1:Mdb9e/EpCSac4Ccg7w4UchS/o4yY1WoIc9X5o7fTu9E= +go.opentelemetry.io/collector/config/confignet v0.101.0/go.mod h1:3naWoPss70RhDHhYjGACi7xh4NcVRvs9itzIRVWyu1k= go.opentelemetry.io/collector/config/configopaque v1.7.0 h1:nZh5Hb1ofq9xP1wHLSt4obM85pRTccSeAjV0NbrJeTc= go.opentelemetry.io/collector/config/configopaque v1.7.0/go.mod h1:vxoDKYYYUF/arrdQJxmfhlgkcsb0DpdzC9KPFP97uuE= -go.opentelemetry.io/collector/config/configretry v0.100.0 h1:jEswHFjNokqJ0U2iYSzUlDy8N6A6D+zaoHM9t1TB6yw= -go.opentelemetry.io/collector/config/configretry v0.100.0/go.mod h1:uRdmPeCkrW9Zsadh2WEbQ1AGXGYJ02vCfmmT+0g69nY= -go.opentelemetry.io/collector/config/configtelemetry v0.100.0 h1:unlhNrFFXCinxk6iPHPYwANO+eFY4S1NTb5knSxteW4= -go.opentelemetry.io/collector/config/configtelemetry v0.100.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= +go.opentelemetry.io/collector/config/configretry v0.101.0 h1:5QggLq/lZiZXry1Ut52IOTbrdz1RbGoL29Io/wWdE4g= +go.opentelemetry.io/collector/config/configretry v0.101.0/go.mod h1:uRdmPeCkrW9Zsadh2WEbQ1AGXGYJ02vCfmmT+0g69nY= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0 h1:G9RerNdBUm6rYW6wrJoKzleBiDsCGaCjtQx5UYr0hzw= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= go.opentelemetry.io/collector/config/configtls v0.100.0 h1:qcx8EXW4u+IQvyt8ZH5ld2dEns1zp8sugyM+s7RuiKY= go.opentelemetry.io/collector/config/configtls v0.100.0/go.mod h1:f8KZu6P8hIzTfybLKG3xMIzkCmXyjxVUfDTVUp2CmhA= go.opentelemetry.io/collector/config/internal v0.100.0 h1:XSbedIpdXOxIEGnnzCZnulTmWPSGWfXTH18ZMxuqt8s= @@ -927,8 +927,8 @@ go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.100.0 h1:a7qqKfcz go.opentelemetry.io/collector/confmap/provider/yamlprovider v0.100.0/go.mod h1:IlcEhPYl3JDbl+5Bd9kWeDACHnvctdEnA7zlZCdtWk8= go.opentelemetry.io/collector/connector v0.100.0 h1:6LN8LXhv553SjipKl7EbvlAECIZ9/v+vNxGiePp8dDQ= go.opentelemetry.io/collector/connector v0.100.0/go.mod h1:yF4fkEtDrZOpwGOcnDTjkGjMCCQTPL2VCCBe9R43Jbg= -go.opentelemetry.io/collector/consumer v0.100.0 h1:8sALAcWvizSyrZJCF+zTqD2RLmZAyeCuaQrNS2q6ti0= -go.opentelemetry.io/collector/consumer v0.100.0/go.mod h1:JOPOq8nSTdnQwc2xdHl4hcuYBYV8gjN2SlFqlqBe/Nc= +go.opentelemetry.io/collector/consumer v0.101.0 h1:9tDxaeHe1+Uovf3fhdx7T4pV5mo/Dc0hniH7O5H3RBA= +go.opentelemetry.io/collector/consumer v0.101.0/go.mod h1:ud5k64on9m7hHTrhjEeLhWbLkd8+Gp06rDt3p86TKNs= go.opentelemetry.io/collector/exporter v0.100.0 h1:eyPb93tQwdft5Eboo8O5LDdaM1eXAQbtbXKBEYQlwh4= go.opentelemetry.io/collector/exporter v0.100.0/go.mod h1:5UrDewyFp5yIQHyV7HUFAPdhHKJGbz1/uaTunm7X54I= go.opentelemetry.io/collector/extension v0.100.0 h1:HT3h5JE+5xK3CCwF7VJKCOuZkLBMaUtm4T/BnEMpdWc= @@ -937,16 +937,16 @@ go.opentelemetry.io/collector/extension/auth v0.100.0 h1:Z8QVtntWiORnbVSCQfOxtnO go.opentelemetry.io/collector/extension/auth v0.100.0/go.mod h1:nkqaVzUAdqqkUGdMqoIqH/xlGU0rCxRZy1Altyz0gQk= go.opentelemetry.io/collector/extension/zpagesextension v0.100.0 h1:4bHq1NBg8hU+NSd9DBVFAl4vxi40J0tqClmU3IknrEg= go.opentelemetry.io/collector/extension/zpagesextension v0.100.0/go.mod h1:mcLfCcjq0/yZoieuyO0H5rVL3u7Why0/MRezNV2E7AU= -go.opentelemetry.io/collector/featuregate v1.7.0 h1:8tNgX2VaiR9jrpZevRSvStuJrvvL6WwScT264HNLk7U= -go.opentelemetry.io/collector/featuregate v1.7.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= +go.opentelemetry.io/collector/featuregate v1.8.0 h1:p/bAuk5LiSfdYS88yFl/Jzao9bHEYqCh7YvZJ+L+IZg= +go.opentelemetry.io/collector/featuregate v1.8.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= go.opentelemetry.io/collector/filter v0.100.0 h1:XQyhnqJSK2sw+e9yvpkvl7y8QdJwH/gAnFoZDfEZ0dQ= go.opentelemetry.io/collector/filter v0.100.0/go.mod h1:3xGRpZo11DMJTDtMUGsDNkxKM6LMHqROGrQ/aTvskh8= go.opentelemetry.io/collector/otelcol v0.100.0 h1:5NWoo9T5tHP0oWt3OHetYpTRaQCJuef8KDDe5tLi+BA= go.opentelemetry.io/collector/otelcol v0.100.0/go.mod h1:pdPObjfQqP2pdE70jqQiJlZdEyZ0jA1euoRdHtgZjiA= -go.opentelemetry.io/collector/pdata v1.7.0 h1:/WNsBbE6KM3TTPUb9v/5B7IDqnDkgf8GyFhVJJqu7II= -go.opentelemetry.io/collector/pdata v1.7.0/go.mod h1:ehCBBA5GoFrMZkwyZAKGY/lAVSgZf6rzUt3p9mddmPU= -go.opentelemetry.io/collector/pdata/testdata v0.100.0 h1:pliojioiAv+CuLNTK+8tnCD2UgiJbKX9q8bDnpHkV1U= -go.opentelemetry.io/collector/pdata/testdata v0.100.0/go.mod h1:01BHOXvXaQaLLt5J34S093u3e+j//RhbfmEujpFJ/ME= +go.opentelemetry.io/collector/pdata v1.8.0 h1:d/QQgZxB4Y+d3mqLVh2ozvzujUhloD3P/fk7X+In764= +go.opentelemetry.io/collector/pdata v1.8.0/go.mod h1:/W7clu0wFC4WSRp94Ucn6Vm36Wkrt+tmtlDb1aiNZCY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0 h1:JzeUtg5RN1iIFgY8DakGlqBkGxOTJlkaYlLausnEGKY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0/go.mod h1:ZGobfCus4fWo5RduZ7ENI0+HD9BewgKuO6qU2rBVnUg= go.opentelemetry.io/collector/processor v0.100.0 h1:8Zcd3v77SCSM5mAJbGes5aR/Yof3aY1csiwFhKFpLEQ= go.opentelemetry.io/collector/processor v0.100.0/go.mod h1:ZqUC8WWVYyPkaLUT1JXUCNpCpde8zXgSaFfJq2FXuVU= go.opentelemetry.io/collector/processor/batchprocessor v0.100.0 h1:N94WWv+o9yt4HqvFeRUjL8VNEhY8mCym+2XPKUWxjmo= @@ -957,8 +957,8 @@ go.opentelemetry.io/collector/receiver v0.100.0 h1:RFeOVhS7o39G562w0H0hqfh1o2QvK go.opentelemetry.io/collector/receiver v0.100.0/go.mod h1:Qo3xkorbUy0VXHh7WxMQyphIWiqxI3ZOG0O4YqQ2mCE= go.opentelemetry.io/collector/receiver/otlpreceiver v0.100.0 h1:JuTBkWQ0xc8FargDe5aE6wCoAEk1gxLnBzfdIi8NOL8= go.opentelemetry.io/collector/receiver/otlpreceiver v0.100.0/go.mod h1:X2xzbz9K4Kz0i3c1IKa1gcwf7rpoJDidyp28A1AuHFs= -go.opentelemetry.io/collector/semconv v0.100.0 h1:QArUvWcbmsMjM4PV0zngUHRizZeUXibsPBWjDuNJXAs= -go.opentelemetry.io/collector/semconv v0.100.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= +go.opentelemetry.io/collector/semconv v0.101.0 h1:tOe9iTe9dDCnvz/bqgfNRr4w80kXG8505tQJ5h5v08Q= +go.opentelemetry.io/collector/semconv v0.101.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= go.opentelemetry.io/collector/service v0.100.0 h1:2LFvNQNSs2NnwElyqkyhAiqaGoDdiMnTQeFPCLZNgg0= go.opentelemetry.io/collector/service v0.100.0/go.mod h1:65NPZ6THkR/e7fd8vh+tw4Lh6iDJ1twNXVzL76a3VNk= go.opentelemetry.io/contrib/config v0.6.0 h1:M1SRD1Z15XHPGk61tMLI1up77XT5FdrqQSRrlH0fYuk= @@ -1032,8 +1032,8 @@ golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1044,8 +1044,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ= -golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1115,8 +1115,8 @@ golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1197,14 +1197,14 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1213,8 +1213,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1278,8 +1278,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw= -golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1389,8 +1389,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/comp/otelcol/otlp/components/pipeline/provider/provider.go b/comp/otelcol/provider/impl/provider.go similarity index 89% rename from comp/otelcol/otlp/components/pipeline/provider/provider.go rename to comp/otelcol/provider/impl/provider.go index 0b449787e169c..f2a04036b8998 100644 --- a/comp/otelcol/otlp/components/pipeline/provider/provider.go +++ b/comp/otelcol/provider/impl/provider.go @@ -3,13 +3,14 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2024-present Datadog, Inc. -package provider +package providerimpl import ( "context" "fmt" "os" + provider "github.com/DataDog/datadog-agent/comp/otelcol/provider/def" "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/confmap/converter/expandconverter" "go.opentelemetry.io/collector/confmap/provider/envprovider" @@ -21,14 +22,6 @@ import ( "gopkg.in/yaml.v3" ) -// ExtendedConfigProvider implements the otelcol.ConfigProvider interface and -// provides extra functions to expose the provided and enhanced configs. -type ExtendedConfigProvider interface { - otelcol.ConfigProvider - GetProvidedConf() string - GetEnhancedConf() string -} - type configProvider struct { base otelcol.ConfigProvider confDump confDump @@ -42,14 +35,14 @@ type confDump struct { var _ otelcol.ConfigProvider = (*configProvider)(nil) // currently only supports a single URI in the uris slice, and this URI needs to be a file path. -func NewConfigProvider(uris []string) (ExtendedConfigProvider, error) { - ocp, err := otelcol.NewConfigProvider(newDefaultConfigProviderSettings(uris)) +func NewConfigProvider(reqs provider.Requires) (provider.Component, error) { + ocp, err := otelcol.NewConfigProvider(newDefaultConfigProviderSettings(reqs.URIs)) if err != nil { return nil, fmt.Errorf("failed to create configprovider: %w", err) } // this is a hack until we are unblocked from upstream to be able to use confToString. - yamlBytes, err := os.ReadFile(uris[0]) + yamlBytes, err := os.ReadFile(reqs.URIs[0]) if err != nil { return nil, fmt.Errorf("failed to read config: %w", err) } @@ -124,8 +117,8 @@ func (cp *configProvider) addEnhancedConf(conf *otelcol.Config) error { // GetProvidedConf returns a string representing the collector configuration passed // by the user. Should not be called concurrently with Get. -// Note: the current implementation does not redact sensitive data (e.g. API Key). -// Once we are unblocked and are able to remove the hack, this will provide the config +// Note: the current implementation does not redact sensitive data (e.g. API Key). +// Once we are unblocked and are able to remove the hack, this will provide the config // with any sensitive data redacted. func (cp *configProvider) GetProvidedConf() string { return cp.confDump.provided @@ -133,7 +126,7 @@ func (cp *configProvider) GetProvidedConf() string { // GetEnhancedConf returns a string representing the ehnhanced collector configuration. // Should not be called concurrently with Get. -// Note: this is currently not supported. +// Note: this is currently not supported. func (cp *configProvider) GetEnhancedConf() string { return cp.confDump.enhanced } diff --git a/comp/otelcol/otlp/components/pipeline/provider/provider_test.go b/comp/otelcol/provider/impl/provider_test.go similarity index 87% rename from comp/otelcol/otlp/components/pipeline/provider/provider_test.go rename to comp/otelcol/provider/impl/provider_test.go index 329d1e2bfc6d2..0b8827fbad0f3 100644 --- a/comp/otelcol/otlp/components/pipeline/provider/provider_test.go +++ b/comp/otelcol/provider/impl/provider_test.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2024-present Datadog, Inc. -package provider +package providerimpl import ( "context" @@ -12,6 +12,7 @@ import ( "path/filepath" "testing" + provider "github.com/DataDog/datadog-agent/comp/otelcol/provider/def" "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/datadogexporter" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/connector" @@ -34,12 +35,16 @@ func uriFromFile(filename string) string { } func TestNewConfigProvider(t *testing.T) { - _, err := NewConfigProvider([]string{uriFromFile("nop/config.yaml")}) + _, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("nop/config.yaml")}, + }) assert.NoError(t, err) } func TestConfigProviderGet(t *testing.T) { - provider, err := NewConfigProvider([]string{uriFromFile("nop/config.yaml")}) + provider, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("nop/config.yaml")}, + }) assert.NoError(t, err) factories, err := nopFactories() @@ -67,7 +72,9 @@ func upstreamConfigProvider(file string) (otelcol.ConfigProvider, error) { } func TestConfigProviderWatch(t *testing.T) { - provider, err := NewConfigProvider([]string{uriFromFile("nop/config.yaml")}) + provider, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("nop/config.yaml")}, + }) assert.NoError(t, err) var expected <-chan error @@ -75,7 +82,9 @@ func TestConfigProviderWatch(t *testing.T) { } func TestConfigProviderShutdown(t *testing.T) { - provider, err := NewConfigProvider([]string{uriFromFile("nop/config.yaml")}) + provider, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("nop/config.yaml")}, + }) assert.NoError(t, err) err = provider.Shutdown(context.Background()) @@ -84,7 +93,9 @@ func TestConfigProviderShutdown(t *testing.T) { func TestGetConfDump(t *testing.T) { t.Run("nop", func(t *testing.T) { - provider, err := NewConfigProvider([]string{uriFromFile("nop/config.yaml")}) + provider, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("nop/config.yaml")}, + }) assert.NoError(t, err) factories, err := nopFactories() @@ -122,7 +133,9 @@ func TestGetConfDump(t *testing.T) { }) t.Run("dd", func(t *testing.T) { - provider, err := NewConfigProvider([]string{uriFromFile("dd/config-dd.yaml")}) + provider, err := NewConfigProvider(provider.Requires{ + URIs: []string{uriFromFile("dd/config-dd.yaml")}, + }) assert.NoError(t, err) factories, err := nopFactories() diff --git a/comp/otelcol/otlp/components/pipeline/provider/testdata/dd/config-dd-result.yaml b/comp/otelcol/provider/impl/testdata/dd/config-dd-result.yaml similarity index 100% rename from comp/otelcol/otlp/components/pipeline/provider/testdata/dd/config-dd-result.yaml rename to comp/otelcol/provider/impl/testdata/dd/config-dd-result.yaml diff --git a/comp/otelcol/otlp/components/pipeline/provider/testdata/dd/config-dd.yaml b/comp/otelcol/provider/impl/testdata/dd/config-dd.yaml similarity index 100% rename from comp/otelcol/otlp/components/pipeline/provider/testdata/dd/config-dd.yaml rename to comp/otelcol/provider/impl/testdata/dd/config-dd.yaml diff --git a/comp/otelcol/otlp/components/pipeline/provider/testdata/nop/config-result.yaml b/comp/otelcol/provider/impl/testdata/nop/config-result.yaml similarity index 100% rename from comp/otelcol/otlp/components/pipeline/provider/testdata/nop/config-result.yaml rename to comp/otelcol/provider/impl/testdata/nop/config-result.yaml diff --git a/comp/otelcol/otlp/components/pipeline/provider/testdata/nop/config.yaml b/comp/otelcol/provider/impl/testdata/nop/config.yaml similarity index 100% rename from comp/otelcol/otlp/components/pipeline/provider/testdata/nop/config.yaml rename to comp/otelcol/provider/impl/testdata/nop/config.yaml diff --git a/go.mod b/go.mod index b795241df28a6..58efefd489d0e 100644 --- a/go.mod +++ b/go.mod @@ -50,6 +50,8 @@ replace ( github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/pipeline/provider => ./comp/otelcol/otlp/components/pipeline/provider github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/statsprocessor => ./comp/otelcol/otlp/components/statsprocessor github.com/DataDog/datadog-agent/comp/otelcol/otlp/testutil => ./comp/otelcol/otlp/testutil + github.com/DataDog/datadog-agent/comp/otelcol/provider/def => ./comp/otelcol/provider/def + github.com/DataDog/datadog-agent/comp/otelcol/provider/impl => ./comp/otelcol/provider/impl github.com/DataDog/datadog-agent/comp/serializer/compression => ./comp/serializer/compression github.com/DataDog/datadog-agent/pkg/aggregator/ckey => ./pkg/aggregator/ckey/ github.com/DataDog/datadog-agent/pkg/api => ./pkg/api @@ -232,14 +234,14 @@ require ( github.com/pahanini/go-grpc-bidirectional-streaming-example v0.0.0-20211027164128-cc6111af44be github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/prometheus/client_golang v1.19.0 + github.com/prometheus/client_golang v1.19.1 github.com/prometheus/client_model v0.6.1 github.com/prometheus/procfs v0.14.0 github.com/richardartoul/molecule v1.0.1-0.20221107223329-32cfee06a052 // indirect github.com/robfig/cron/v3 v3.0.1 github.com/samber/lo v1.39.0 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da - github.com/shirou/gopsutil/v3 v3.24.3 + github.com/shirou/gopsutil/v3 v3.24.4 github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 github.com/sirupsen/logrus v1.9.3 github.com/skydive-project/go-debouncer v1.0.0 @@ -271,7 +273,7 @@ require ( go.opentelemetry.io/collector/exporter v0.100.0 go.opentelemetry.io/collector/exporter/loggingexporter v0.100.0 go.opentelemetry.io/collector/exporter/otlpexporter v0.100.0 - go.opentelemetry.io/collector/pdata v1.7.0 + go.opentelemetry.io/collector/pdata v1.8.0 go.opentelemetry.io/collector/processor/batchprocessor v0.100.0 go.opentelemetry.io/collector/receiver v0.100.0 go.opentelemetry.io/collector/receiver/otlpreceiver v0.100.0 @@ -284,18 +286,18 @@ require ( go.uber.org/zap v1.27.0 go4.org/netipx v0.0.0-20220812043211-3cc044ffd68d golang.org/x/arch v0.7.0 - golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 - golang.org/x/net v0.24.0 + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 + golang.org/x/net v0.25.0 golang.org/x/sync v0.7.0 - golang.org/x/sys v0.19.0 + golang.org/x/sys v0.20.0 golang.org/x/text v0.15.0 golang.org/x/time v0.5.0 - golang.org/x/tools v0.20.0 + golang.org/x/tools v0.21.0 golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/grpc v1.63.2 google.golang.org/grpc/examples v0.0.0-20221020162917-9127159caf5a - google.golang.org/protobuf v1.34.0 + google.golang.org/protobuf v1.34.1 gopkg.in/DataDog/dd-trace-go.v1 v1.61.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 @@ -551,9 +553,9 @@ require ( go.etcd.io/etcd/client/v3 v3.6.0-alpha.0 // indirect go.etcd.io/etcd/server/v3 v3.6.0-alpha.0.0.20220522111935-c3bc4116dcd1 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/collector/consumer v0.100.0 - go.opentelemetry.io/collector/featuregate v1.7.0 - go.opentelemetry.io/collector/semconv v0.100.0 + go.opentelemetry.io/collector/consumer v0.101.0 + go.opentelemetry.io/collector/featuregate v1.8.0 + go.opentelemetry.io/collector/semconv v0.101.0 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect go.opentelemetry.io/contrib/propagators/b3 v1.26.0 // indirect go.opentelemetry.io/otel v1.26.0 // indirect @@ -566,10 +568,10 @@ require ( go.opentelemetry.io/otel/sdk/metric v1.26.0 // indirect go.opentelemetry.io/otel/trace v1.26.0 go.opentelemetry.io/proto/otlp v1.2.0 // indirect - golang.org/x/crypto v0.22.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.17.0 golang.org/x/oauth2 v0.19.0 // indirect - golang.org/x/term v0.19.0 // indirect + golang.org/x/term v0.20.0 // indirect gonum.org/v1/gonum v0.15.0 // indirect google.golang.org/api v0.177.0 // indirect google.golang.org/appengine v1.6.8 // indirect @@ -625,8 +627,9 @@ require ( github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/datadogexporter v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/logsagentexporter v0.54.0-rc.2 github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/exporter/serializerexporter v0.54.0-rc.2 - github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/pipeline/provider v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/otelcol/otlp/testutil v0.54.0-rc.2 + github.com/DataDog/datadog-agent/comp/otelcol/provider/def v0.0.0-00010101000000-000000000000 + github.com/DataDog/datadog-agent/comp/otelcol/provider/impl v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/serializer/compression v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/aggregator/ckey v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/api v0.54.0-rc.2 @@ -1128,10 +1131,10 @@ require ( go.opentelemetry.io/collector/config/configcompression v1.7.0 // indirect go.opentelemetry.io/collector/config/configgrpc v0.100.0 // indirect go.opentelemetry.io/collector/config/confighttp v0.100.0 // indirect - go.opentelemetry.io/collector/config/confignet v0.100.0 // indirect + go.opentelemetry.io/collector/config/confignet v0.101.0 // indirect go.opentelemetry.io/collector/config/configopaque v1.7.0 // indirect - go.opentelemetry.io/collector/config/configretry v0.100.0 // indirect - go.opentelemetry.io/collector/config/configtelemetry v0.100.0 // indirect + go.opentelemetry.io/collector/config/configretry v0.101.0 // indirect + go.opentelemetry.io/collector/config/configtelemetry v0.101.0 // indirect go.opentelemetry.io/collector/config/configtls v0.100.0 // indirect go.opentelemetry.io/collector/config/internal v0.100.0 // indirect go.opentelemetry.io/collector/connector v0.100.0 // indirect @@ -1142,7 +1145,7 @@ require ( go.opentelemetry.io/collector/extension/ballastextension v0.100.0 // indirect go.opentelemetry.io/collector/extension/zpagesextension v0.100.0 // indirect go.opentelemetry.io/collector/filter v0.100.0 // indirect - go.opentelemetry.io/collector/pdata/testdata v0.100.0 // indirect + go.opentelemetry.io/collector/pdata/testdata v0.101.0 // indirect go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.100.0 // indirect go.opentelemetry.io/collector/receiver/nopreceiver v0.100.0 // indirect go.opentelemetry.io/contrib/config v0.6.0 // indirect diff --git a/go.sum b/go.sum index aea8c3125abbf..050879c8ce552 100644 --- a/go.sum +++ b/go.sum @@ -2809,8 +2809,8 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= -github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= +github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU= +github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -3139,14 +3139,14 @@ go.opentelemetry.io/collector/config/configgrpc v0.100.0 h1:+RuWrysXLbjaf/+I7dU9 go.opentelemetry.io/collector/config/configgrpc v0.100.0/go.mod h1:NDKPjtEVL7TJMfByR/D5MYyjveUU3D0GkM96jU0u494= go.opentelemetry.io/collector/config/confighttp v0.100.0 h1:bkB8ZkkRL+N75QofuIosf2ZzkEYaBAA5C+eQpL4fOis= go.opentelemetry.io/collector/config/confighttp v0.100.0/go.mod h1:AaugDfPoHeOmFT2BICuGNp3ja3Sq1AcTxxw4WysFZsI= -go.opentelemetry.io/collector/config/confignet v0.100.0 h1:SW8IMK+9GwFa1cNZdXw6wMkbCsqUaRtj0fgP8/yG6oI= -go.opentelemetry.io/collector/config/confignet v0.100.0/go.mod h1:3naWoPss70RhDHhYjGACi7xh4NcVRvs9itzIRVWyu1k= +go.opentelemetry.io/collector/config/confignet v0.101.0 h1:Mdb9e/EpCSac4Ccg7w4UchS/o4yY1WoIc9X5o7fTu9E= +go.opentelemetry.io/collector/config/confignet v0.101.0/go.mod h1:3naWoPss70RhDHhYjGACi7xh4NcVRvs9itzIRVWyu1k= go.opentelemetry.io/collector/config/configopaque v1.7.0 h1:nZh5Hb1ofq9xP1wHLSt4obM85pRTccSeAjV0NbrJeTc= go.opentelemetry.io/collector/config/configopaque v1.7.0/go.mod h1:vxoDKYYYUF/arrdQJxmfhlgkcsb0DpdzC9KPFP97uuE= -go.opentelemetry.io/collector/config/configretry v0.100.0 h1:jEswHFjNokqJ0U2iYSzUlDy8N6A6D+zaoHM9t1TB6yw= -go.opentelemetry.io/collector/config/configretry v0.100.0/go.mod h1:uRdmPeCkrW9Zsadh2WEbQ1AGXGYJ02vCfmmT+0g69nY= -go.opentelemetry.io/collector/config/configtelemetry v0.100.0 h1:unlhNrFFXCinxk6iPHPYwANO+eFY4S1NTb5knSxteW4= -go.opentelemetry.io/collector/config/configtelemetry v0.100.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= +go.opentelemetry.io/collector/config/configretry v0.101.0 h1:5QggLq/lZiZXry1Ut52IOTbrdz1RbGoL29Io/wWdE4g= +go.opentelemetry.io/collector/config/configretry v0.101.0/go.mod h1:uRdmPeCkrW9Zsadh2WEbQ1AGXGYJ02vCfmmT+0g69nY= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0 h1:G9RerNdBUm6rYW6wrJoKzleBiDsCGaCjtQx5UYr0hzw= +go.opentelemetry.io/collector/config/configtelemetry v0.101.0/go.mod h1:YV5PaOdtnU1xRomPcYqoHmyCr48tnaAREeGO96EZw8o= go.opentelemetry.io/collector/config/configtls v0.100.0 h1:qcx8EXW4u+IQvyt8ZH5ld2dEns1zp8sugyM+s7RuiKY= go.opentelemetry.io/collector/config/configtls v0.100.0/go.mod h1:f8KZu6P8hIzTfybLKG3xMIzkCmXyjxVUfDTVUp2CmhA= go.opentelemetry.io/collector/config/internal v0.100.0 h1:XSbedIpdXOxIEGnnzCZnulTmWPSGWfXTH18ZMxuqt8s= @@ -3169,8 +3169,8 @@ go.opentelemetry.io/collector/connector v0.100.0 h1:6LN8LXhv553SjipKl7EbvlAECIZ9 go.opentelemetry.io/collector/connector v0.100.0/go.mod h1:yF4fkEtDrZOpwGOcnDTjkGjMCCQTPL2VCCBe9R43Jbg= go.opentelemetry.io/collector/connector/forwardconnector v0.100.0 h1:XDjWlanH99OcRmHsrmdGJ6Wt2Emn3ngTqYDN9hICOc0= go.opentelemetry.io/collector/connector/forwardconnector v0.100.0/go.mod h1:6CMahqUyGv/pXcOStoDPzuGR4NfQwwu8xWjp2apF0GQ= -go.opentelemetry.io/collector/consumer v0.100.0 h1:8sALAcWvizSyrZJCF+zTqD2RLmZAyeCuaQrNS2q6ti0= -go.opentelemetry.io/collector/consumer v0.100.0/go.mod h1:JOPOq8nSTdnQwc2xdHl4hcuYBYV8gjN2SlFqlqBe/Nc= +go.opentelemetry.io/collector/consumer v0.101.0 h1:9tDxaeHe1+Uovf3fhdx7T4pV5mo/Dc0hniH7O5H3RBA= +go.opentelemetry.io/collector/consumer v0.101.0/go.mod h1:ud5k64on9m7hHTrhjEeLhWbLkd8+Gp06rDt3p86TKNs= go.opentelemetry.io/collector/exporter v0.100.0 h1:eyPb93tQwdft5Eboo8O5LDdaM1eXAQbtbXKBEYQlwh4= go.opentelemetry.io/collector/exporter v0.100.0/go.mod h1:5UrDewyFp5yIQHyV7HUFAPdhHKJGbz1/uaTunm7X54I= go.opentelemetry.io/collector/exporter/debugexporter v0.100.0 h1:9y+6FxuwbjQA7osZ1ywNJ7baV+PXOBges2RPmv2Xs20= @@ -3189,16 +3189,16 @@ go.opentelemetry.io/collector/extension/ballastextension v0.100.0 h1:hQEuz3c81Kl go.opentelemetry.io/collector/extension/ballastextension v0.100.0/go.mod h1:45XovD+uwmVcPD8FAMKCIxdR1TnEbWz9cD7i4sJtFr4= go.opentelemetry.io/collector/extension/zpagesextension v0.100.0 h1:4bHq1NBg8hU+NSd9DBVFAl4vxi40J0tqClmU3IknrEg= go.opentelemetry.io/collector/extension/zpagesextension v0.100.0/go.mod h1:mcLfCcjq0/yZoieuyO0H5rVL3u7Why0/MRezNV2E7AU= -go.opentelemetry.io/collector/featuregate v1.7.0 h1:8tNgX2VaiR9jrpZevRSvStuJrvvL6WwScT264HNLk7U= -go.opentelemetry.io/collector/featuregate v1.7.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= +go.opentelemetry.io/collector/featuregate v1.8.0 h1:p/bAuk5LiSfdYS88yFl/Jzao9bHEYqCh7YvZJ+L+IZg= +go.opentelemetry.io/collector/featuregate v1.8.0/go.mod h1:w7nUODKxEi3FLf1HslCiE6YWtMtOOrMnSwsDam8Mg9w= go.opentelemetry.io/collector/filter v0.100.0 h1:XQyhnqJSK2sw+e9yvpkvl7y8QdJwH/gAnFoZDfEZ0dQ= go.opentelemetry.io/collector/filter v0.100.0/go.mod h1:3xGRpZo11DMJTDtMUGsDNkxKM6LMHqROGrQ/aTvskh8= go.opentelemetry.io/collector/otelcol v0.100.0 h1:5NWoo9T5tHP0oWt3OHetYpTRaQCJuef8KDDe5tLi+BA= go.opentelemetry.io/collector/otelcol v0.100.0/go.mod h1:pdPObjfQqP2pdE70jqQiJlZdEyZ0jA1euoRdHtgZjiA= -go.opentelemetry.io/collector/pdata v1.7.0 h1:/WNsBbE6KM3TTPUb9v/5B7IDqnDkgf8GyFhVJJqu7II= -go.opentelemetry.io/collector/pdata v1.7.0/go.mod h1:ehCBBA5GoFrMZkwyZAKGY/lAVSgZf6rzUt3p9mddmPU= -go.opentelemetry.io/collector/pdata/testdata v0.100.0 h1:pliojioiAv+CuLNTK+8tnCD2UgiJbKX9q8bDnpHkV1U= -go.opentelemetry.io/collector/pdata/testdata v0.100.0/go.mod h1:01BHOXvXaQaLLt5J34S093u3e+j//RhbfmEujpFJ/ME= +go.opentelemetry.io/collector/pdata v1.8.0 h1:d/QQgZxB4Y+d3mqLVh2ozvzujUhloD3P/fk7X+In764= +go.opentelemetry.io/collector/pdata v1.8.0/go.mod h1:/W7clu0wFC4WSRp94Ucn6Vm36Wkrt+tmtlDb1aiNZCY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0 h1:JzeUtg5RN1iIFgY8DakGlqBkGxOTJlkaYlLausnEGKY= +go.opentelemetry.io/collector/pdata/testdata v0.101.0/go.mod h1:ZGobfCus4fWo5RduZ7ENI0+HD9BewgKuO6qU2rBVnUg= go.opentelemetry.io/collector/processor v0.100.0 h1:8Zcd3v77SCSM5mAJbGes5aR/Yof3aY1csiwFhKFpLEQ= go.opentelemetry.io/collector/processor v0.100.0/go.mod h1:ZqUC8WWVYyPkaLUT1JXUCNpCpde8zXgSaFfJq2FXuVU= go.opentelemetry.io/collector/processor/batchprocessor v0.100.0 h1:N94WWv+o9yt4HqvFeRUjL8VNEhY8mCym+2XPKUWxjmo= @@ -3211,8 +3211,8 @@ go.opentelemetry.io/collector/receiver/nopreceiver v0.100.0 h1:IsLY4qMy9EVD68yXK go.opentelemetry.io/collector/receiver/nopreceiver v0.100.0/go.mod h1:kefF3fOWGppE4n3Lpr4fTP8Fuj05aKH23iDp5eXqs2M= go.opentelemetry.io/collector/receiver/otlpreceiver v0.100.0 h1:JuTBkWQ0xc8FargDe5aE6wCoAEk1gxLnBzfdIi8NOL8= go.opentelemetry.io/collector/receiver/otlpreceiver v0.100.0/go.mod h1:X2xzbz9K4Kz0i3c1IKa1gcwf7rpoJDidyp28A1AuHFs= -go.opentelemetry.io/collector/semconv v0.100.0 h1:QArUvWcbmsMjM4PV0zngUHRizZeUXibsPBWjDuNJXAs= -go.opentelemetry.io/collector/semconv v0.100.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= +go.opentelemetry.io/collector/semconv v0.101.0 h1:tOe9iTe9dDCnvz/bqgfNRr4w80kXG8505tQJ5h5v08Q= +go.opentelemetry.io/collector/semconv v0.101.0/go.mod h1:8ElcRZ8Cdw5JnvhTOQOdYizkJaQ10Z2fS+R6djOnj6A= go.opentelemetry.io/collector/service v0.100.0 h1:2LFvNQNSs2NnwElyqkyhAiqaGoDdiMnTQeFPCLZNgg0= go.opentelemetry.io/collector/service v0.100.0/go.mod h1:65NPZ6THkR/e7fd8vh+tw4Lh6iDJ1twNXVzL76a3VNk= go.opentelemetry.io/contrib/config v0.6.0 h1:M1SRD1Z15XHPGk61tMLI1up77XT5FdrqQSRrlH0fYuk= @@ -3331,8 +3331,8 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -3348,8 +3348,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= -golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ= golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= @@ -3488,8 +3488,8 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190130055435-99b60b757ec1/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -3677,8 +3677,9 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -3695,8 +3696,8 @@ golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -3809,8 +3810,8 @@ golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -4133,8 +4134,8 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.0 h1:Qo/qEd2RZPCf2nKuorzksSknv0d3ERwp1vFG38gSmH4= -google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/DataDog/dd-trace-go.v1 v1.61.0 h1:XKO91GwTjpIRhd56Xif/BZ2YgHkQufVTOvtkbRYSPi8= gopkg.in/DataDog/dd-trace-go.v1 v1.61.0/go.mod h1:NHKX1t9eKmajySb6H+zLdgZizCFzbt5iKvrTyxEyy8w= gopkg.in/Knetic/govaluate.v3 v3.0.0 h1:18mUyIt4ZlRlFZAAfVetz4/rzlJs9yhN+U02F4u1AOc= From bd25c5e0e2950645c554f52c4ce296a4939a7ae0 Mon Sep 17 00:00:00 2001 From: Yoann Ghigoff Date: Thu, 30 May 2024 15:55:43 +0200 Subject: [PATCH 32/54] [CWS] Skip empty strings when parsing args/envs (#25871) * testsuite: add test on empty args/envs strings * skip empty strings when parsing args/envs --- .../ebpf/c/include/constants/custom.h | 4 +- pkg/security/ebpf/c/include/hooks/exec.h | 28 +++++++--- pkg/security/tests/process_test.go | 56 ++++++++++++++++--- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/pkg/security/ebpf/c/include/constants/custom.h b/pkg/security/ebpf/c/include/constants/custom.h index e2b8b974f1343..e7c2d4113c727 100644 --- a/pkg/security/ebpf/c/include/constants/custom.h +++ b/pkg/security/ebpf/c/include/constants/custom.h @@ -27,9 +27,9 @@ enum MONITOR_KEYS { #define MAX_PERF_STR_BUFF_LEN 256 #define MAX_STR_BUFF_LEN (1 << 15) #define MAX_ARRAY_ELEMENT_SIZE 4096 -#define MAX_ARRAY_ELEMENT_PER_TAIL 28 +#define MAX_ARRAY_ELEMENT_PER_TAIL 27 #define MAX_ARGS_ELEMENTS (MAX_ARRAY_ELEMENT_PER_TAIL * (32 / 2)) // split tailcall limit -#define MAX_ARGS_READ_PER_TAIL 208 +#define MAX_ARGS_READ_PER_TAIL 160 #define EXEC_GET_ENVS_OFFSET 0 #define EXEC_PARSE_ARGS_ENVS_SPLIT 1 diff --git a/pkg/security/ebpf/c/include/hooks/exec.h b/pkg/security/ebpf/c/include/hooks/exec.h index 22946c6426ab9..e25bdbe2d2f89 100644 --- a/pkg/security/ebpf/c/include/hooks/exec.h +++ b/pkg/security/ebpf/c/include/hooks/exec.h @@ -392,9 +392,14 @@ int tail_call_target_get_envs_offset(void *ctx) { #pragma unroll for (i = 0; i < MAX_ARGS_READ_PER_TAIL && args_count < syscall->exec.args.count; i++) { bytes_read = bpf_probe_read_str(&buff->value[0], MAX_ARRAY_ELEMENT_SIZE, (void *)(args_start + offset)); - if (bytes_read <= 0 || bytes_read == MAX_ARRAY_ELEMENT_SIZE) { + if (bytes_read < 0 || bytes_read == MAX_ARRAY_ELEMENT_SIZE) { syscall->exec.args_envs_ctx.envs_offset = 0; return 0; + } else if (buff->value[0] == '\0') { + // skip empty strings + // directly check the first character instead of bytes_read because bpf_probe_read_str() + // may return 0 or 1 depending on the kernel version when reading empty strings + bytes_read = 1; } offset += bytes_read; args_count++; @@ -440,10 +445,19 @@ void __attribute__((always_inline)) parse_args_envs(void *ctx, struct args_envs_ #pragma unroll for (i = 0; i < MAX_ARRAY_ELEMENT_PER_TAIL; i++) { - void *string_array_ptr = &(buff->value[(event.size + sizeof(bytes_read)) & (MAX_STR_BUFF_LEN - MAX_ARRAY_ELEMENT_SIZE - 1)]); + if (args_envs->counter == args_envs->count) { + break; + } + + char *string_array_ptr = &(buff->value[(event.size + sizeof(bytes_read)) & (MAX_STR_BUFF_LEN - MAX_ARRAY_ELEMENT_SIZE - 1)]); - bytes_read = bpf_probe_read_str(string_array_ptr, MAX_ARRAY_ELEMENT_SIZE, (void *)(args_start + offset)); - if (bytes_read > 0) { + bytes_read = bpf_probe_read_str((void *)string_array_ptr, MAX_ARRAY_ELEMENT_SIZE, (void *)(args_start + offset)); + // skip empty strings + // depending on the kernel version, bpf_probe_read_str() may return 0 or 1 when reading empty strings + if (bytes_read == 0 || (bytes_read == 1 && *string_array_ptr == '\0')) { + offset += 1; + args_envs->counter++; + } else if (bytes_read > 0) { bytes_read--; // remove trailing 0 // insert size before the string @@ -470,11 +484,7 @@ void __attribute__((always_inline)) parse_args_envs(void *ctx, struct args_envs_ } else { event.size += data_length; args_envs->counter++; - offset += bytes_read + 1; - } - - if (args_envs->counter == args_envs->count) { - break; + offset += bytes_read + 1; // count trailing 0 } } else { break; diff --git a/pkg/security/tests/process_test.go b/pkg/security/tests/process_test.go index ca62d06d15777..aa93363f4f523 100644 --- a/pkg/security/tests/process_test.go +++ b/pkg/security/tests/process_test.go @@ -30,6 +30,7 @@ import ( sprobe "github.com/DataDog/datadog-agent/pkg/security/probe" "github.com/DataDog/datadog-agent/pkg/security/probe/constantfetch" "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" + "github.com/DataDog/datadog-agent/pkg/security/utils" "github.com/avast/retry-go/v4" "github.com/oliveagle/jsonpath" @@ -39,7 +40,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/secl/compiler/eval" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" - "github.com/DataDog/datadog-agent/pkg/security/utils" ) func TestProcess(t *testing.T) { @@ -485,8 +485,8 @@ func TestProcessContext(t *testing.T) { assert.Equal(t, args[i], argv[i], "expected arg not found") } } else { - assert.Equal(t, 459, len(argv), "incorrect number of args: %s", argv) - for i := 0; i != 459; i++ { + assert.Equal(t, 439, len(argv), "incorrect number of args: %s", argv) + for i := 0; i != 439; i++ { assert.Equal(t, args[i], argv[i], "expected arg not found") } } @@ -539,8 +539,8 @@ func TestProcessContext(t *testing.T) { assert.Equal(t, expected, argv[i], "expected arg not found") } } else { - assert.Equal(t, 474, len(argv), "incorrect number of args: %s", argv) - for i := 0; i != 474; i++ { + assert.Equal(t, 457, len(argv), "incorrect number of args: %s", argv) + for i := 0; i != 457; i++ { expected := args[i] if len(expected) > model.MaxArgEnvSize { expected = args[i][:model.MaxArgEnvSize-4] + "..." // 4 is the size number of the string @@ -650,8 +650,8 @@ func TestProcessContext(t *testing.T) { assert.Equal(t, envs[i], envp[i], "expected env not found") } } else { - assert.Equal(t, 736, len(envp), "incorrect number of envs: %s", envp) - for i := 0; i != 736; i++ { + assert.Equal(t, 704, len(envp), "incorrect number of envs: %s", envp) + for i := 0; i != 704; i++ { assert.Equal(t, envs[i], envp[i], "expected env not found") } } @@ -716,8 +716,8 @@ func TestProcessContext(t *testing.T) { assert.Equal(t, expected, envp[i], "expected env not found") } } else { - assert.Equal(t, 895, len(envp), "incorrect number of envs: %s", envp) - for i := 0; i != 895; i++ { + assert.Equal(t, 863, len(envp), "incorrect number of envs: %s", envp) + for i := 0; i != 863; i++ { expected := envs[i] if len(expected) > model.MaxArgEnvSize { expected = envs[i][:model.MaxArgEnvSize-4] + "..." // 4 is the size number of the string @@ -740,6 +740,44 @@ func TestProcessContext(t *testing.T) { } }) + t.Run("args-envs-empty-strings", func(t *testing.T) { + test.WaitSignal(t, func() error { + args := []string{"-al", ""} + envs := []string{"LD_LIBRARY_PATH=/tmp/lib"} + cmd := exec.Command("ls", args...) + cmd.Env = envs + _ = cmd.Run() + return nil + }, test.validateExecEvent(t, noWrapperType, func(event *model.Event, rule *rules.Rule) { + assertTriggeredRule(t, rule, "test_rule_args_envs") + + args, err := event.GetFieldValue("exec.args") + if err != nil || len(args.(string)) == 0 { + t.Error("not able to get args") + } + assert.Contains(t, args.(string), "-al", "arg not found") + + // envs + envs, err := event.GetFieldValue("exec.envs") + if err != nil || len(envs.([]string)) == 0 { + t.Error("not able to get envs") + } + + contains := func(s string) bool { + for _, env := range envs.([]string) { + if strings.Contains(env, s) { + return true + } + } + return false + } + assert.True(t, contains("LD_LIBRARY_PATH"), "env not found") + + assert.False(t, event.Exec.ArgsTruncated, "args should not be truncated") + assert.False(t, event.Exec.EnvsTruncated, "envs should not be truncated") + })) + }) + t.Run("tty", func(t *testing.T) { testFile, _, err := test.Path("test-process-tty") if err != nil { From 7b15e7f3db85efbdaf64f50d718d8ee9cf71cc9c Mon Sep 17 00:00:00 2001 From: morgan-wang <96082814+mwdd146980@users.noreply.github.com> Date: Thu, 30 May 2024 07:52:48 -0700 Subject: [PATCH 33/54] add linux compatibility for diagnose port-conflict (#25667) * initial commit * add port-conflict suite to diagnose e2er test * modify e2e test so that port-conflict suite is added conditionally based on OS * remove os == darwin || os == linux conditional in order to pass windows e2e test * added unit tests for poller.go, re-added OS conditional to runner.go, removing changes to e2e test * re-add OS conditionals in e2e testing * add conditional to diagnose_common_test.go to prevent port-conflict suite from appearing on windows * modify getDiagnoseOutput to remove port-conflict from output on windows * modify getDiagnoseOutput to remove port-conflict from output on windows * remove changes diagnose_common_test.go to evaluate how to make changes to OS-specific files * turn TestDiagnoseIn/Exclude into helper function, make it OS specific in diagnose_nix/win_test.go * add tests for coverage * move tests from poller_macos_test.go to poller_test.go and add windows conditionals to skip * change poller_other to poller_windows, change error logs from printf to errorf * stop exporting windowsOSImpl * remove typo --- pkg/diagnose/runner.go | 4 +- pkg/util/port/portlist/netstat_test.go | 4 +- pkg/util/port/portlist/poller.go | 31 ++ pkg/util/port/portlist/poller_linux.go | 459 ++++++++++++++++++ pkg/util/port/portlist/poller_linux_test.go | 257 ++++++++++ pkg/util/port/portlist/poller_macos.go | 36 +- pkg/util/port/portlist/poller_macos_test.go | 176 ------- pkg/util/port/portlist/poller_test.go | 256 ++++++++++ ...{poller_not_macos.go => poller_windows.go} | 18 +- .../diagnose/diagnose_common_test.go | 8 +- .../diagnose/diagnose_nix_test.go | 20 +- .../diagnose/diagnose_win_test.go | 13 +- 12 files changed, 1054 insertions(+), 228 deletions(-) create mode 100644 pkg/util/port/portlist/poller_linux.go create mode 100644 pkg/util/port/portlist/poller_linux_test.go create mode 100644 pkg/util/port/portlist/poller_test.go rename pkg/util/port/portlist/{poller_not_macos.go => poller_windows.go} (68%) diff --git a/pkg/diagnose/runner.go b/pkg/diagnose/runner.go index 88e0d094b0b51..55da898412994 100644 --- a/pkg/diagnose/runner.go +++ b/pkg/diagnose/runner.go @@ -568,8 +568,8 @@ func RegisterConnectivityDatadogEventPlatform(catalog *diagnosis.Catalog) { // RegisterPortConflict registers the port-conflict diagnose suite. func RegisterPortConflict(catalog *diagnosis.Catalog) { - // port-conflict suite available in darwin only for now - if runtime.GOOS == "darwin" { + // port-conflict suite available in darwin and linux only for now + if runtime.GOOS == "darwin" || runtime.GOOS == "linux" { catalog.Register("port-conflict", func() []diagnosis.Diagnosis { return ports.DiagnosePortSuite() }) } } diff --git a/pkg/util/port/portlist/netstat_test.go b/pkg/util/port/portlist/netstat_test.go index bb9608551717f..d39801b71e66c 100644 --- a/pkg/util/port/portlist/netstat_test.go +++ b/pkg/util/port/portlist/netstat_test.go @@ -3,8 +3,8 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2014-present Datadog, Inc. -//go:build darwin && !ios -// +build darwin,!ios +//go:build darwin +// +build darwin package portlist diff --git a/pkg/util/port/portlist/poller.go b/pkg/util/port/portlist/poller.go index bd48452ca9a8a..6c6b56e1c4179 100644 --- a/pkg/util/port/portlist/poller.go +++ b/pkg/util/port/portlist/poller.go @@ -10,6 +10,7 @@ package portlist import ( "fmt" + "sort" "sync" "golang.org/x/exp/slices" @@ -93,3 +94,33 @@ func (p *Poller) getList() (List, error) { p.scratch, err = p.os.AppendListeningPorts(p.scratch[:0]) return p.scratch, err } + +func (a *Port) lessThan(b *Port) bool { + if a.Port != b.Port { + return a.Port < b.Port + } + if a.Proto != b.Proto { + return a.Proto < b.Proto + } + return a.Process < b.Process +} + +// sortAndDedup sorts ps in place (by Port.LessThan) and then returns +// a subset of it with duplicate (Proto, Port) removed. +// Multiple processes can't use the same port and protocol +// on the same port so there's no need to check for that +func sortAndDedup(ps List) List { + sort.Slice(ps, func(i, j int) bool { + return (&ps[i]).lessThan(&ps[j]) + }) + out := ps[:0] + var last Port + for _, p := range ps { + if last.Proto == p.Proto && last.Port == p.Port { + continue + } + out = append(out, p) + last = p + } + return out +} diff --git a/pkg/util/port/portlist/poller_linux.go b/pkg/util/port/portlist/poller_linux.go new file mode 100644 index 0000000000000..ee8c4cef20c33 --- /dev/null +++ b/pkg/util/port/portlist/poller_linux.go @@ -0,0 +1,459 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2014-present Datadog, Inc. + +//go:build linux + +package portlist + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "io/fs" + "os" + "path/filepath" + "runtime" + "strings" + "syscall" + "unsafe" + + "go4.org/mem" + "golang.org/x/sys/unix" + + "github.com/DataDog/datadog-agent/pkg/util/log" +) + +func (p *Poller) init() { + p.os = newLinuxImpl(p.IncludeLocalhost) +} + +type linuxImpl struct { + procNetFiles []*os.File // seeked to start & reused between calls + readlinkPathBuf []byte + + known map[string]*portMeta // inode string => metadata + br *bufio.Reader + includeLocalhost bool +} + +type portMeta struct { + port Port + pid int + keep bool + needsProcName bool +} + +var eofReader = bytes.NewReader(nil) + +func newLinuxImplBase(includeLocalhost bool) *linuxImpl { + return &linuxImpl{ + br: bufio.NewReader(eofReader), + known: map[string]*portMeta{}, + includeLocalhost: includeLocalhost, + } +} + +func newLinuxImpl(includeLocalhost bool) osImpl { + li := newLinuxImplBase(includeLocalhost) + for _, name := range []string{ + "/proc/net/tcp", + "/proc/net/tcp6", + "/proc/net/udp", + "/proc/net/udp6", + } { + f, err := os.Open(name) + if err != nil { + if os.IsNotExist(err) { + continue + } + log.Errorf("diagnose port-conflict poller warning; ignoring: %v", err) + continue + } + li.procNetFiles = append(li.procNetFiles, f) + } + return li +} + +func (li *linuxImpl) Close() error { + for _, f := range li.procNetFiles { + f.Close() + } + li.procNetFiles = nil + return nil +} + +const ( + v6Localhost = "00000000000000000000000001000000:" + v6Any = "00000000000000000000000000000000:0000" + v4Localhost = "0100007F:" + v4Any = "00000000:0000" +) + +func (li *linuxImpl) AppendListeningPorts(base []Port) ([]Port, error) { + + br := li.br + defer br.Reset(eofReader) + + // Start by marking all previous known ports as gone. If this mark + // bit is still false later, we'll remove them. + for _, pm := range li.known { + pm.keep = false + } + + for _, f := range li.procNetFiles { + name := f.Name() + _, err := f.Seek(0, io.SeekStart) + if err != nil { + return nil, err + } + br.Reset(f) + err = li.parseProcNetFile(br, filepath.Base(name)) + if err != nil { + return nil, fmt.Errorf("parsing %q: %w", name, err) + } + } + + // Delete ports that aren't open any longer. + // And see if there are any process names we need to look for. + needProc := make(map[string]*portMeta) + for inode, pm := range li.known { + if !pm.keep { + delete(li.known, inode) + continue + } + if pm.needsProcName { + needProc[inode] = pm + } + } + err := li.findProcessNames(needProc) + if err != nil { + return nil, err + } + + ret := base + for _, pm := range li.known { + ret = append(ret, pm.port) + } + return sortAndDedup(ret), nil +} + +func (li *linuxImpl) parseProcNetFile(r *bufio.Reader, fileBase string) error { + proto := strings.TrimSuffix(fileBase, "6") + + // skip header row + _, err := r.ReadSlice('\n') + if err != nil { + return err + } + + fields := make([]mem.RO, 0, 20) // 17 current fields + some future slop + + wantRemote := mem.S(v4Any) + if strings.HasSuffix(fileBase, "6") { + wantRemote = mem.S(v6Any) + } + + // remoteIndex is the index within a line to the remote address field. + // -1 means not yet found. + remoteIndex := -1 + + // Add an upper bound on how many rows we'll attempt to read just + // to make sure this doesn't consume too much of their CPU. + // TODO(bradfitz,crawshaw): adaptively adjust polling interval as function + // of open sockets. + const maxRows = 1e6 + rows := 0 + + // Scratch buffer for making inode strings. + inoBuf := make([]byte, 0, 50) + + for { + line, err := r.ReadSlice('\n') + if err == io.EOF { + break + } + if err != nil { + return err + } + rows++ + if rows >= maxRows { + break + } + if len(line) == 0 { + continue + } + + // On the first row of output, find the index of the 3rd field (index 2), + // the remote address. All the rows are aligned, at least until 4 billion open + // TCP connections, per the Linux get_tcp4_sock's "%4d: " on an int i. + if remoteIndex == -1 { + remoteIndex = fieldIndex(line, 2) + if remoteIndex == -1 { + break + } + } + + if len(line) < remoteIndex || !mem.HasPrefix(mem.B(line).SliceFrom(remoteIndex), wantRemote) { + // Fast path for not being a listener port. + continue + } + + // sl local rem ... inode + fields = mem.AppendFields(fields[:0], mem.B(line)) + local := fields[1] + rem := fields[2] + inode := fields[9] + + if !rem.Equal(wantRemote) { + // not a "listener" port + continue + } + + // If a port is bound to localhost, ignore it. + // TODO: localhost is bigger than 1 IP, we need to ignore + // more things. + if !li.includeLocalhost && (mem.HasPrefix(local, mem.S(v4Localhost)) || mem.HasPrefix(local, mem.S(v6Localhost))) { + continue + } + + // Don't use strings.Split here, because it causes + // allocations significant enough to show up in profiles. + i := mem.IndexByte(local, ':') + if i == -1 { + return fmt.Errorf("%q unexpectedly didn't have a colon", local.StringCopy()) + } + portv, err := mem.ParseUint(local.SliceFrom(i+1), 16, 16) + if err != nil { + return fmt.Errorf("%#v: %s", local.SliceFrom(9).StringCopy(), err) + } + inoBuf = append(inoBuf[:0], "socket:["...) + inoBuf = mem.Append(inoBuf, inode) + inoBuf = append(inoBuf, ']') + + if pm, ok := li.known[string(inoBuf)]; ok { + pm.keep = true + // Rest should be unchanged. + } else { + li.known[string(inoBuf)] = &portMeta{ + needsProcName: true, + keep: true, + port: Port{ + Proto: proto, + Port: uint16(portv), + }, + } + } + } + + return nil +} + +// errDone is an internal sentinel error that we found everything we were looking for. +var errDone = errors.New("done") + +// need is keyed by inode string. +func (li *linuxImpl) findProcessNames(need map[string]*portMeta) error { + if len(need) == 0 { + return nil + } + defer func() { + // Anything we didn't find, give up on and don't try to look for it later. + for _, pm := range need { + pm.needsProcName = false + } + }() + + err := foreachPID(func(pid mem.RO) error { + var procBuf [128]byte + fdPath := mem.Append(procBuf[:0], mem.S("/proc/")) + fdPath = mem.Append(fdPath, pid) + fdPath = mem.Append(fdPath, mem.S("/fd")) + + // Android logs a bunch of audit violations in logcat + // if we try to open things we don't have access + // to. So on Android only, ask if we have permission + // rather than just trying it to determine whether we + // have permission. + if runtime.GOOS == "android" && syscall.Access(string(fdPath), unix.R_OK) != nil { + return nil + } + _ = walkShallow(mem.B(fdPath), func(fd mem.RO, de fs.DirEntry) error { + targetBuf := make([]byte, 64) // plenty big for "socket:[165614651]" + + linkPath := li.readlinkPathBuf[:0] + linkPath = fmt.Appendf(linkPath, "/proc/") + linkPath = mem.Append(linkPath, pid) + linkPath = append(linkPath, "/fd/"...) + linkPath = mem.Append(linkPath, fd) + linkPath = append(linkPath, 0) // terminating NUL + li.readlinkPathBuf = linkPath // to reuse its buffer next time + n, ok := readlink(linkPath, targetBuf) + if !ok { + // Not a symlink or no permission. + // Skip it. + return nil + } + + pe := need[string(targetBuf[:n])] // m[string([]byte)] avoids alloc + if pe == nil { + return nil + } + bs, err := os.ReadFile(fmt.Sprintf("/proc/%s/cmdline", pid.StringCopy())) + if err != nil { + // Usually shouldn't happen. One possibility is + // the process has gone away, so let's skip it. + return nil + } + + argv := strings.Split(strings.TrimSuffix(string(bs), "\x00"), "\x00") + if p, err := mem.ParseInt(pid, 10, 0); err == nil { + pe.pid = int(p) + } + pe.port.Process = argvSubject(argv...) + pid64, _ := mem.ParseInt(pid, 10, 0) + pe.port.Pid = int(pid64) + pe.needsProcName = false + delete(need, string(targetBuf[:n])) + if len(need) == 0 { + return errDone + } + return nil + }) + return nil + }) + if err == errDone { + return nil + } + return err +} + +func foreachPID(fn func(pidStr mem.RO) error) error { + err := walkShallow(mem.S("/proc"), func(name mem.RO, de fs.DirEntry) error { + if !isNumeric(name) { + return nil + } + return fn(name) + }) + if os.IsNotExist(err) { + // This can happen if the directory we're + // reading disappears during the run. No big + // deal. + return nil + } + return err +} + +func isNumeric(s mem.RO) bool { + for i, n := 0, s.Len(); i < n; i++ { + b := s.At(i) + if b < '0' || b > '9' { + return false + } + } + return s.Len() > 0 +} + +// fieldIndex returns the offset in line where the Nth field (0-based) begins, or -1 +// if there aren't that many fields. Fields are separated by 1 or more spaces. +func fieldIndex(line []byte, n int) int { + skip := 0 + for i := 0; i <= n; i++ { + // Skip spaces. + for skip < len(line) && line[skip] == ' ' { + skip++ + } + if skip == len(line) { + return -1 + } + if i == n { + break + } + // Skip non-space. + for skip < len(line) && line[skip] != ' ' { + skip++ + } + } + return skip +} + +// path must be null terminated. +func readlink(path, buf []byte) (n int, ok bool) { + if len(buf) == 0 || len(path) < 2 || path[len(path)-1] != 0 { + return 0, false + } + var dirfd int = unix.AT_FDCWD + r0, _, e1 := unix.Syscall6(unix.SYS_READLINKAT, + uintptr(dirfd), + uintptr(unsafe.Pointer(&path[0])), + uintptr(unsafe.Pointer(&buf[0])), + uintptr(len(buf)), + 0, 0) + n = int(r0) + if e1 != 0 { + return 0, false + } + return n, true +} + +// argvSubject takes a command and its flags, and returns the +// short/pretty name for the process. This is usually the basename of +// the binary being executed, but can sometimes vary (e.g. so that we +// don't report all Java programs as "java"). +func argvSubject(argv ...string) string { + if len(argv) == 0 { + return "" + } + ret := filepath.Base(argv[0]) + + // Handle special cases. + switch { + case ret == "mono" && len(argv) >= 2: + // .Net programs execute as `mono actualProgram.exe`. + ret = filepath.Base(argv[1]) + } + + // Handle space separated argv + ret, _, _ = strings.Cut(ret, " ") + + // Remove common noise. + ret = strings.TrimSpace(ret) + ret = strings.TrimSuffix(ret, ".exe") + + return ret +} + +// walkFunc is the callback type used with walkShallow. +// +// The name and de are only valid for the duration of func's call +// and should not be retained. +type walkFunc func(name mem.RO, de fs.DirEntry) error + +// walkShallow reads the entries in the named directory and calls fn for each. +// It does not recurse into subdirectories. +// +// If fn returns an error, iteration stops and walkShallow returns that value. +func walkShallow(dirName mem.RO, fn walkFunc) error { + of, err := os.Open(dirName.StringCopy()) + if err != nil { + return err + } + defer of.Close() + for { + fis, err := of.ReadDir(100) + for _, de := range fis { + if err := fn(mem.S(de.Name()), de); err != nil { + return err + } + } + if err != nil { + if err == io.EOF { + return nil + } + return err + } + } +} diff --git a/pkg/util/port/portlist/poller_linux_test.go b/pkg/util/port/portlist/poller_linux_test.go new file mode 100644 index 0000000000000..d3a96d6107130 --- /dev/null +++ b/pkg/util/port/portlist/poller_linux_test.go @@ -0,0 +1,257 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2014-present Datadog, Inc. + +package portlist + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "reflect" + "sort" + "testing" + + "github.com/google/go-cmp/cmp" + "go4.org/mem" +) + +func TestFieldIndex(t *testing.T) { + tests := []struct { + in string + field int + want int + }{ + {"foo", 0, 0}, + {" foo", 0, 2}, + {"foo bar", 1, 5}, + {" foo bar", 1, 6}, + {" foo bar", 2, -1}, + {" foo bar ", 2, -1}, + {" foo bar x", 2, 10}, + {" 1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 34062 1 0000000000000000 100 0 0 10 0", + 2, 19}, + } + for _, tt := range tests { + if got := fieldIndex([]byte(tt.in), tt.field); got != tt.want { + t.Errorf("fieldIndex(%q, %v) = %v; want %v", tt.in, tt.field, got, tt.want) + } + } +} + +func TestParsePorts(t *testing.T) { + tests := []struct { + name string + in string + file string + want map[string]*portMeta + }{ + { + name: "empty", + in: "header line (ignored)\n", + want: map[string]*portMeta{}, + }, + { + name: "ipv4", + file: "tcp", + in: `header line + 0: 0100007F:0277 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 22303 1 0000000000000000 100 0 0 10 0 + 1: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 34062 1 0000000000000000 100 0 0 10 0 + 2: 5501A8C0:ADD4 B25E9536:01BB 01 00000000:00000000 02:00000B2B 00000000 1000 0 155276677 2 0000000000000000 22 4 30 10 -1 +`, + want: map[string]*portMeta{ + "socket:[34062]": { + port: Port{Proto: "tcp", Port: 22}, + }, + }, + }, + { + name: "ipv6", + file: "tcp6", + in: ` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000000000000000000001000000:0277 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 35720 1 0000000000000000 100 0 0 10 0 + 1: 00000000000000000000000000000000:1F91 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 142240557 1 0000000000000000 100 0 0 10 0 + 2: 00000000000000000000000000000000:0016 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 34064 1 0000000000000000 100 0 0 10 0 + 3: 69050120005716BC64906EBE009ECD4D:D506 0047062600000000000000006E171268:01BB 01 00000000:00000000 02:0000009E 00000000 1000 0 151042856 2 0000000000000000 21 4 28 10 -1 +`, + want: map[string]*portMeta{ + "socket:[142240557]": { + port: Port{Proto: "tcp", Port: 8081}, + }, + "socket:[34064]": { + port: Port{Proto: "tcp", Port: 22}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + buf := bytes.NewBufferString(tt.in) + r := bufio.NewReader(buf) + file := "tcp" + if tt.file != "" { + file = tt.file + } + li := newLinuxImplBase(false) + err := li.parseProcNetFile(r, file) + if err != nil { + t.Fatal(err) + } + for _, pm := range tt.want { + pm.keep = true + pm.needsProcName = true + } + if diff := cmp.Diff(li.known, tt.want, cmp.AllowUnexported(Port{}), cmp.AllowUnexported(portMeta{})); diff != "" { + t.Errorf("unexpected parsed ports (-got+want):\n%s", diff) + } + }) + } +} + +func BenchmarkParsePorts(b *testing.B) { + b.ReportAllocs() + + var contents bytes.Buffer + contents.WriteString(` sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000000000000000000001000000:0277 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 35720 1 0000000000000000 100 0 0 10 0 + 1: 00000000000000000000000000000000:1F91 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 1000 0 142240557 1 0000000000000000 100 0 0 10 0 + 2: 00000000000000000000000000000000:0016 00000000000000000000000000000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 34064 1 0000000000000000 100 0 0 10 0 +`) + for i := 0; i < 50000; i++ { + contents.WriteString(" 3: 69050120005716BC64906EBE009ECD4D:D506 0047062600000000000000006E171268:01BB 01 00000000:00000000 02:0000009E 00000000 1000 0 151042856 2 0000000000000000 21 4 28 10 -1\n") + } + + li := newLinuxImplBase(false) + + r := bytes.NewReader(contents.Bytes()) + br := bufio.NewReader(&contents) + b.ResetTimer() + for i := 0; i < b.N; i++ { + r.Seek(0, io.SeekStart) + br.Reset(r) + err := li.parseProcNetFile(br, "tcp6") + if err != nil { + b.Fatal(err) + } + if len(li.known) != 2 { + b.Fatalf("wrong results; want 2 parsed got %d", len(li.known)) + } + } +} + +func BenchmarkFindProcessNames(b *testing.B) { + b.ReportAllocs() + li := &linuxImpl{} + need := map[string]*portMeta{ + "something-we'll-never-find": new(portMeta), + } + for i := 0; i < b.N; i++ { + if err := li.findProcessNames(need); err != nil { + b.Fatal(err) + } + } +} + +func TestWalkShallow(t *testing.T) { + d := t.TempDir() + + t.Run("basics", func(t *testing.T) { + if err := os.WriteFile(filepath.Join(d, "foo"), []byte("1"), 0600); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(d, "bar"), []byte("22"), 0400); err != nil { + t.Fatal(err) + } + if err := os.Mkdir(filepath.Join(d, "baz"), 0777); err != nil { + t.Fatal(err) + } + + var got []string + if err := walkShallow(mem.S(d), func(name mem.RO, de os.DirEntry) error { + var size int64 + if fi, err := de.Info(); err != nil { + t.Errorf("Info stat error on %q: %v", de.Name(), err) + } else if !fi.IsDir() { + size = fi.Size() + } + got = append(got, fmt.Sprintf("%q %q dir=%v type=%d size=%v", name.StringCopy(), de.Name(), de.IsDir(), de.Type(), size)) + return nil + }); err != nil { + t.Fatal(err) + } + sort.Strings(got) + want := []string{ + `"bar" "bar" dir=false type=0 size=2`, + `"baz" "baz" dir=true type=2147483648 size=0`, + `"foo" "foo" dir=false type=0 size=1`, + } + if !reflect.DeepEqual(got, want) { + t.Errorf("mismatch:\n got %#q\nwant %#q", got, want) + } + }) + + t.Run("err_not_exist", func(t *testing.T) { + err := walkShallow(mem.S(filepath.Join(d, "not_exist")), func(name mem.RO, de os.DirEntry) error { + return nil + }) + if !os.IsNotExist(err) { + t.Errorf("unexpected error: %v", err) + } + }) +} + +func TestArgvSubject(t *testing.T) { + tests := []struct { + in []string + want string + }{ + { + in: nil, + want: "", + }, + { + in: []string{"/usr/bin/sshd"}, + want: "sshd", + }, + { + in: []string{"/bin/mono"}, + want: "mono", + }, + { + in: []string{"/nix/store/x2cw2xjw98zdysf56bdlfzsr7cyxv0jf-mono-5.20.1.27/bin/mono", "/bin/exampleProgram.exe"}, + want: "exampleProgram", + }, + { + in: []string{"/bin/mono", "/sbin/exampleProgram.bin"}, + want: "exampleProgram.bin", + }, + { + in: []string{"/usr/bin/sshd_config [listener] 1 of 10-100 startups"}, + want: "sshd_config", + }, + { + in: []string{"/usr/bin/sshd [listener] 0 of 10-100 startups"}, + want: "sshd", + }, + { + in: []string{"/opt/aws/bin/eic_run_authorized_keys %u %f -o AuthorizedKeysCommandUser ec2-instance-connect [listener] 0 of 10-100 startups"}, + want: "eic_run_authorized_keys", + }, + { + in: []string{"/usr/bin/nginx worker"}, + want: "nginx", + }, + } + + for _, test := range tests { + got := argvSubject(test.in...) + if got != test.want { + t.Errorf("argvSubject(%v) = %q, want %q", test.in, got, test.want) + } + } +} diff --git a/pkg/util/port/portlist/poller_macos.go b/pkg/util/port/portlist/poller_macos.go index ece0a7246a6d7..cbefe70562b15 100644 --- a/pkg/util/port/portlist/poller_macos.go +++ b/pkg/util/port/portlist/poller_macos.go @@ -11,13 +11,13 @@ import ( "bufio" "bytes" "fmt" - "log" "os/exec" - "sort" "strings" "go.uber.org/atomic" "go4.org/mem" + + "github.com/DataDog/datadog-agent/pkg/util/log" ) // init initializes the Poller by ensuring it has an underlying @@ -156,14 +156,14 @@ func (im *macOSImpl) addProcesses() error { } // fails when run in a macOS sandbox, so make this non-fatal. if lsofFailed.CompareAndSwap(false, true) { - log.Printf("portlist: can't run lsof in Mac sandbox; omitting process names from service list. Error details: %v, %s", err, bytes.TrimSpace(stderr)) + log.Errorf("diagnose port-conflict poller: can't run lsof in Mac sandbox; omitting process names from service list. Error details: %v, %s", err, bytes.TrimSpace(stderr)) } return nil } defer func() { ps, err := lsofCmd.Process.Wait() if err != nil || ps.ExitCode() != 0 { - log.Printf("portlist: can't run lsof in Mac sandbox; omitting process names from service list. Error: %v, exit code %d", err, ps.ExitCode()) + log.Errorf("diagnose port-conflict poller: can't run lsof in Mac sandbox; omitting process names from service list. Error: %v, exit code %d", err, ps.ExitCode()) lsofFailed.Store(true) } }() @@ -229,16 +229,6 @@ func lsofProtoLower(p []byte) string { return strings.ToLower(string(p)) } -func (a *Port) lessThan(b *Port) bool { - if a.Port != b.Port { - return a.Port < b.Port - } - if a.Proto != b.Proto { - return a.Proto < b.Proto - } - return a.Process < b.Process -} - func (a List) String() string { var sb strings.Builder for _, v := range a { @@ -247,21 +237,3 @@ func (a List) String() string { } return strings.TrimRight(sb.String(), "\n") } - -// sortAndDedup sorts ps in place (by Port.LessThan) and then returns -// a subset of it with duplicate (Proto, Port) removed. -func sortAndDedup(ps List) List { - sort.Slice(ps, func(i, j int) bool { - return (&ps[i]).lessThan(&ps[j]) - }) - out := ps[:0] - var last Port - for _, p := range ps { - if last.Proto == p.Proto && last.Port == p.Port { - continue - } - out = append(out, p) - last = p - } - return out -} diff --git a/pkg/util/port/portlist/poller_macos_test.go b/pkg/util/port/portlist/poller_macos_test.go index 8859d4b106f6f..2f4b967eadbf5 100644 --- a/pkg/util/port/portlist/poller_macos_test.go +++ b/pkg/util/port/portlist/poller_macos_test.go @@ -8,185 +8,9 @@ package portlist import ( - "net" "testing" ) -func TestGetList(t *testing.T) { - var p Poller - pl, _, err := p.Poll() - if err != nil { - t.Fatal(err) - } - for i, p := range pl { - t.Logf("[%d] %+v", i, p) - } - t.Logf("As String: %s", List(pl)) -} - -func TestIgnoreLocallyBoundPorts(t *testing.T) { - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Skipf("failed to bind: %v", err) - } - defer ln.Close() - ta := ln.Addr().(*net.TCPAddr) - port := ta.Port - var p Poller - pl, _, err := p.Poll() - if err != nil { - t.Fatal(err) - } - for _, p := range pl { - if p.Proto == "tcp" && int(p.Port) == port { - t.Fatal("didn't expect to find test's localhost ephemeral port") - } - } -} - -func TestPoller(t *testing.T) { - var p Poller - p.IncludeLocalhost = true - get := func(t *testing.T) []Port { - t.Helper() - s, _, err := p.Poll() - if err != nil { - t.Fatal(err) - } - return s - } - - p1 := get(t) - ln, err := net.Listen("tcp", "127.0.0.1:0") - if err != nil { - t.Skipf("failed to bind: %v", err) - } - defer ln.Close() - port := uint16(ln.Addr().(*net.TCPAddr).Port) - containsPort := func(pl List) bool { - for _, p := range pl { - if p.Proto == "tcp" && p.Port == port { - return true - } - } - return false - } - if containsPort(p1) { - t.Error("unexpectedly found ephemeral port in p1, before it was opened", port) - } - p2 := get(t) - if !containsPort(p2) { - t.Error("didn't find ephemeral port in p2", port) - } - ln.Close() - p3 := get(t) - if containsPort(p3) { - t.Error("unexpectedly found ephemeral port in p3, after it was closed", port) - } -} - -func TestEqualLessThan(t *testing.T) { - tests := []struct { - name string - a, b Port - want bool - }{ - { - "Port a < b", - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - Port{Proto: "tcp", Port: 101, Process: "proc1"}, - true, - }, - { - "Port a > b", - Port{Proto: "tcp", Port: 101, Process: "proc1"}, - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - false, - }, - { - "Proto a < b", - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - Port{Proto: "udp", Port: 100, Process: "proc1"}, - true, - }, - { - "Proto a < b", - Port{Proto: "udp", Port: 100, Process: "proc1"}, - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - false, - }, - { - "Process a < b", - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - Port{Proto: "tcp", Port: 100, Process: "proc2"}, - true, - }, - { - "Process a > b", - Port{Proto: "tcp", Port: 100, Process: "proc2"}, - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - false, - }, - { - "Port evaluated first", - Port{Proto: "udp", Port: 100, Process: "proc2"}, - Port{Proto: "tcp", Port: 101, Process: "proc1"}, - true, - }, - { - "Proto evaluated second", - Port{Proto: "tcp", Port: 100, Process: "proc2"}, - Port{Proto: "udp", Port: 100, Process: "proc1"}, - true, - }, - { - "Process evaluated fourth", - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - Port{Proto: "tcp", Port: 100, Process: "proc2"}, - true, - }, - { - "equal", - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - Port{Proto: "tcp", Port: 100, Process: "proc1"}, - false, - }, - } - - for _, tt := range tests { - got := tt.a.lessThan(&tt.b) - if got != tt.want { - t.Errorf("%s: Equal = %v; want %v", tt.name, got, tt.want) - } - lessBack := tt.b.lessThan(&tt.a) - if got && lessBack { - t.Errorf("%s: both a and b report being less than each other", tt.name) - } - wantEqual := !got && !lessBack - gotEqual := tt.a.equal(&tt.b) - if gotEqual != wantEqual { - t.Errorf("%s: equal = %v; want %v", tt.name, gotEqual, wantEqual) - } - } -} - -func TestClose(t *testing.T) { - var p Poller - err := p.Close() - if err != nil { - t.Fatal(err) - } - p = Poller{} - _, _, err = p.Poll() - if err != nil { - t.Skipf("skipping due to poll error: %v", err) - } - err = p.Close() - if err != nil { - t.Fatal(err) - } -} - func BenchmarkGetList(b *testing.B) { benchmarkGetList(b, false) } diff --git a/pkg/util/port/portlist/poller_test.go b/pkg/util/port/portlist/poller_test.go new file mode 100644 index 0000000000000..ff2df90f47e4b --- /dev/null +++ b/pkg/util/port/portlist/poller_test.go @@ -0,0 +1,256 @@ +// Copyright (c) Tailscale Inc & AUTHORS +// SPDX-License-Identifier: BSD-3-Clause +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2014-present Datadog, Inc. + +package portlist + +import ( + "net" + "reflect" + "runtime" + "testing" +) + +func TestEqualLessThan(t *testing.T) { + tests := []struct { + name string + a, b Port + want bool + }{ + { + "Port a < b", + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + Port{Proto: "tcp", Port: 101, Process: "proc1"}, + true, + }, + { + "Port a > b", + Port{Proto: "tcp", Port: 101, Process: "proc1"}, + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + false, + }, + { + "Proto a < b", + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + Port{Proto: "udp", Port: 100, Process: "proc1"}, + true, + }, + { + "Proto a < b", + Port{Proto: "udp", Port: 100, Process: "proc1"}, + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + false, + }, + { + "Process a < b", + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + Port{Proto: "tcp", Port: 100, Process: "proc2"}, + true, + }, + { + "Process a > b", + Port{Proto: "tcp", Port: 100, Process: "proc2"}, + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + false, + }, + { + "Port evaluated first", + Port{Proto: "udp", Port: 100, Process: "proc2"}, + Port{Proto: "tcp", Port: 101, Process: "proc1"}, + true, + }, + { + "Proto evaluated second", + Port{Proto: "tcp", Port: 100, Process: "proc2"}, + Port{Proto: "udp", Port: 100, Process: "proc1"}, + true, + }, + { + "Process evaluated fourth", + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + Port{Proto: "tcp", Port: 100, Process: "proc2"}, + true, + }, + { + "equal", + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + Port{Proto: "tcp", Port: 100, Process: "proc1"}, + false, + }, + } + + for _, tt := range tests { + got := tt.a.lessThan(&tt.b) + if got != tt.want { + t.Errorf("%s: Equal = %v; want %v", tt.name, got, tt.want) + } + lessBack := tt.b.lessThan(&tt.a) + if got && lessBack { + t.Errorf("%s: both a and b report being less than each other", tt.name) + } + wantEqual := !got && !lessBack + gotEqual := tt.a.equal(&tt.b) + if gotEqual != wantEqual { + t.Errorf("%s: equal = %v; want %v", tt.name, gotEqual, wantEqual) + } + } +} + +func TestSortAndDedup(t *testing.T) { + tests := []struct { + name string + input List + expected List + }{ + { + "Simple Case", + List{ + {Port: 80, Proto: "tcp", Process: "nginx"}, + {Port: 443, Proto: "tcp", Process: "nginx"}, + {Port: 80, Proto: "tcp", Process: "apache"}, + {Port: 80, Proto: "udp", Process: "apache"}, + {Port: 443, Proto: "tcp", Process: "nginx"}, + }, + List{ + {Port: 80, Proto: "tcp", Process: "apache"}, + {Port: 80, Proto: "udp", Process: "apache"}, + {Port: 443, Proto: "tcp", Process: "nginx"}, + }, + }, + { + "Already Sorted", + List{ + {Port: 22, Proto: "tcp", Process: "ssh"}, + {Port: 80, Proto: "tcp", Process: "nginx"}, + {Port: 443, Proto: "tcp", Process: "nginx"}, + }, + List{ + {Port: 22, Proto: "tcp", Process: "ssh"}, + {Port: 80, Proto: "tcp", Process: "nginx"}, + {Port: 443, Proto: "tcp", Process: "nginx"}, + }, + }, + { + "No Duplicates", + List{ + {Port: 80, Proto: "tcp", Process: "nginx"}, + {Port: 8080, Proto: "tcp", Process: "node"}, + }, + List{ + {Port: 80, Proto: "tcp", Process: "nginx"}, + {Port: 8080, Proto: "tcp", Process: "node"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := sortAndDedup(tt.input) + if !reflect.DeepEqual(result, tt.expected) { + t.Errorf("sortAndDedup(%v) = %v; want %v", tt.input, result, tt.expected) + } + }) + } +} + +func TestGetList(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on Windows -- not implemented yet") + } + var p Poller + pl, _, err := p.Poll() + if err != nil { + t.Fatal(err) + } + for i, p := range pl { + t.Logf("[%d] %+v", i, p) + } +} + +func TestIgnoreLocallyBoundPorts(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on Windows -- not implemented yet") + } + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Skipf("failed to bind: %v", err) + } + defer ln.Close() + ta := ln.Addr().(*net.TCPAddr) + port := ta.Port + var p Poller + pl, _, err := p.Poll() + if err != nil { + t.Fatal(err) + } + for _, p := range pl { + if p.Proto == "tcp" && int(p.Port) == port { + t.Fatal("didn't expect to find test's localhost ephemeral port") + } + } +} + +func TestPoller(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on Windows -- not implemented yet") + } + var p Poller + p.IncludeLocalhost = true + get := func(t *testing.T) []Port { + t.Helper() + s, _, err := p.Poll() + if err != nil { + t.Fatal(err) + } + return s + } + + p1 := get(t) + ln, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Skipf("failed to bind: %v", err) + } + defer ln.Close() + port := uint16(ln.Addr().(*net.TCPAddr).Port) + containsPort := func(pl List) bool { + for _, p := range pl { + if p.Proto == "tcp" && p.Port == port { + return true + } + } + return false + } + if containsPort(p1) { + t.Error("unexpectedly found ephemeral port in p1, before it was opened", port) + } + p2 := get(t) + if !containsPort(p2) { + t.Error("didn't find ephemeral port in p2", port) + } + ln.Close() + p3 := get(t) + if containsPort(p3) { + t.Error("unexpectedly found ephemeral port in p3, after it was closed", port) + } +} + +func TestClose(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("Skipping test on Windows -- not implemented yet") + } + var p Poller + err := p.Close() + if err != nil { + t.Fatal(err) + } + p = Poller{} + _, _, err = p.Poll() + if err != nil { + t.Skipf("skipping due to poll error: %v", err) + } + err = p.Close() + if err != nil { + t.Fatal(err) + } +} diff --git a/pkg/util/port/portlist/poller_not_macos.go b/pkg/util/port/portlist/poller_windows.go similarity index 68% rename from pkg/util/port/portlist/poller_not_macos.go rename to pkg/util/port/portlist/poller_windows.go index e4be29de87776..ec33188b84ddf 100644 --- a/pkg/util/port/portlist/poller_not_macos.go +++ b/pkg/util/port/portlist/poller_windows.go @@ -3,11 +3,13 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build !darwin +//go:build windows package portlist -import "errors" +import ( + "errors" +) // ErrNotImplemented is the "not implemented" error given by `gopsutil` when an // OS doesn't support and API. Unfortunately it's in an internal package so @@ -16,21 +18,21 @@ var ErrNotImplemented = errors.New("not implemented yet") // init initializes the Poller by ensuring it has an underlying func (p *Poller) init() { - p.os = newOtherOSImpl(p.IncludeLocalhost) + p.os = newWindowsOSImpl(p.IncludeLocalhost) } -type otherOSImpl struct { +type windowsOSImpl struct { includeLocalhost bool } -func newOtherOSImpl(includeLocalhost bool) osImpl { - return &otherOSImpl{ +func newWindowsOSImpl(includeLocalhost bool) osImpl { + return &windowsOSImpl{ includeLocalhost: includeLocalhost, } } -func (im *otherOSImpl) AppendListeningPorts(_ []Port) ([]Port, error) { +func (im *windowsOSImpl) AppendListeningPorts(_ []Port) ([]Port, error) { return nil, ErrNotImplemented } -func (*otherOSImpl) Close() error { return ErrNotImplemented } +func (*windowsOSImpl) Close() error { return ErrNotImplemented } diff --git a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_common_test.go b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_common_test.go index bafa0a9b1d7a2..00feb0f40d510 100644 --- a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_common_test.go +++ b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_common_test.go @@ -45,7 +45,8 @@ func getDiagnoseOutput(v *baseDiagnoseSuite, commandArgs ...agentclient.AgentArg assert.NoError(c, v.Env().FakeIntake.Client().GetServerHealth()) }, 5*time.Minute, 20*time.Second, "timedout waiting for fakeintake to be healthy") - return v.Env().Agent.Client.Diagnose(commandArgs...) + diagnose := v.Env().Agent.Client.Diagnose(commandArgs...) + return diagnose } func (v *baseDiagnoseSuite) TestDiagnoseDefaultConfig() { @@ -65,10 +66,9 @@ func (v *baseDiagnoseSuite) TestDiagnoseList() { } } -func (v *baseDiagnoseSuite) TestDiagnoseInclude() { +func (v *baseDiagnoseSuite) AssertDiagnoseInclude() { diagnose := getDiagnoseOutput(v) diagnoseSummary := getDiagnoseSummary(diagnose) - for _, suite := range allSuites { diagnoseInclude := getDiagnoseOutput(v, agentclient.WithArgs([]string{"--include", suite})) resultInclude := getDiagnoseSummary(diagnoseInclude) @@ -87,7 +87,7 @@ func (v *baseDiagnoseSuite) TestDiagnoseInclude() { assert.Equal(v.T(), diagnoseIncludeEverySuiteSummary, diagnoseSummary) } -func (v *baseDiagnoseSuite) TestDiagnoseExclude() { +func (v *baseDiagnoseSuite) AssertDiagnoseExclude() { for _, suite := range allSuites { diagnoseExclude := getDiagnoseOutput(v, agentclient.WithArgs([]string{"--exclude", suite})) resultExclude := getDiagnoseSummary(diagnoseExclude) diff --git a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_nix_test.go b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_nix_test.go index ef2300c4fc200..6e5cd0fe6ee1b 100644 --- a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_nix_test.go +++ b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_nix_test.go @@ -7,13 +7,15 @@ package diagnose import ( + "slices" "testing" + "github.com/DataDog/test-infra-definitions/components/datadog/agentparams" + "github.com/stretchr/testify/assert" + "github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" svcmanager "github.com/DataDog/datadog-agent/test/new-e2e/tests/agent-platform/common/svc-manager" - "github.com/DataDog/test-infra-definitions/components/datadog/agentparams" - "github.com/stretchr/testify/assert" ) type linuxDiagnoseSuite struct { @@ -42,3 +44,17 @@ func (v *linuxDiagnoseSuite) TestDiagnoseLocalFallback() { svcManager.Start("datadog-agent") } + +func (v *linuxDiagnoseSuite) TestDiagnoseInclude() { + if !slices.Contains(allSuites, "port-conflict") { + allSuites = append(allSuites, "port-conflict") + } + v.AssertDiagnoseInclude() +} + +func (v *linuxDiagnoseSuite) TestDiagnoseExclude() { + if !slices.Contains(allSuites, "port-conflict") { + allSuites = append(allSuites, "port-conflict") + } + v.AssertDiagnoseInclude() +} diff --git a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_win_test.go b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_win_test.go index 3cf5a99a0058e..f2ef75650628e 100644 --- a/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_win_test.go +++ b/test/new-e2e/tests/agent-subcommands/diagnose/diagnose_win_test.go @@ -9,11 +9,12 @@ package diagnose import ( "testing" - "github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" - awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" "github.com/DataDog/test-infra-definitions/components/datadog/agentparams" "github.com/DataDog/test-infra-definitions/components/os" "github.com/DataDog/test-infra-definitions/scenarios/aws/ec2" + + "github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" + awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" ) type windowsDiagnoseSuite struct { @@ -31,3 +32,11 @@ func (v *windowsDiagnoseSuite) TestDiagnoseOtherCmdPort() { diagnose := getDiagnoseOutput(&v.baseDiagnoseSuite) v.AssertOutputNotError(diagnose) } + +func (v *windowsDiagnoseSuite) TestDiagnoseInclude() { + v.AssertDiagnoseInclude() +} + +func (v *windowsDiagnoseSuite) TestDiagnoseExclude() { + v.AssertDiagnoseInclude() +} From 019f214fe186dd61a176d39a720581695782a8bb Mon Sep 17 00:00:00 2001 From: Usama Saqib Date: Thu, 30 May 2024 16:58:48 +0200 Subject: [PATCH 34/54] Fix `minimized-btfs.tar.xz` existence check (#26007) --- tasks/kmt.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tasks/kmt.py b/tasks/kmt.py index 0ee772f75c113..3bc8515a33631 100644 --- a/tasks/kmt.py +++ b/tasks/kmt.py @@ -667,12 +667,14 @@ def prepare( d.copy(ctx, f"{paths.arch_dir}/opt/*", "/opt/", exclude="*.ninja", verbose=verbose) d.run_cmd( ctx, - f"! [ -f {btf_dir}/minimized-btfs.tar.xz \ - && [ -d /opt/btf ] \ + f"[ -f /sys/kernel/btf/vmlinux ] \ + || [ -f {btf_dir}/minimized-btfs.tar.xz ] \ + || ([ -d /opt/btf ] \ && cd /opt/btf/ \ && tar cJf minimized-btfs.tar.xz * \ && mkdir -p {btf_dir} \ - && mv /opt/btf/minimized-btfs.tar.xz {btf_dir}/", + && mv /opt/btf/minimized-btfs.tar.xz {btf_dir}/)", + verbose=verbose, ) info(f"[+] Tests packages setup in target VM {d}") From a0fd230906038e7b575b14d1d118937b2403a468 Mon Sep 17 00:00:00 2001 From: Hasan Mahmood <6599778+hmahmood@users.noreply.github.com> Date: Thu, 30 May 2024 11:06:56 -0400 Subject: [PATCH 35/54] Use random temp path for sysprobe pprof flare test (#26102) --- cmd/agent/subcommands/flare/command_test.go | 42 +++++++++++++++------ 1 file changed, 30 insertions(+), 12 deletions(-) diff --git a/cmd/agent/subcommands/flare/command_test.go b/cmd/agent/subcommands/flare/command_test.go index f25ed6af04676..f199a9a3a59b8 100644 --- a/cmd/agent/subcommands/flare/command_test.go +++ b/cmd/agent/subcommands/flare/command_test.go @@ -11,10 +11,12 @@ import ( "net/http" "net/http/httptest" "net/url" + "path" "runtime" "testing" "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" "github.com/DataDog/datadog-agent/cmd/agent/command" "github.com/DataDog/datadog-agent/comp/core" @@ -24,9 +26,17 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) -const sysprobeSockPath = "/tmp/sysprobe.sock" +type commandTestSuite struct { + suite.Suite + sysprobeSocketPath string +} + +func (c *commandTestSuite) SetupSuite() { + t := c.T() + c.sysprobeSocketPath = path.Join(t.TempDir(), "sysprobe.sock") +} -func getPprofTestServer(t *testing.T) (tcpServer *httptest.Server, unixServer *httptest.Server) { +func getPprofTestServer(t *testing.T, utsPath string) (tcpServer *httptest.Server, unixServer *httptest.Server) { handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch r.URL.Path { case "/debug/pprof/heap": @@ -51,8 +61,8 @@ func getPprofTestServer(t *testing.T) (tcpServer *httptest.Server, unixServer *h if runtime.GOOS == "linux" { unixServer = httptest.NewUnstartedServer(handler) var err error - unixServer.Listener, err = net.Listen("unix", sysprobeSockPath) - require.NoError(t, err, "could not create listener for unix socket /tmp/sysprobe.sock") + unixServer.Listener, err = net.Listen("unix", utsPath) + require.NoError(t, err, "could not create listener for unix socket on %s", utsPath) unixServer.Start() return tcpServer, unixServer @@ -61,8 +71,13 @@ func getPprofTestServer(t *testing.T) (tcpServer *httptest.Server, unixServer *h return tcpServer, tcpServer } -func TestReadProfileData(t *testing.T) { - ts, uts := getPprofTestServer(t) +func TestCommandTestSuite(t *testing.T) { + suite.Run(t, &commandTestSuite{}) +} + +func (c *commandTestSuite) TestReadProfileData() { + t := c.T() + ts, uts := getPprofTestServer(t, c.sysprobeSocketPath) t.Cleanup(func() { ts.Close() uts.Close() @@ -85,7 +100,7 @@ func TestReadProfileData(t *testing.T) { if runtime.GOOS == "windows" { mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", u.Host) } else { - mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", sysprobeSockPath) + mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", c.sysprobeSocketPath) } data, err := readProfileData(10) @@ -134,8 +149,9 @@ func TestReadProfileData(t *testing.T) { } } -func TestReadProfileDataNoTraceAgent(t *testing.T) { - ts, uts := getPprofTestServer(t) +func (c *commandTestSuite) TestReadProfileDataNoTraceAgent() { + t := c.T() + ts, uts := getPprofTestServer(t, c.sysprobeSocketPath) t.Cleanup(func() { ts.Close() uts.Close() @@ -158,7 +174,7 @@ func TestReadProfileDataNoTraceAgent(t *testing.T) { if runtime.GOOS == "windows" { mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", u.Host) } else { - mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", sysprobeSockPath) + mockSysProbeConfig.SetWithoutSource("system_probe_config.sysprobe_socket", c.sysprobeSocketPath) } data, err := readProfileData(10) @@ -202,7 +218,8 @@ func TestReadProfileDataNoTraceAgent(t *testing.T) { } } -func TestReadProfileDataErrors(t *testing.T) { +func (c *commandTestSuite) TestReadProfileDataErrors() { + t := c.T() mockConfig := config.Mock(t) mockConfig.SetWithoutSource("apm_config.enabled", true) mockConfig.SetWithoutSource("apm_config.debug.port", 0) @@ -213,7 +230,8 @@ func TestReadProfileDataErrors(t *testing.T) { require.Len(t, data, 0) } -func TestCommand(t *testing.T) { +func (c *commandTestSuite) TestCommand() { + t := c.T() fxutil.TestOneShotSubcommand(t, Commands(&command.GlobalParams{}), []string{"flare", "1234"}, From cab6958397a8c285f8767fa29bbb4550d5497a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Thu, 30 May 2024 17:47:30 +0200 Subject: [PATCH 36/54] Cleanup build and packaging (#26121) --- .gitlab/package_build/linux.yml | 30 +++---- .gitlab/packaging/deb.yml | 103 ++++++++-------------- .gitlab/packaging/rpm.yml | 151 +++++++++----------------------- 3 files changed, 87 insertions(+), 197 deletions(-) diff --git a/.gitlab/package_build/linux.yml b/.gitlab/package_build/linux.yml index 725dd53c5fd8a..d6e16884241a5 100644 --- a/.gitlab/package_build/linux.yml +++ b/.gitlab/package_build/linux.yml @@ -60,41 +60,35 @@ variables: PACKAGE_ARCH: arm64 -# build Agent 6 binaries for x86_64 -datadog-agent-6-x64: - extends: [.agent_build_common, .agent_build_x86] +.agent_6_build: variables: AGENT_MAJOR_VERSION: 6 PYTHON_RUNTIMES: "2,3" before_script: - export RELEASE_VERSION=$RELEASE_VERSION_6 -# build Agent 7 binaries for x86_64 -datadog-agent-7-x64: - extends: [.agent_build_common, .agent_build_x86] +.agent_7_build: variables: AGENT_MAJOR_VERSION: 7 PYTHON_RUNTIMES: "3" before_script: - export RELEASE_VERSION=$RELEASE_VERSION_7 +# build Agent 6 binaries for x86_64 +datadog-agent-6-x64: + extends: [.agent_build_common, .agent_build_x86, .agent_6_build] + +# build Agent 7 binaries for x86_64 +datadog-agent-7-x64: + extends: [.agent_build_common, .agent_build_x86, .agent_7_build] + # build Agent 6 binaries for arm64 datadog-agent-6-arm64: - extends: [.agent_build_common, .agent_build_arm64] - variables: - AGENT_MAJOR_VERSION: 6 - PYTHON_RUNTIMES: "2,3" - before_script: - - export RELEASE_VERSION=$RELEASE_VERSION_6 + extends: [.agent_build_common, .agent_build_arm64, .agent_6_build] # build Agent 7 binaries for arm64 datadog-agent-7-arm64: - extends: [.agent_build_common, .agent_build_arm64] - variables: - AGENT_MAJOR_VERSION: 7 - PYTHON_RUNTIMES: "3" - before_script: - - export RELEASE_VERSION=$RELEASE_VERSION_7 + extends: [.agent_build_common, .agent_build_arm64, .agent_7_build] .iot-agent-common: extends: .agent_build_common diff --git a/.gitlab/packaging/deb.yml b/.gitlab/packaging/deb.yml index fb2ddc7ca2330..9187935af85c5 100644 --- a/.gitlab/packaging/deb.yml +++ b/.gitlab/packaging/deb.yml @@ -22,108 +22,95 @@ cache: - !reference [.cache_omnibus_ruby_deps, cache] +.package_deb_x86: + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + variables: + DD_PKG_ARCH: x86_64 + PACKAGE_ARCH: amd64 + +.package_deb_arm64: + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES + tags: ["arch:arm64"] + variables: + PACKAGE_ARCH: arm64 + DD_PKG_ARCH: arm64 + +.package_deb_agent_6: + variables: + RELEASE_VERSION: $RELEASE_VERSION_6 + AGENT_MAJOR_VERSION: 6 + +.package_deb_agent_7: + variables: + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 + agent_deb-x64-a6: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_x86, .package_deb_agent_6] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] needs: ["datadog-agent-6-x64"] variables: - PACKAGE_ARCH: amd64 DESTINATION_DEB: "datadog-agent_6_amd64.deb" - DD_PKG_ARCH: "x86_64" DD_PROJECT: "agent" - RELEASE_VERSION: $RELEASE_VERSION_6 - AGENT_MAJOR_VERSION: 6 agent_deb-x64-a7: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_x86, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] needs: ["datadog-agent-7-x64"] variables: - PACKAGE_ARCH: amd64 DESTINATION_DEB: "datadog-agent_7_amd64.deb" - DD_PKG_ARCH: "x86_64" DD_PROJECT: "agent" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 agent_deb-arm64-a6: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_arm64, .package_deb_agent_6] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] needs: ["datadog-agent-6-arm64"] variables: - PACKAGE_ARCH: arm64 DESTINATION_DEB: "datadog-agent_6_arm64.deb" - DD_PKG_ARCH: "arm64" DD_PROJECT: "agent" - RELEASE_VERSION: $RELEASE_VERSION_6 - AGENT_MAJOR_VERSION: 6 agent_deb-arm64-a7: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_arm64, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] needs: ["datadog-agent-7-arm64"] variables: - PACKAGE_ARCH: arm64 DESTINATION_DEB: "datadog-agent_7_arm64.deb" - DD_PKG_ARCH: "arm64" DD_PROJECT: "agent" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_deb-amd64: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_x86, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] needs: ["installer-amd64"] variables: - PACKAGE_ARCH: amd64 DESTINATION_DEB: "datadog-installer_7_amd64.deb" - DD_PKG_ARCH: "x86_64" DD_PROJECT: "installer" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_deb-arm64: - extends: .package_deb_common + extends: [.package_deb_common, .package_deb_arm64, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] needs: ["installer-arm64"] variables: - PACKAGE_ARCH: arm64 DESTINATION_DEB: "datadog-installer_7_arm64.deb" - DD_PKG_ARCH: "arm64" DD_PROJECT: "installer" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 .package_iot_deb_common: + extends: [.package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - before_script: - - export RELEASE_VERSION=$RELEASE_VERSION_7 stage: packaging script: - source /root/.bashrc @@ -147,23 +134,15 @@ installer_deb-arm64: - !reference [.cache_omnibus_ruby_deps, cache] iot_agent_deb-x64: - extends: .package_iot_deb_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_iot_deb_common, .package_deb_x86] needs: ["iot-agent-x64"] variables: - PACKAGE_ARCH: amd64 - DD_PKG_ARCH: "x86_64" DESTINATION_DEB: "datadog-iot-agent_7_amd64.deb" iot_agent_deb-arm64: - extends: .package_iot_deb_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_iot_deb_common, .package_deb_arm64] needs: ["iot-agent-arm64"] variables: - PACKAGE_ARCH: arm64 - DD_PKG_ARCH: "arm64" DESTINATION_DEB: "datadog-iot-agent_7_arm64.deb" iot_agent_deb-armhf: @@ -178,34 +157,22 @@ iot_agent_deb-armhf: FORCED_PACKAGE_COMPRESSION_LEVEL: 5 dogstatsd_deb-x64: - extends: .package_deb_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + extends: [.package_deb_common, .package_deb_x86, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - tags: ["arch:amd64"] needs: ["dogstatsd-x64"] variables: DD_PROJECT: dogstatsd - PACKAGE_ARCH: amd64 - DD_PKG_ARCH: "x86_64" DESTINATION_DEB: "datadog-dogstatsd_amd64.deb" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 dogstatsd_deb-arm64: - extends: .package_deb_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/deb_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES + extends: [.package_deb_common, .package_deb_arm64, .package_deb_agent_7] rules: - !reference [.except_mergequeue] - when: on_success - tags: ["arch:arm64"] needs: ["dogstatsd-arm64"] variables: DD_PROJECT: dogstatsd - PACKAGE_ARCH: arm64 - DD_PKG_ARCH: "arm64" DESTINATION_DEB: "datadog-dogstatsd_arm64.deb" - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 diff --git a/.gitlab/packaging/rpm.yml b/.gitlab/packaging/rpm.yml index 7226183c766d3..c09cceafa6e2e 100644 --- a/.gitlab/packaging/rpm.yml +++ b/.gitlab/packaging/rpm.yml @@ -29,6 +29,30 @@ cache: - !reference [.cache_omnibus_ruby_deps, cache] +.package_rpm_x86: + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + tags: ["arch:amd64"] + variables: + DD_PKG_ARCH: x86_64 + PACKAGE_ARCH: amd64 + +.package_rpm_arm64: + image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES + tags: ["arch:arm64"] + variables: + PACKAGE_ARCH: arm64 + DD_PKG_ARCH: arm64 + +.package_rpm_agent_6: + variables: + RELEASE_VERSION: $RELEASE_VERSION_6 + AGENT_MAJOR_VERSION: 6 + +.package_rpm_agent_7: + variables: + RELEASE_VERSION: $RELEASE_VERSION_7 + AGENT_MAJOR_VERSION: 7 + .package_suse_rpm_common: extends: .package_rpm_common script: @@ -42,136 +66,71 @@ OMNIBUS_EXTRA_ARGS: "--host-distribution=suse" agent_rpm-x64-a6: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_rpm_common, .package_rpm_agent_6, .package_rpm_x86] needs: ["datadog-agent-6-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: agent - RELEASE_VERSION: $RELEASE_VERSION_6 - AGENT_MAJOR_VERSION: 6 agent_rpm-x64-a7: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_x86] tags: ["arch:amd64"] needs: ["datadog-agent-7-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: agent - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 agent_rpm-arm64-a6: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_rpm_common, .package_rpm_agent_6, .package_rpm_arm64] needs: ["datadog-agent-6-arm64"] variables: - PACKAGE_ARCH: arm64 - DD_PKG_ARCH: "arm64" DD_PROJECT: agent - RELEASE_VERSION: $RELEASE_VERSION_6 - AGENT_MAJOR_VERSION: 6 agent_rpm-arm64-a7: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] needs: ["datadog-agent-7-arm64"] variables: - PACKAGE_ARCH: arm64 - DD_PKG_ARCH: "arm64" DD_PROJECT: agent - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 agent_suse-x64-a6: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_6, .package_rpm_x86] needs: ["datadog-agent-6-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PRODUCT: agent - RELEASE_VERSION: $RELEASE_VERSION_6 - AGENT_MAJOR_VERSION: 6 agent_suse-x64-a7: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_x86] needs: ["datadog-agent-7-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PRODUCT: agent - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 agent_suse-arm64-a7: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] needs: ["datadog-agent-7-arm64"] variables: - PACKAGE_ARCH: arm64 - DD_PKG_ARCH: "arm64" DD_PRODUCT: agent - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_rpm-amd64: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_x86] needs: ["installer-amd64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: installer - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_rpm-arm64: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] needs: ["installer-arm64"] variables: - PACKAGE_ARCH: aarch64 - DD_PKG_ARCH: "arm64" DD_PROJECT: installer - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_suse_rpm-amd64: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_x86] needs: ["installer-amd64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: installer - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 installer_suse_rpm-arm64: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_arm64] needs: ["installer-arm64"] variables: - PACKAGE_ARCH: aarch64 - DD_PKG_ARCH: "arm64" DD_PROJECT: installer - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 .package_iot_rpm_common: rules: @@ -203,24 +162,12 @@ installer_suse_rpm-arm64: - !reference [.cache_omnibus_ruby_deps, cache] iot_agent_rpm-x64: - extends: .package_iot_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_iot_rpm_common, .package_rpm_x86] needs: ["iot-agent-x64"] - variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" - DD_PROJECT: agent iot_agent_rpm-arm64: - extends: .package_iot_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_arm64$DATADOG_AGENT_ARMBUILDIMAGES_SUFFIX:$DATADOG_AGENT_ARMBUILDIMAGES - tags: ["arch:arm64"] + extends: [.package_iot_rpm_common, .package_rpm_arm64] needs: ["iot-agent-arm64"] - variables: - PACKAGE_ARCH: aarch64 - DD_PKG_ARCH: "arm64" - DD_PROJECT: agent iot_agent_rpm-armhf: extends: .package_iot_rpm_common @@ -231,15 +178,12 @@ iot_agent_rpm-armhf: variables: PACKAGE_ARCH: armhf DD_PKG_ARCH: "arm64" - DD_PROJECT: agent before_script: # Ensures uname -m reports armv7l - export LD_PRELOAD="/usr/local/lib/libfakearmv7l.so" iot_agent_suse-x64: - extends: .package_iot_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_iot_rpm_common, .package_rpm_x86] needs: ["iot-agent-x64"] script: # Don't simply redefine OMNIBUS_PACKAGE_DIR since it also defines where the input @@ -252,33 +196,18 @@ iot_agent_suse-x64: paths: - $OMNIBUS_PACKAGE_DIR_SUSE variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" - DD_PROJECT: agent OMNIBUS_EXTRA_ARGS: "--host-distribution=suse" cache: - !reference [.cache_omnibus_ruby_deps, cache] dogstatsd_rpm-x64: - extends: .package_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_rpm_common, .package_rpm_agent_7, .package_rpm_x86] needs: ["dogstatsd-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: dogstatsd - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 dogstatsd_suse-x64: - extends: .package_suse_rpm_common - image: 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/datadog-agent-buildimages/rpm_x64$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES - tags: ["arch:amd64"] + extends: [.package_suse_rpm_common, .package_rpm_agent_7, .package_rpm_x86] needs: ["dogstatsd-x64"] variables: - PACKAGE_ARCH: x86_64 - DD_PKG_ARCH: "x86_64" DD_PROJECT: dogstatsd - RELEASE_VERSION: $RELEASE_VERSION_7 - AGENT_MAJOR_VERSION: 7 From babd88d25a03c59f47a597efea9ef56ea94b1d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Juli=C3=A1n?= Date: Thu, 30 May 2024 17:48:12 +0200 Subject: [PATCH 37/54] [EBPF] Bump KMT images (#25954) --- .../system-probe/config/platforms.json | 115 ++++++++++-------- 1 file changed, 63 insertions(+), 52 deletions(-) diff --git a/test/new-e2e/system-probe/config/platforms.json b/test/new-e2e/system-probe/config/platforms.json index 2eabf114a7c8e..52ed9e8e168d5 100644 --- a/test/new-e2e/system-probe/config/platforms.json +++ b/test/new-e2e/system-probe/config/platforms.json @@ -7,7 +7,7 @@ "kernel": "6.0.7-301.fc37", "os_version": "37", "image": "Fedora-Cloud-Base-37.x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "fedora_38": { "os_name": "Fedora Linux", @@ -15,7 +15,7 @@ "kernel": "6.2.9-300.fc38", "os_version": "38", "image": "Fedora-Cloud-Base-38.x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_4.14": { "os_name": "Amazon Linux", @@ -23,7 +23,7 @@ "kernel": "4.14.314-237.533.amzn2", "os_version": "2", "image": "amzn2-x86_64-4.14.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_5.10": { "os_name": "Amazon Linux", @@ -31,7 +31,7 @@ "kernel": "5.10.216-204.855.amzn2", "os_version": "2", "image": "amzn2-x86_64-5.10.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_5.4": { "os_name": "Amazon Linux", @@ -39,7 +39,7 @@ "kernel": "5.4.275-189.375.amzn2", "os_version": "2", "image": "amzn2-x86_64-5.4.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_2023": { "os_name": "Amazon Linux", @@ -47,7 +47,7 @@ "kernel": "6.1.77-99.164.amzn2023", "os_version": "2023", "image": "amzn2023-amd64-2023.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "centos_7.9": { "os_name": "CentOS Linux", @@ -55,15 +55,15 @@ "kernel": "3.10.0-1160.80.1.el7", "os_version": "7.9.2009", "image": "centos-7.9-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "centos_8": { "os_name": "CentOS Stream", "os_id": "centos", - "kernel": "4.18.0-545.el8", + "kernel": "4.18.0-552.3.1.el8", "os_version": "8", "image": "centos-8-x86_64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "debian_10": { "os_name": "Debian GNU/Linux", @@ -71,7 +71,7 @@ "kernel": "4.19.0-26", "os_version": "10", "image": "debian-10-generic-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "buster" ] @@ -82,7 +82,7 @@ "kernel": "5.10.0-28", "os_version": "11", "image": "debian-11-generic-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bullseye" ] @@ -93,7 +93,7 @@ "kernel": "6.1.0-18", "os_version": "12", "image": "debian-12-generic-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bookworm" ] @@ -104,7 +104,7 @@ "kernel": "3.10.0-1160.105.1.0.1.el7", "os_version": "7.9", "image": "oracle-7.9-x86_64.qcow.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "oracle_8.9": { "os_name": "Oracle Linux Server", @@ -112,7 +112,7 @@ "kernel": "5.15.0-200.131.27.el8uek", "os_version": "8.9", "image": "oracle-8.9-x86_64.qcow.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "oracle_9.3": { "os_name": "Oracle Linux Server", @@ -120,7 +120,7 @@ "kernel": "5.15.0-200.131.27.el9uek", "os_version": "9.3", "image": "oracle-9.3-x86_64.qcow.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "rocky_8.5": { "os_name": "Rocky Linux", @@ -128,7 +128,7 @@ "kernel": "4.18.0-348.el8.0.2", "os_version": "8.5", "image": "rocky-8.5-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "rocky_9.3": { "os_name": "Rocky Linux", @@ -136,7 +136,7 @@ "kernel": "5.14.0-362.8.1.el9_3", "os_version": "9.3", "image": "rocky-9.3-x86_64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "ubuntu_16.04": { "os_name": "Ubuntu", @@ -144,7 +144,7 @@ "kernel": "4.4.0-210-generic", "os_version": "16.04", "image": "ubuntu-16.04-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "xenial" ] @@ -155,7 +155,7 @@ "kernel": "4.18.0-25-generic", "os_version": "18.04", "image": "ubuntu-18.04-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bionic" ] @@ -166,7 +166,7 @@ "kernel": "5.4.0-167-generic", "os_version": "20.04", "image": "ubuntu-20.04-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "focal" ] @@ -177,7 +177,7 @@ "kernel": "5.15.0-91-generic", "os_version": "22.04", "image": "ubuntu-22.04-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "jammy" ] @@ -188,7 +188,7 @@ "kernel": "6.5.0-9-generic", "os_version": "23.10", "image": "ubuntu-23.10-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "mantic" ] @@ -199,7 +199,7 @@ "kernel": "6.8.0-31-generic", "os_version": "24.04", "image": "ubuntu-24.04-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "noble" ] @@ -210,15 +210,15 @@ "kernel": "5.3.18-150300.59.106.1", "os_version": "15.3", "image": "opensuse-15.3-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "opensuse_15.5": { "os_name": "openSUSE Leap", "os_id": "opensuse-leap", - "kernel": "5.14.21-150500.55.52.1", + "kernel": "5.14.21-150500.55.65.1", "os_version": "15.5", "image": "opensuse-15.5-x86_64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "suse_12.5": { "os_name": "SLES", @@ -226,7 +226,18 @@ "kernel": "4.12.14-122.212.1", "os_version": "12.5", "image": "suse-12.5-x86_64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" + }, + "debian_9": { + "os_name": "Debian GNU/Linux", + "os_id": "debian", + "kernel": "4.9.0-19", + "os_version": "9", + "image": "debian-9-generic-x86_64.qcow2.xz", + "image_version": "20240529_dd00b86a", + "alt_version_names": [ + "stretch" + ] } }, "arm64": { @@ -236,7 +247,7 @@ "kernel": "6.0.7-301.fc37", "os_version": "37", "image": "Fedora-Cloud-Base-37.arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "fedora_38": { "os_name": "Fedora Linux", @@ -244,7 +255,7 @@ "kernel": "6.2.9-300.fc38", "os_version": "38", "image": "Fedora-Cloud-Base-38.arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_4.14": { "os_name": "Amazon Linux", @@ -252,7 +263,7 @@ "kernel": "4.14.314-237.533.amzn2", "os_version": "2", "image": "amzn2-arm64-4.14.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_5.4": { "os_name": "Amazon Linux", @@ -260,7 +271,7 @@ "kernel": "5.4.275-189.375.amzn2", "os_version": "2", "image": "amzn2-arm64-5.4.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "centos_7.9": { "os_name": "CentOS Linux", @@ -268,15 +279,15 @@ "kernel": "4.18.0-348.20.1.el7", "os_version": "7.9.2009", "image": "centos-7.9-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "centos_8": { "os_name": "CentOS Stream", "os_id": "centos", - "kernel": "4.18.0-545.el8", + "kernel": "4.18.0-552.3.1.el8", "os_version": "8", "image": "centos-8-arm64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "debian_10": { "os_name": "Debian GNU/Linux", @@ -284,7 +295,7 @@ "kernel": "4.19.0-26", "os_version": "10", "image": "debian-10-generic-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "buster" ] @@ -295,7 +306,7 @@ "kernel": "5.10.0-28", "os_version": "11", "image": "debian-11-generic-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bullseye" ] @@ -306,7 +317,7 @@ "kernel": "6.1.0-18", "os_version": "12", "image": "debian-12-generic-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bookworm" ] @@ -317,7 +328,7 @@ "kernel": "5.15.0-200.131.27.el8uek", "os_version": "8.9", "image": "oracle-8.9-arm64.qcow.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "oracle_9.3": { "os_name": "Oracle Linux Server", @@ -325,7 +336,7 @@ "kernel": "5.15.0-200.131.27.el9uek", "os_version": "9.3", "image": "oracle-9.3-arm64.qcow.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "rocky_8.5": { "os_name": "Rocky Linux", @@ -333,7 +344,7 @@ "kernel": "4.18.0-348.el8.0.2", "os_version": "8.5", "image": "rocky-8.5-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "rocky_9.3": { "os_name": "Rocky Linux", @@ -341,7 +352,7 @@ "kernel": "5.14.0-362.8.1.el9_3", "os_version": "9.3", "image": "rocky-9.3-arm64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "amazon_5.10": { "os_name": "Amazon Linux", @@ -349,7 +360,7 @@ "kernel": "5.10.216-204.855.amzn2", "os_version": "2", "image": "amzn2-arm64-5.10.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "amazon_2023": { "os_name": "Amazon Linux", @@ -357,7 +368,7 @@ "kernel": "6.1.77-99.164.amzn2023", "os_version": "2023", "image": "amzn2023-arm64-2023.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "ubuntu_18.04": { "os_name": "Ubuntu", @@ -365,7 +376,7 @@ "kernel": "4.18.0-25-generic", "os_version": "18.04", "image": "ubuntu-18.04-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "bionic" ] @@ -376,7 +387,7 @@ "kernel": "5.4.0-167-generic", "os_version": "20.04", "image": "ubuntu-20.04-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "focal" ] @@ -387,7 +398,7 @@ "kernel": "5.15.0-91-generic", "os_version": "22.04", "image": "ubuntu-22.04-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "jammy" ] @@ -398,7 +409,7 @@ "kernel": "6.5.0-9-generic", "os_version": "23.10", "image": "ubuntu-23.10-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "mantic" ] @@ -409,7 +420,7 @@ "kernel": "6.8.0-31-generic", "os_version": "24.04", "image": "ubuntu-24.04-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e", + "image_version": "20240529_dd00b86a", "alt_version_names": [ "noble" ] @@ -420,15 +431,15 @@ "kernel": "5.3.18-150300.59.106.1", "os_version": "15.3", "image": "opensuse-15.3-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" }, "opensuse_15.5": { "os_name": "openSUSE Leap", "os_id": "opensuse-leap", - "kernel": "5.14.21-150500.55.52.1", + "kernel": "5.14.21-150500.55.65.1", "os_version": "15.5", "image": "opensuse-15.5-arm64.qcow2.xz", - "image_version": "20240430_7bbb4ede" + "image_version": "20240529_dd00b86a" }, "suse_12.5": { "os_name": "SLES", @@ -436,7 +447,7 @@ "kernel": "4.12.14-122.212.1", "os_version": "12.5", "image": "suse-12.5-arm64.qcow2.xz", - "image_version": "20240523_238eeb4e" + "image_version": "20240529_dd00b86a" } } } \ No newline at end of file From e4d70c33efd115028374e876fd1713565e1d9ed5 Mon Sep 17 00:00:00 2001 From: Daniel Tafoya <63120739+daniel-taf@users.noreply.github.com> Date: Thu, 30 May 2024 11:56:07 -0400 Subject: [PATCH 38/54] [PROCS-4045] Move process agent statsd client initialization into process agent component (#26096) --- cmd/agent/subcommands/run/command.go | 3 +++ cmd/process-agent/command/main_common.go | 8 +------ comp/process/agent/agentimpl/agent.go | 23 +++++++++++++++---- .../agent/agentimpl/agent_linux_test.go | 10 ++++++-- comp/process/agent/agentimpl/agent_test.go | 2 ++ comp/process/bundle_test.go | 3 +++ pkg/process/checks/container.go | 4 +++- pkg/process/checks/process.go | 6 +++-- ...ss-statsd-client-fix-66b11cc991a9f6be.yaml | 11 +++++++++ 9 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 releasenotes/notes/process-statsd-client-fix-66b11cc991a9f6be.yaml diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index cecf2b74ddc1a..37982354bf397 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -83,6 +83,7 @@ import ( replay "github.com/DataDog/datadog-agent/comp/dogstatsd/replay/def" dogstatsdServer "github.com/DataDog/datadog-agent/comp/dogstatsd/server" dogstatsddebug "github.com/DataDog/datadog-agent/comp/dogstatsd/serverDebug" + "github.com/DataDog/datadog-agent/comp/dogstatsd/statsd" dogstatsdStatusimpl "github.com/DataDog/datadog-agent/comp/dogstatsd/status/statusimpl" "github.com/DataDog/datadog-agent/comp/forwarder" "github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder" @@ -219,6 +220,7 @@ func run(log log.Component, demultiplexer demultiplexer.Component, sharedSerializer serializer.MetricSerializer, logsAgent optional.Option[logsAgent.Component], + _ statsd.Component, processAgent processAgent.Component, otelcollector otelcollector.Component, _ host.Component, @@ -366,6 +368,7 @@ func getSharedFxOption() fx.Option { traceagentStatusImpl.Module(), processagentStatusImpl.Module(), dogstatsdStatusimpl.Module(), + statsd.Module(), statusimpl.Module(), authtokenimpl.Module(), apiimpl.Module(), diff --git a/cmd/process-agent/command/main_common.go b/cmd/process-agent/command/main_common.go index 1fde7fa35abc5..005f27a65803d 100644 --- a/cmd/process-agent/command/main_common.go +++ b/cmd/process-agent/command/main_common.go @@ -54,7 +54,6 @@ import ( ddconfig "github.com/DataDog/datadog-agent/pkg/config" commonsettings "github.com/DataDog/datadog-agent/pkg/config/settings" "github.com/DataDog/datadog-agent/pkg/process/metadata/workloadmeta/collector" - "github.com/DataDog/datadog-agent/pkg/process/statsd" "github.com/DataDog/datadog-agent/pkg/process/util" ddutil "github.com/DataDog/datadog-agent/pkg/util" "github.com/DataDog/datadog-agent/pkg/util/fxutil" @@ -303,7 +302,6 @@ type miscDeps struct { Lc fx.Lifecycle Config config.Component - Statsd compstatsd.Component Syscfg sysprobeconfig.Component HostInfo hostinfo.Component WorkloadMeta workloadmeta.Component @@ -311,13 +309,9 @@ type miscDeps struct { } // initMisc initializes modules that cannot, or have not yet been componetized. -// Todo: (Components) WorkloadMeta, remoteTagger, statsd +// Todo: (Components) WorkloadMeta, remoteTagger // Todo: move metadata/workloadmeta/collector to workloadmeta func initMisc(deps miscDeps) error { - if err := statsd.Configure(ddconfig.GetBindHost(), deps.Config.GetInt("dogstatsd_port"), deps.Statsd.CreateForHostPort); err != nil { - deps.Logger.Criticalf("Error configuring statsd: %s", err) - return err - } if err := ddutil.SetupCoreDump(deps.Config); err != nil { deps.Logger.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) diff --git a/comp/process/agent/agentimpl/agent.go b/comp/process/agent/agentimpl/agent.go index 2fde222f59cfc..29e9e4787290b 100644 --- a/comp/process/agent/agentimpl/agent.go +++ b/comp/process/agent/agentimpl/agent.go @@ -14,13 +14,16 @@ import ( logComponent "github.com/DataDog/datadog-agent/comp/core/log" statusComponent "github.com/DataDog/datadog-agent/comp/core/status" "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + statsdComp "github.com/DataDog/datadog-agent/comp/dogstatsd/statsd" "github.com/DataDog/datadog-agent/comp/process/agent" expvars "github.com/DataDog/datadog-agent/comp/process/expvars/expvarsimpl" "github.com/DataDog/datadog-agent/comp/process/hostinfo" "github.com/DataDog/datadog-agent/comp/process/runner" submitterComp "github.com/DataDog/datadog-agent/comp/process/submitter" "github.com/DataDog/datadog-agent/comp/process/types" + ddconfig "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/process/checks" + processStatsd "github.com/DataDog/datadog-agent/pkg/process/statsd" "github.com/DataDog/datadog-agent/pkg/util/flavor" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -52,6 +55,7 @@ type dependencies struct { Submitter submitterComp.Component SysProbeConfig sysprobeconfig.Component HostInfo hostinfo.Component + Statsd statsdComp.Component } type processAgent struct { @@ -69,13 +73,13 @@ type provides struct { FlareProvider flaretypes.Provider } -func newProcessAgent(deps dependencies) provides { +func newProcessAgent(deps dependencies) (provides, error) { if !agent.Enabled(deps.Config, deps.Checks, deps.Log) { return provides{ Comp: processAgent{ enabled: false, }, - } + }, nil } enabledChecks := make([]checks.Check, 0, len(deps.Checks)) @@ -93,7 +97,16 @@ func newProcessAgent(deps dependencies) provides { Comp: processAgent{ enabled: false, }, - } + }, nil + } + + if err := processStatsd.Configure(ddconfig.GetBindHost(), deps.Config.GetInt("dogstatsd_port"), deps.Statsd.CreateForHostPort); err != nil { + deps.Log.Criticalf("Error configuring statsd for process-agent: %s", err) + return provides{ + Comp: processAgent{ + enabled: false, + }, + }, err } processAgentComponent := processAgent{ @@ -114,10 +127,10 @@ func newProcessAgent(deps dependencies) provides { Comp: processAgentComponent, StatusProvider: statusComponent.NewInformationProvider(agent.NewStatusProvider(deps.Config)), FlareProvider: flaretypes.NewProvider(processAgentComponent.flarehelper.FillFlare), - } + }, nil } - return provides{Comp: processAgentComponent} + return provides{Comp: processAgentComponent}, nil } // Enabled determines whether the process agent is enabled based on the configuration. diff --git a/comp/process/agent/agentimpl/agent_linux_test.go b/comp/process/agent/agentimpl/agent_linux_test.go index ad627fc3656bd..401c9d2c63d41 100644 --- a/comp/process/agent/agentimpl/agent_linux_test.go +++ b/comp/process/agent/agentimpl/agent_linux_test.go @@ -19,6 +19,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" "github.com/DataDog/datadog-agent/comp/core/telemetry" "github.com/DataDog/datadog-agent/comp/core/telemetry/telemetryimpl" + "github.com/DataDog/datadog-agent/comp/dogstatsd/statsd" "github.com/DataDog/datadog-agent/comp/process/agent" "github.com/DataDog/datadog-agent/comp/process/hostinfo/hostinfoimpl" "github.com/DataDog/datadog-agent/comp/process/processcheck/processcheckimpl" @@ -117,6 +118,7 @@ func TestProcessAgentComponentOnLinux(t *testing.T) { submitterimpl.MockModule(), taggerimpl.MockModule(), telemetryimpl.Module(), + statsd.MockModule(), Module(), fx.Replace(configComp.MockParams{Overrides: map[string]interface{}{ @@ -178,6 +180,7 @@ func TestStatusProvider(t *testing.T) { submitterimpl.MockModule(), taggerimpl.MockModule(), telemetryimpl.Module(), + statsd.MockModule(), Module(), fx.Replace(configComp.MockParams{Overrides: map[string]interface{}{ "process_config.run_in_core_agent.enabled": true, @@ -196,8 +199,9 @@ func TestStatusProvider(t *testing.T) { } }), )) - provides := newProcessAgent(deps) + provides, err := newProcessAgent(deps) assert.IsType(t, tc.expected, provides.StatusProvider.Provider) + assert.NoError(t, err) }) } } @@ -215,6 +219,7 @@ func TestTelemetryCoreAgent(t *testing.T) { hostinfoimpl.MockModule(), submitterimpl.MockModule(), taggerimpl.MockModule(), + statsd.MockModule(), Module(), fx.Replace(configComp.MockParams{Overrides: map[string]interface{}{ "process_config.run_in_core_agent.enabled": true, @@ -234,7 +239,8 @@ func TestTelemetryCoreAgent(t *testing.T) { } }), )) - _ = newProcessAgent(deps) + _, err := newProcessAgent(deps) + assert.NoError(t, err) tel := fxutil.Test[telemetry.Mock](t, telemetryimpl.MockModule()) tel.Reset() diff --git a/comp/process/agent/agentimpl/agent_test.go b/comp/process/agent/agentimpl/agent_test.go index 0d3c73d9e4aeb..2daf319069c81 100644 --- a/comp/process/agent/agentimpl/agent_test.go +++ b/comp/process/agent/agentimpl/agent_test.go @@ -15,6 +15,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/tagger/taggerimpl" "github.com/DataDog/datadog-agent/comp/core/telemetry/telemetryimpl" + "github.com/DataDog/datadog-agent/comp/dogstatsd/statsd" "github.com/DataDog/datadog-agent/comp/process/agent" "github.com/DataDog/datadog-agent/comp/process/hostinfo/hostinfoimpl" "github.com/DataDog/datadog-agent/comp/process/processcheck/processcheckimpl" @@ -65,6 +66,7 @@ func TestProcessAgentComponent(t *testing.T) { submitterimpl.MockModule(), telemetryimpl.Module(), taggerimpl.MockModule(), + statsd.MockModule(), Module(), } diff --git a/comp/process/bundle_test.go b/comp/process/bundle_test.go index 121bba33098a5..907dcf7bb09d7 100644 --- a/comp/process/bundle_test.go +++ b/comp/process/bundle_test.go @@ -19,6 +19,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/status" "github.com/DataDog/datadog-agent/comp/core/tagger" "github.com/DataDog/datadog-agent/comp/core/workloadmeta" + "github.com/DataDog/datadog-agent/comp/dogstatsd/statsd" "github.com/DataDog/datadog-agent/comp/forwarder/eventplatform/eventplatformimpl" "github.com/DataDog/datadog-agent/comp/forwarder/eventplatformreceiver/eventplatformreceiverimpl" "github.com/DataDog/datadog-agent/comp/networkpath/npcollector/npcollectorimpl" @@ -54,6 +55,7 @@ func TestBundleDependencies(t *testing.T) { ), fx.Provide(func() context.Context { return context.TODO() }), npcollectorimpl.MockModule(), + statsd.MockModule(), ) } @@ -94,6 +96,7 @@ func TestBundleOneShot(t *testing.T) { eventplatformimpl.Module(), fx.Supply(eventplatformimpl.NewDefaultParams()), npcollectorimpl.Module(), + statsd.MockModule(), Bundle(), ) require.NoError(t, err) diff --git a/pkg/process/checks/container.go b/pkg/process/checks/container.go index 9cbcf499814f2..65d86a3f4bf08 100644 --- a/pkg/process/checks/container.go +++ b/pkg/process/checks/container.go @@ -7,6 +7,7 @@ package checks import ( "context" + "fmt" "sync" "time" @@ -134,7 +135,8 @@ func (c *ContainerCheck) Run(nextGroupID func() int32, options *RunOptions) (Run } numContainers := float64(len(containers)) - statsd.Client.Gauge("datadog.process.containers.host_count", numContainers, []string{}, 1) //nolint:errcheck + agentNameTag := fmt.Sprintf("agent:%s", flavor.GetFlavor()) + statsd.Client.Gauge("datadog.process.containers.host_count", numContainers, []string{agentNameTag}, 1) //nolint:errcheck log.Debugf("collected %d containers in %s", int(numContainers), time.Since(startTime)) return StandardRunResult(messages), nil } diff --git a/pkg/process/checks/process.go b/pkg/process/checks/process.go index e57798df2db24..08f81cfd4fa68 100644 --- a/pkg/process/checks/process.go +++ b/pkg/process/checks/process.go @@ -8,6 +8,7 @@ package checks import ( "context" "errors" + "fmt" "regexp" "strings" "time" @@ -340,8 +341,9 @@ func (p *ProcessCheck) run(groupID int32, collectRealTime bool) (RunResult, erro p.realtimeLastRun = p.lastRun } - statsd.Client.Gauge("datadog.process.containers.host_count", float64(totalContainers), []string{}, 1) //nolint:errcheck - statsd.Client.Gauge("datadog.process.processes.host_count", float64(totalProcs), []string{}, 1) //nolint:errcheck + agentNameTag := fmt.Sprintf("agent:%s", flavor.GetFlavor()) + statsd.Client.Gauge("datadog.process.containers.host_count", float64(totalContainers), []string{agentNameTag}, 1) //nolint:errcheck + statsd.Client.Gauge("datadog.process.processes.host_count", float64(totalProcs), []string{agentNameTag}, 1) //nolint:errcheck log.Debugf("collected processes in %s", time.Since(start)) return result, nil diff --git a/releasenotes/notes/process-statsd-client-fix-66b11cc991a9f6be.yaml b/releasenotes/notes/process-statsd-client-fix-66b11cc991a9f6be.yaml new file mode 100644 index 0000000000000..922c3399d7898 --- /dev/null +++ b/releasenotes/notes/process-statsd-client-fix-66b11cc991a9f6be.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. +--- +fixes: + - | + Fix metric reporting for process checks on the core Agent. From 65cccf20d553082bef7dedcf2de36075104e1634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Juli=C3=A1n?= Date: Thu, 30 May 2024 17:56:21 +0200 Subject: [PATCH 39/54] [EBPF] Test correct packaging of eBPF files (#25727) --- omnibus/package-scripts/agent-rpm/prerm | 5 ++ pkg/ebpf/c/bpf_metadata.h | 19 +++++++ pkg/ebpf/c/kconfig.h | 2 + pkg/ebpf/c/ktypes.h | 2 + tasks/libs/common/utils.py | 1 + .../agent-platform/common/agent_behaviour.go | 55 ++++++++++++++++++- .../install-script/install_script_test.go | 7 ++- .../step-by-step/step_by_step_test.go | 7 ++- 8 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 pkg/ebpf/c/bpf_metadata.h diff --git a/omnibus/package-scripts/agent-rpm/prerm b/omnibus/package-scripts/agent-rpm/prerm index 520cb64c33e01..44a8c5398ca53 100755 --- a/omnibus/package-scripts/agent-rpm/prerm +++ b/omnibus/package-scripts/agent-rpm/prerm @@ -115,6 +115,11 @@ remove_sysprobe_secagent_files() if [ -d "$INSTALL_DIR/run" ]; then rmdir "$INSTALL_DIR/run" 2>/dev/null || true fi + + # Remove any unpacked BTF files + find "$INSTALL_DIR/embedded/share/system-probe/ebpf/co-re/btf" -name "*.btf*" -type f -delete || true + # And remove empty directories + find "$INSTALL_DIR/embedded/share/system-probe/ebpf/co-re" -type d -empty -delete || true } remove_remote_config_db() diff --git a/pkg/ebpf/c/bpf_metadata.h b/pkg/ebpf/c/bpf_metadata.h new file mode 100644 index 0000000000000..ad7162404feca --- /dev/null +++ b/pkg/ebpf/c/bpf_metadata.h @@ -0,0 +1,19 @@ +/** + * This header file is included in kconfig.h and ktypes.h, which should make + * this metadata available to all object files. + */ + +#ifndef __BPF_METADATA_H__ +#define __BPF_METADATA_H__ + +#ifndef __CGO__ // Do not include metadata in CGo builds, as it causes duplicate symbols +#if defined(__x86_64__) || defined(__TARGET_ARCH_x86) +char __dd_metadata_arch[] __attribute__((section("dd_metadata"), used)) = ""; +#elif defined(__aarch64__) || defined(__TARGET_ARCH_arm64) +char __dd_metadata_arch[] __attribute__((section("dd_metadata"), used)) = ""; +#else +char __dd_metadata_arch[] __attribute__((section("dd_metadata"), used)) = ""; +#endif +#endif + +#endif diff --git a/pkg/ebpf/c/kconfig.h b/pkg/ebpf/c/kconfig.h index c93f500eb7d09..a5a186d940453 100644 --- a/pkg/ebpf/c/kconfig.h +++ b/pkg/ebpf/c/kconfig.h @@ -1,6 +1,8 @@ #ifndef __KCONFIG_H #define __KCONFIG_H +#include "bpf_metadata.h" + #include // include asm/compiler.h to fix `error: expected string literal in 'asm'` compilation error coming from mte-kasan.h // this was fixed in https://github.com/torvalds/linux/commit/b859ebedd1e730bbda69142fca87af4e712649a1 diff --git a/pkg/ebpf/c/ktypes.h b/pkg/ebpf/c/ktypes.h index de243a94a5a9a..0daf39816bc36 100644 --- a/pkg/ebpf/c/ktypes.h +++ b/pkg/ebpf/c/ktypes.h @@ -1,6 +1,8 @@ #ifndef __KTYPES_H__ #define __KTYPES_H__ +#include "bpf_metadata.h" + #ifdef COMPILE_CORE #include "vmlinux.h" #else diff --git a/tasks/libs/common/utils.py b/tasks/libs/common/utils.py index 9a477f51aafe9..3ab39b06f57e8 100644 --- a/tasks/libs/common/utils.py +++ b/tasks/libs/common/utils.py @@ -261,6 +261,7 @@ def get_build_flags( env['CGO_LDFLAGS'] = os.environ.get('CGO_LDFLAGS', '') + ' -Wl,--allow-multiple-definition' extra_cgo_flags = " -Werror -Wno-deprecated-declarations" + extra_cgo_flags += " -D__CGO__" # Some C files include definitions that can cause duplicate symbols if included in CGo code (bpf_metadata.h) mainly if rtloader_headers: extra_cgo_flags += f" -I{rtloader_headers}" if rtloader_common_headers: diff --git a/test/new-e2e/tests/agent-platform/common/agent_behaviour.go b/test/new-e2e/tests/agent-platform/common/agent_behaviour.go index 4ea8eae34c3c9..dfb29e58a4707 100644 --- a/test/new-e2e/tests/agent-platform/common/agent_behaviour.go +++ b/test/new-e2e/tests/agent-platform/common/agent_behaviour.go @@ -15,10 +15,11 @@ import ( componentos "github.com/DataDog/test-infra-definitions/components/os" - agentclient "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client/agentclient" - boundport "github.com/DataDog/datadog-agent/test/new-e2e/tests/agent-platform/common/bound-port" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + agentclient "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client/agentclient" + boundport "github.com/DataDog/datadog-agent/test/new-e2e/tests/agent-platform/common/bound-port" ) // CheckAgentBehaviour runs test to check the agent is behaving as expected @@ -319,3 +320,53 @@ func CheckCWSBehaviour(t *testing.T, client *TestClient) { require.True(tt, result, "system-probe and security-agent should communicate") }) } + +// CheckSystemProbeBehavior runs tests to check the agent behave correctly when system-probe is enabled +func CheckSystemProbeBehavior(t *testing.T, client *TestClient) { + t.Run("enable system-probe and restarts", func(tt *testing.T) { + err := client.SetConfig(client.Helper.GetConfigFolder()+"system-probe.yaml", "system_probe_config.enabled", "true") + require.NoError(tt, err) + + err = client.SetConfig(client.Helper.GetConfigFolder()+"system-probe.yaml", "system_probe_config.enabled", "true") + require.NoError(tt, err) + + _, err = client.SvcManager.Restart(client.Helper.GetServiceName()) + require.NoError(tt, err, "datadog-agent should restart after CWS is enabled") + }) + + t.Run("system-probe is running", func(tt *testing.T) { + var err error + require.Eventually(tt, func() bool { + return AgentProcessIsRunning(client, "system-probe") + }, 1*time.Minute, 500*time.Millisecond, "system-probe should be running ", err) + }) + + t.Run("ebpf programs are unpacked and valid", func(tt *testing.T) { + ebpfPath := "/opt/datadog-agent/embedded/share/system-probe/ebpf" + output, err := client.Host.Execute(fmt.Sprintf("find %s -name '*.o'", ebpfPath)) + require.NoError(tt, err) + files := strings.Split(strings.TrimSpace(output), "\n") + require.Greater(tt, len(files), 0, "ebpf object files should be present") + + _, err = client.Host.Execute("command -v readelf") + if err != nil { + tt.Skip("readelf is not available on the host") + return + } + + hostArch, err := client.Host.Execute("uname -m") + require.NoError(tt, err) + hostArch = strings.TrimSpace(hostArch) + if hostArch == "aarch64" { + hostArch = "arm64" + } + archMetadata := fmt.Sprintf("", hostArch) + + for _, file := range files { + file = strings.TrimSpace(file) + ddMetadata, err := client.Host.Execute(fmt.Sprintf("readelf -p dd_metadata %s", file)) + require.NoError(tt, err, "readelf should not error, file is %s", file) + require.Contains(tt, ddMetadata, archMetadata, "invalid arch metadata") + } + }) +} diff --git a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go index 89fc4a9ba9b38..56ab93a12a7c6 100644 --- a/test/new-e2e/tests/agent-platform/install-script/install_script_test.go +++ b/test/new-e2e/tests/agent-platform/install-script/install_script_test.go @@ -136,8 +136,11 @@ func (is *installScriptSuite) AgentTest(flavor string) { common.CheckAgentPython(is.T(), client, common.ExpectedPythonVersion3) common.CheckApmEnabled(is.T(), client) common.CheckApmDisabled(is.T(), client) - if flavor == "datadog-agent" && is.cwsSupported { - common.CheckCWSBehaviour(is.T(), client) + if flavor == "datadog-agent" { + common.CheckSystemProbeBehavior(is.T(), client) + if is.cwsSupported { + common.CheckCWSBehaviour(is.T(), client) + } } common.CheckInstallationInstallScript(is.T(), client) is.testUninstall(client, flavor) diff --git a/test/new-e2e/tests/agent-platform/step-by-step/step_by_step_test.go b/test/new-e2e/tests/agent-platform/step-by-step/step_by_step_test.go index 9e8a690024ed6..6a7dd091427ec 100644 --- a/test/new-e2e/tests/agent-platform/step-by-step/step_by_step_test.go +++ b/test/new-e2e/tests/agent-platform/step-by-step/step_by_step_test.go @@ -160,8 +160,11 @@ func (is *stepByStepSuite) CheckStepByStepAgentInstallation(VMclient *common.Tes } common.CheckApmEnabled(is.T(), VMclient) common.CheckApmDisabled(is.T(), VMclient) - if *flavorName == "datadog-agent" && is.cwsSupported { - common.CheckCWSBehaviour(is.T(), VMclient) + if *flavorName == "datadog-agent" { + common.CheckSystemProbeBehavior(is.T(), VMclient) + if is.cwsSupported { + common.CheckCWSBehaviour(is.T(), VMclient) + } } is.T().Run("remove the agent", func(tt *testing.T) { From 65f5c024fe5dec1a860d4beec7d92f23cbfc0d93 Mon Sep 17 00:00:00 2001 From: maxime mouial Date: Thu, 30 May 2024 18:11:53 +0200 Subject: [PATCH 40/54] Bumping viper to v1.13.5 (#26123) --- comp/core/config/go.mod | 2 +- comp/core/config/go.sum | 4 ++-- comp/core/log/go.mod | 2 +- comp/core/log/go.sum | 4 ++-- comp/core/status/statusimpl/go.mod | 2 +- comp/core/status/statusimpl/go.sum | 4 ++-- comp/forwarder/defaultforwarder/go.mod | 2 +- comp/forwarder/defaultforwarder/go.sum | 4 ++-- comp/forwarder/orchestrator/orchestratorinterface/go.mod | 2 +- comp/forwarder/orchestrator/orchestratorinterface/go.sum | 4 ++-- comp/logs/agent/config/go.mod | 2 +- comp/logs/agent/config/go.sum | 4 ++-- comp/otelcol/logsagentpipeline/go.mod | 2 +- comp/otelcol/logsagentpipeline/go.sum | 4 ++-- comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod | 2 +- comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum | 4 ++-- comp/otelcol/otlp/components/exporter/datadogexporter/go.mod | 2 +- comp/otelcol/otlp/components/exporter/datadogexporter/go.sum | 4 ++-- .../otelcol/otlp/components/exporter/logsagentexporter/go.mod | 2 +- .../otelcol/otlp/components/exporter/logsagentexporter/go.sum | 4 ++-- .../otlp/components/exporter/serializerexporter/go.mod | 2 +- .../otlp/components/exporter/serializerexporter/go.sum | 4 ++-- comp/otelcol/otlp/testutil/go.mod | 2 +- comp/otelcol/otlp/testutil/go.sum | 4 ++-- comp/serializer/compression/go.mod | 2 +- comp/serializer/compression/go.sum | 4 ++-- go.mod | 2 +- go.sum | 4 ++-- pkg/api/go.mod | 2 +- pkg/api/go.sum | 4 ++-- pkg/config/env/go.mod | 2 +- pkg/config/env/go.sum | 4 ++-- pkg/config/logs/go.mod | 2 +- pkg/config/logs/go.sum | 4 ++-- pkg/config/model/go.mod | 2 +- pkg/config/model/go.sum | 4 ++-- pkg/config/remote/go.mod | 2 +- pkg/config/remote/go.sum | 4 ++-- pkg/config/setup/go.mod | 2 +- pkg/config/setup/go.sum | 4 ++-- pkg/config/utils/go.mod | 2 +- pkg/config/utils/go.sum | 4 ++-- pkg/logs/auditor/go.mod | 2 +- pkg/logs/auditor/go.sum | 4 ++-- pkg/logs/client/go.mod | 2 +- pkg/logs/client/go.sum | 4 ++-- pkg/logs/diagnostic/go.mod | 2 +- pkg/logs/diagnostic/go.sum | 4 ++-- pkg/logs/message/go.mod | 2 +- pkg/logs/message/go.sum | 4 ++-- pkg/logs/pipeline/go.mod | 2 +- pkg/logs/pipeline/go.sum | 4 ++-- pkg/logs/processor/go.mod | 2 +- pkg/logs/processor/go.sum | 4 ++-- pkg/logs/sds/go.mod | 2 +- pkg/logs/sds/go.sum | 4 ++-- pkg/logs/sender/go.mod | 2 +- pkg/logs/sender/go.sum | 4 ++-- pkg/logs/sources/go.mod | 2 +- pkg/logs/sources/go.sum | 4 ++-- pkg/logs/util/testutils/go.mod | 2 +- pkg/logs/util/testutils/go.sum | 4 ++-- pkg/metrics/go.mod | 2 +- pkg/metrics/go.sum | 4 ++-- pkg/serializer/go.mod | 2 +- pkg/serializer/go.sum | 4 ++-- pkg/util/flavor/go.mod | 2 +- pkg/util/flavor/go.sum | 4 ++-- pkg/util/http/go.mod | 2 +- pkg/util/http/go.sum | 4 ++-- 70 files changed, 105 insertions(+), 105 deletions(-) diff --git a/comp/core/config/go.mod b/comp/core/config/go.mod index 3fe6d96201b7a..45f927629d584 100644 --- a/comp/core/config/go.mod +++ b/comp/core/config/go.mod @@ -37,7 +37,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/optional v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 - github.com/DataDog/viper v1.13.4 + github.com/DataDog/viper v1.13.5 github.com/stretchr/testify v1.9.0 go.uber.org/fx v1.18.2 ) diff --git a/comp/core/config/go.sum b/comp/core/config/go.sum index 1144bfcc19ac1..6081ecc18017b 100644 --- a/comp/core/config/go.sum +++ b/comp/core/config/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/core/log/go.mod b/comp/core/log/go.mod index 44d573bac7e97..1f9e92e37ffb2 100644 --- a/comp/core/log/go.mod +++ b/comp/core/log/go.mod @@ -68,7 +68,7 @@ require ( github.com/DataDog/go-sqllexer v0.0.9 // indirect github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/comp/core/log/go.sum b/comp/core/log/go.sum index c33f2706ddf1b..f57da860c6479 100644 --- a/comp/core/log/go.sum +++ b/comp/core/log/go.sum @@ -9,8 +9,8 @@ github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4ti github.com/DataDog/go-tuf v1.1.0-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0 h1:10TPqpTlIkmDPFWVIEZ4ZX3rWrCrx3rEoeoAooZr6LM= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.14.0/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= diff --git a/comp/core/status/statusimpl/go.mod b/comp/core/status/statusimpl/go.mod index 80ce4c5cb1102..6b0e67ad49a60 100644 --- a/comp/core/status/statusimpl/go.mod +++ b/comp/core/status/statusimpl/go.mod @@ -62,7 +62,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/comp/core/status/statusimpl/go.sum b/comp/core/status/statusimpl/go.sum index 48d73f03e354a..37e735d6066e9 100644 --- a/comp/core/status/statusimpl/go.sum +++ b/comp/core/status/statusimpl/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/forwarder/defaultforwarder/go.mod b/comp/forwarder/defaultforwarder/go.mod index a695e974f0eba..38cd52436dcce 100644 --- a/comp/forwarder/defaultforwarder/go.mod +++ b/comp/forwarder/defaultforwarder/go.mod @@ -85,7 +85,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/comp/forwarder/defaultforwarder/go.sum b/comp/forwarder/defaultforwarder/go.sum index 85537136660db..c2359f6536dbf 100644 --- a/comp/forwarder/defaultforwarder/go.sum +++ b/comp/forwarder/defaultforwarder/go.sum @@ -1,7 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.mod b/comp/forwarder/orchestrator/orchestratorinterface/go.mod index 5235be4c0cfcd..bd5982f5aafef 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.mod +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.mod @@ -88,7 +88,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.sum b/comp/forwarder/orchestrator/orchestratorinterface/go.sum index 6b63d4285311f..e77bd87853c7d 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.sum +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.sum @@ -2,8 +2,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/cast v1.3.1-0.20190301154711-1ee8c8bd14a3 h1:SobA9WYm4K/MUtWlbKaomWTmnuYp1KhIm8Wlx3vmpsg= github.com/DataDog/cast v1.3.1-0.20190301154711-1ee8c8bd14a3/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/logs/agent/config/go.mod b/comp/logs/agent/config/go.mod index 654d2aad3801d..5e43dd57227f8 100644 --- a/comp/logs/agent/config/go.mod +++ b/comp/logs/agent/config/go.mod @@ -39,7 +39,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/fxutil v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/log v0.54.0-rc.2 github.com/DataDog/datadog-agent/pkg/util/pointer v0.54.0-rc.2 - github.com/DataDog/viper v1.13.4 + github.com/DataDog/viper v1.13.5 github.com/stretchr/testify v1.9.0 go.uber.org/fx v1.18.2 ) diff --git a/comp/logs/agent/config/go.sum b/comp/logs/agent/config/go.sum index 4f5285c7fa353..b159185f44664 100644 --- a/comp/logs/agent/config/go.sum +++ b/comp/logs/agent/config/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/otelcol/logsagentpipeline/go.mod b/comp/otelcol/logsagentpipeline/go.mod index 5b5790024260e..6c0ba0d794bfe 100644 --- a/comp/otelcol/logsagentpipeline/go.mod +++ b/comp/otelcol/logsagentpipeline/go.mod @@ -97,7 +97,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/comp/otelcol/logsagentpipeline/go.sum b/comp/otelcol/logsagentpipeline/go.sum index c368b404899ba..9dc78dae99954 100644 --- a/comp/otelcol/logsagentpipeline/go.sum +++ b/comp/otelcol/logsagentpipeline/go.sum @@ -43,8 +43,8 @@ github.com/DataDog/agent-payload/v5 v5.0.106 h1:A3dGX+JYoL7OJe2crpxznW7hWxLxhOk/ github.com/DataDog/agent-payload/v5 v5.0.106/go.mod h1:COngtbYYCncpIPiE5D93QlXDH/3VAKk10jDNwGHcMRE= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe h1:efzxujZ7VHWFxjmWjcJyUEpPrN8qdiZPYb+dBw547Wo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod index 9664a5be3f4b8..79e9cb985232b 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod @@ -108,7 +108,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum index 3f8c9d86d36de..8decf55752b31 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum @@ -43,8 +43,8 @@ github.com/DataDog/agent-payload/v5 v5.0.106 h1:A3dGX+JYoL7OJe2crpxznW7hWxLxhOk/ github.com/DataDog/agent-payload/v5 v5.0.106/go.mod h1:COngtbYYCncpIPiE5D93QlXDH/3VAKk10jDNwGHcMRE= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe h1:efzxujZ7VHWFxjmWjcJyUEpPrN8qdiZPYb+dBw547Wo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod index 0619a0aba20ab..b5466332a7132 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod @@ -165,7 +165,7 @@ require ( github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.16.0 // indirect github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 // indirect github.com/DataDog/sketches-go v1.4.4 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f // indirect github.com/Microsoft/go-winio v0.6.1 // indirect diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum index 31449ad831d59..47d43cbdaf7e5 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum @@ -20,8 +20,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 h1:BdfRSuCoHyKa github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0/go.mod h1:6eYyd+lJYH+uRuZqhyW/u+9ykaXBWetDGj44+txz6jU= github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod index 119f1230fb91f..83939e378bbbc 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod @@ -71,7 +71,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/datadog-api-client-go/v2 v2.13.0 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/DataDog/zstd v1.5.2 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum index e02b4cea1d411..f2273f425b08b 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum @@ -8,8 +8,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1 h1:ZI8u3 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.16.1/go.mod h1:dvIWN9pA2zWNTw5rhDWZgzZnhcfpH++d+8d1SWW6xkY= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.14.0 h1:nma5ZICTbHZ0YoMu18ziWGSLK1ICzMm6rJTv+IatJ0U= github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/logs v0.14.0/go.mod h1:xUiGj13q5uHPboc0xZ754fyusiF5C2RxNzOFdTbdZFA= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8= github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod index 455a9545dcfe2..6d39cc94c30dc 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod @@ -123,7 +123,7 @@ require ( github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 // indirect github.com/DataDog/sketches-go v1.4.4 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f // indirect github.com/Microsoft/go-winio v0.6.1 // indirect diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum index 2c5a7da1b8a34..eb26676ac20a2 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum @@ -14,8 +14,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0 h1:QHx6B/VUx3rZ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0/go.mod h1:q4c7zbmdnIdSJNZuBsveTk5ZeRkSkS2g6b8zzFF1mE4= github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= diff --git a/comp/otelcol/otlp/testutil/go.mod b/comp/otelcol/otlp/testutil/go.mod index 9663cfb13a9b5..9fd6dc1454a1a 100644 --- a/comp/otelcol/otlp/testutil/go.mod +++ b/comp/otelcol/otlp/testutil/go.mod @@ -47,7 +47,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/comp/otelcol/otlp/testutil/go.sum b/comp/otelcol/otlp/testutil/go.sum index f9659fa496721..8b840551fac81 100644 --- a/comp/otelcol/otlp/testutil/go.sum +++ b/comp/otelcol/otlp/testutil/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/comp/serializer/compression/go.mod b/comp/serializer/compression/go.mod index cf57ae6820039..8dc326ae31660 100644 --- a/comp/serializer/compression/go.mod +++ b/comp/serializer/compression/go.mod @@ -53,7 +53,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/comp/serializer/compression/go.sum b/comp/serializer/compression/go.sum index 9615820c08c5e..7af49998bfb57 100644 --- a/comp/serializer/compression/go.sum +++ b/comp/serializer/compression/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= diff --git a/go.mod b/go.mod index 58efefd489d0e..408e217201641 100644 --- a/go.mod +++ b/go.mod @@ -145,7 +145,7 @@ require ( github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.16.0 github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.16.0 github.com/DataDog/sketches-go v1.4.4 - github.com/DataDog/viper v1.13.4 + github.com/DataDog/viper v1.13.5 github.com/DataDog/watermarkpodautoscaler v0.6.1 github.com/DataDog/zstd v1.5.5 github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f // indirect diff --git a/go.sum b/go.sum index 050879c8ce552..a338104fc01bd 100644 --- a/go.sum +++ b/go.sum @@ -799,8 +799,8 @@ github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1 github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= github.com/DataDog/trivy v0.0.0-20240524155722-0c355f8e174c h1:Xsdjt6y5ajENVAasDmdl0d6jbFkSYwhi5HD9CXke9Rc= github.com/DataDog/trivy v0.0.0-20240524155722-0c355f8e174c/go.mod h1:xmc7xCb5KSg2mFbztyInH8ciotVbad9SOmGFClgD0cU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/walker v0.0.0-20230418153152-7f29bb2dc950 h1:2imDajw3V85w1iqHsuXN+hUBZQVF+r9eME8tsPq/HpA= github.com/DataDog/walker v0.0.0-20230418153152-7f29bb2dc950/go.mod h1:FU+7qU8DeQQgSZDmmThMJi93kPkLFgy0oVAcLxurjIk= github.com/DataDog/watermarkpodautoscaler v0.6.1 h1:KEj10Cm8wO/36lEOgqjgDfIMMpMPReY/+bDacWe7Adw= diff --git a/pkg/api/go.mod b/pkg/api/go.mod index 12b6088a8e173..7dae1c5aa1251 100644 --- a/pkg/api/go.mod +++ b/pkg/api/go.mod @@ -57,7 +57,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/api/go.sum b/pkg/api/go.sum index 4f5285c7fa353..b159185f44664 100644 --- a/pkg/api/go.sum +++ b/pkg/api/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/config/env/go.mod b/pkg/config/env/go.mod index 6404b987b6d3d..a4e01466aa1f8 100644 --- a/pkg/config/env/go.mod +++ b/pkg/config/env/go.mod @@ -20,7 +20,7 @@ require ( require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/config/env/go.sum b/pkg/config/env/go.sum index b8cdbec3a8655..f3f8a4c5745c9 100644 --- a/pkg/config/env/go.sum +++ b/pkg/config/env/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/config/logs/go.mod b/pkg/config/logs/go.mod index 9a599a76e19cd..36b748233c48c 100644 --- a/pkg/config/logs/go.mod +++ b/pkg/config/logs/go.mod @@ -17,7 +17,7 @@ require ( ) require ( - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/google/go-cmp v0.6.0 // indirect diff --git a/pkg/config/logs/go.sum b/pkg/config/logs/go.sum index ffa38e4c29cd9..584c372a509b5 100644 --- a/pkg/config/logs/go.sum +++ b/pkg/config/logs/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/pkg/config/model/go.mod b/pkg/config/model/go.mod index 726cea6783915..47845fbec2e8b 100644 --- a/pkg/config/model/go.mod +++ b/pkg/config/model/go.mod @@ -10,7 +10,7 @@ replace ( require ( github.com/DataDog/datadog-agent/pkg/util/log v0.54.0-rc.2 - github.com/DataDog/viper v1.13.4 + github.com/DataDog/viper v1.13.5 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 github.com/spf13/afero v1.1.2 github.com/spf13/pflag v1.0.3 diff --git a/pkg/config/model/go.sum b/pkg/config/model/go.sum index 9b3ed3de9f0a9..c9284d1b2c6c0 100644 --- a/pkg/config/model/go.sum +++ b/pkg/config/model/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/pkg/config/remote/go.mod b/pkg/config/remote/go.mod index 02a1d0a68066c..50c71a0af2f4c 100644 --- a/pkg/config/remote/go.mod +++ b/pkg/config/remote/go.mod @@ -58,7 +58,7 @@ require ( require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 // indirect github.com/DataDog/go-tuf v1.1.0-0.5.2 - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect diff --git a/pkg/config/remote/go.sum b/pkg/config/remote/go.sum index 478ea46481e13..fe4cda0b6db9a 100644 --- a/pkg/config/remote/go.sum +++ b/pkg/config/remote/go.sum @@ -46,8 +46,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= github.com/DataDog/go-tuf v1.1.0-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/config/setup/go.mod b/pkg/config/setup/go.mod index 53192d3d543b2..5f7c72b2ffadc 100644 --- a/pkg/config/setup/go.mod +++ b/pkg/config/setup/go.mod @@ -55,7 +55,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/pointer v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/config/setup/go.sum b/pkg/config/setup/go.sum index ce57d84e6feab..ca38c4245d1f0 100644 --- a/pkg/config/setup/go.sum +++ b/pkg/config/setup/go.sum @@ -3,8 +3,8 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/cast v1.3.1-0.20190301154711-1ee8c8bd14a3 h1:SobA9WYm4K/MUtWlbKaomWTmnuYp1KhIm8Wlx3vmpsg= github.com/DataDog/cast v1.3.1-0.20190301154711-1ee8c8bd14a3/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/config/utils/go.mod b/pkg/config/utils/go.mod index 8c5709d8f82d4..a39cbce46793e 100644 --- a/pkg/config/utils/go.mod +++ b/pkg/config/utils/go.mod @@ -49,7 +49,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/config/utils/go.sum b/pkg/config/utils/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/config/utils/go.sum +++ b/pkg/config/utils/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/auditor/go.mod b/pkg/logs/auditor/go.mod index 9d0b228c223a2..1c87cb9ab046a 100644 --- a/pkg/logs/auditor/go.mod +++ b/pkg/logs/auditor/go.mod @@ -64,7 +64,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/logs/auditor/go.sum b/pkg/logs/auditor/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/logs/auditor/go.sum +++ b/pkg/logs/auditor/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/client/go.mod b/pkg/logs/client/go.mod index c319836638833..d022834d48c23 100644 --- a/pkg/logs/client/go.mod +++ b/pkg/logs/client/go.mod @@ -79,7 +79,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/logs/client/go.sum b/pkg/logs/client/go.sum index 45d4b9308d579..8d8b896046108 100644 --- a/pkg/logs/client/go.sum +++ b/pkg/logs/client/go.sum @@ -39,8 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/diagnostic/go.mod b/pkg/logs/diagnostic/go.mod index 4c25bf55335c3..2f75d08579d75 100644 --- a/pkg/logs/diagnostic/go.mod +++ b/pkg/logs/diagnostic/go.mod @@ -68,7 +68,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/logs/diagnostic/go.sum b/pkg/logs/diagnostic/go.sum index 4f5285c7fa353..b159185f44664 100644 --- a/pkg/logs/diagnostic/go.sum +++ b/pkg/logs/diagnostic/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/message/go.mod b/pkg/logs/message/go.mod index a310a526504d5..77b62428d065c 100644 --- a/pkg/logs/message/go.mod +++ b/pkg/logs/message/go.mod @@ -60,7 +60,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/logs/message/go.sum b/pkg/logs/message/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/logs/message/go.sum +++ b/pkg/logs/message/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/pipeline/go.mod b/pkg/logs/pipeline/go.mod index be8526f758453..67fa037fe935c 100644 --- a/pkg/logs/pipeline/go.mod +++ b/pkg/logs/pipeline/go.mod @@ -98,7 +98,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect diff --git a/pkg/logs/pipeline/go.sum b/pkg/logs/pipeline/go.sum index 1624aff2bdd5e..01b356fe5381b 100644 --- a/pkg/logs/pipeline/go.sum +++ b/pkg/logs/pipeline/go.sum @@ -43,8 +43,8 @@ github.com/DataDog/agent-payload/v5 v5.0.106 h1:A3dGX+JYoL7OJe2crpxznW7hWxLxhOk/ github.com/DataDog/agent-payload/v5 v5.0.106/go.mod h1:COngtbYYCncpIPiE5D93QlXDH/3VAKk10jDNwGHcMRE= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe h1:efzxujZ7VHWFxjmWjcJyUEpPrN8qdiZPYb+dBw547Wo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/processor/go.mod b/pkg/logs/processor/go.mod index ad8043ebcb03c..5588f3c93a3f9 100644 --- a/pkg/logs/processor/go.mod +++ b/pkg/logs/processor/go.mod @@ -78,7 +78,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/logs/processor/go.sum b/pkg/logs/processor/go.sum index b3b61b6f10a75..0df28af1d9c5b 100644 --- a/pkg/logs/processor/go.sum +++ b/pkg/logs/processor/go.sum @@ -43,8 +43,8 @@ github.com/DataDog/agent-payload/v5 v5.0.106 h1:A3dGX+JYoL7OJe2crpxznW7hWxLxhOk/ github.com/DataDog/agent-payload/v5 v5.0.106/go.mod h1:COngtbYYCncpIPiE5D93QlXDH/3VAKk10jDNwGHcMRE= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe h1:efzxujZ7VHWFxjmWjcJyUEpPrN8qdiZPYb+dBw547Wo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/sds/go.mod b/pkg/logs/sds/go.mod index d8e3a1475f7a1..49a94bf7dab63 100644 --- a/pkg/logs/sds/go.mod +++ b/pkg/logs/sds/go.mod @@ -75,7 +75,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/logs/sds/go.sum b/pkg/logs/sds/go.sum index d1c188fe5103b..c8e007d714a63 100644 --- a/pkg/logs/sds/go.sum +++ b/pkg/logs/sds/go.sum @@ -41,8 +41,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe h1:efzxujZ7VHWFxjmWjcJyUEpPrN8qdiZPYb+dBw547Wo= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240419161837-f1b2f553edfe/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/sender/go.mod b/pkg/logs/sender/go.mod index 1c867148dcd54..12151c1fcc1a1 100644 --- a/pkg/logs/sender/go.mod +++ b/pkg/logs/sender/go.mod @@ -80,7 +80,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect diff --git a/pkg/logs/sender/go.sum b/pkg/logs/sender/go.sum index 94a8ba1fc2228..cdd5aa8ae77f8 100644 --- a/pkg/logs/sender/go.sum +++ b/pkg/logs/sender/go.sum @@ -39,8 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/sources/go.mod b/pkg/logs/sources/go.mod index 59cd81bfa84fe..4fbdef6203254 100644 --- a/pkg/logs/sources/go.mod +++ b/pkg/logs/sources/go.mod @@ -58,7 +58,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/logs/sources/go.sum b/pkg/logs/sources/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/logs/sources/go.sum +++ b/pkg/logs/sources/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/logs/util/testutils/go.mod b/pkg/logs/util/testutils/go.mod index d2fde54e51cb4..8cb33a3f9de1c 100644 --- a/pkg/logs/util/testutils/go.mod +++ b/pkg/logs/util/testutils/go.mod @@ -59,7 +59,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/version v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect diff --git a/pkg/logs/util/testutils/go.sum b/pkg/logs/util/testutils/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/logs/util/testutils/go.sum +++ b/pkg/logs/util/testutils/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/metrics/go.mod b/pkg/metrics/go.mod index 2778615564f39..d87ae6fa6a096 100644 --- a/pkg/metrics/go.mod +++ b/pkg/metrics/go.mod @@ -38,7 +38,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/sort v0.54.0-rc.2 // indirect github.com/DataDog/sketches-go v1.4.4 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect diff --git a/pkg/metrics/go.sum b/pkg/metrics/go.sum index 8fbb026336fd6..8f6e4ea435114 100644 --- a/pkg/metrics/go.sum +++ b/pkg/metrics/go.sum @@ -45,8 +45,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0 h1:QHx6B/VUx3rZ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0/go.mod h1:q4c7zbmdnIdSJNZuBsveTk5ZeRkSkS2g6b8zzFF1mE4= github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= diff --git a/pkg/serializer/go.mod b/pkg/serializer/go.mod index 19c08bd9758e2..91d6eece826f1 100644 --- a/pkg/serializer/go.mod +++ b/pkg/serializer/go.mod @@ -108,7 +108,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 // indirect github.com/DataDog/sketches-go v1.4.4 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f // indirect github.com/Microsoft/go-winio v0.6.1 // indirect diff --git a/pkg/serializer/go.sum b/pkg/serializer/go.sum index 7cee52c719d60..2241b2dba3674 100644 --- a/pkg/serializer/go.sum +++ b/pkg/serializer/go.sum @@ -10,8 +10,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0 h1:QHx6B/VUx3rZ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.14.0/go.mod h1:q4c7zbmdnIdSJNZuBsveTk5ZeRkSkS2g6b8zzFF1mE4= github.com/DataDog/sketches-go v1.4.4 h1:dF52vzXRFSPOj2IjXSWLvXq3jubL4CI69kwYjJ1w5Z8= github.com/DataDog/sketches-go v1.4.4/go.mod h1:XR0ns2RtEEF09mDKXiKZiQg+nfZStrq1ZuL1eezeZe0= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= diff --git a/pkg/util/flavor/go.mod b/pkg/util/flavor/go.mod index ee9146482184d..162df0c896fe4 100644 --- a/pkg/util/flavor/go.mod +++ b/pkg/util/flavor/go.mod @@ -47,7 +47,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/system/socket v0.54.0-rc.2 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/pkg/util/flavor/go.sum b/pkg/util/flavor/go.sum index c1503b41b0ee8..6530b0681514a 100644 --- a/pkg/util/flavor/go.sum +++ b/pkg/util/flavor/go.sum @@ -1,8 +1,8 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= diff --git a/pkg/util/http/go.mod b/pkg/util/http/go.mod index ced231fc62900..df532ce677fe4 100644 --- a/pkg/util/http/go.mod +++ b/pkg/util/http/go.mod @@ -18,7 +18,7 @@ require ( require ( github.com/DataDog/datadog-agent/pkg/util/scrubber v0.54.0-rc.2 // indirect - github.com/DataDog/viper v1.13.4 // indirect + github.com/DataDog/viper v1.13.5 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.4.7 // indirect diff --git a/pkg/util/http/go.sum b/pkg/util/http/go.sum index babbe9d8f6a1e..81f490d4747c6 100644 --- a/pkg/util/http/go.sum +++ b/pkg/util/http/go.sum @@ -39,8 +39,8 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7 github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/viper v1.13.4 h1:0SrZc3zvMAGgVKg96uP4DEJb13lK2Is9a4go7IIcFSE= -github.com/DataDog/viper v1.13.4/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= +github.com/DataDog/viper v1.13.5 h1:SZMcyMknYQN2jRY/40A16gUXexlNJOI8sDs1cWZnI64= +github.com/DataDog/viper v1.13.5/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= From 604101425d1329d83c4fe280236a86101e99c049 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Thu, 30 May 2024 18:25:35 +0200 Subject: [PATCH 41/54] [ASCII-1805] Remove useless flaky test (#26124) --- .../configsyncimpl/sync_integration_test.go | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/comp/core/configsync/configsyncimpl/sync_integration_test.go b/comp/core/configsync/configsyncimpl/sync_integration_test.go index 3c74d20a55d9e..808c3a14bcade 100644 --- a/comp/core/configsync/configsyncimpl/sync_integration_test.go +++ b/comp/core/configsync/configsyncimpl/sync_integration_test.go @@ -21,24 +21,6 @@ import ( ) func TestRunWithChan(t *testing.T) { - t.Run("server error", func(t *testing.T) { - var called bool - handler := func(w http.ResponseWriter, r *http.Request) { - called = true - w.WriteHeader(http.StatusInternalServerError) - } - - ctx, cancel := context.WithCancel(context.Background()) - cs := makeConfigSyncWithServer(t, ctx, handler) - - ch := make(chan time.Time, 1) - ch <- time.Now() - time.AfterFunc(100*time.Millisecond, cancel) - cs.runWithChan(ch) - - require.True(t, called) - }) - t.Run("success", func(t *testing.T) { var called bool handler := func(w http.ResponseWriter, r *http.Request) { From 7afbdeeabd8738fa469116dffb76f87d242c9e34 Mon Sep 17 00:00:00 2001 From: Wassim Dhif Date: Thu, 30 May 2024 18:26:38 +0200 Subject: [PATCH 42/54] feat(opentelemetry): implement unified service tagging (#26118) Signed-off-by: Wassim DHIF Co-authored-by: May Lee --- .../collectors/workloadmeta_extract.go | 34 ++++++ .../collectors/workloadmeta_test.go | 102 ++++++++++++++++++ pkg/util/containers/env_vars_filter.go | 25 ++--- ...ource-attributes-ust-e379136c3a5c8046.yaml | 12 +++ 4 files changed, 161 insertions(+), 12 deletions(-) create mode 100644 releasenotes/notes/opentelemetry-resource-attributes-ust-e379136c3a5c8046.yaml diff --git a/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go b/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go index 09979ee6014a5..45570f2599e0f 100644 --- a/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go +++ b/comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go @@ -49,6 +49,12 @@ const ( envVarVersion = "DD_VERSION" envVarService = "DD_SERVICE" + // OpenTelemetry SDK - Environment variables + // https://opentelemetry.io/docs/languages/sdk-configuration/general + // https://opentelemetry.io/docs/specs/semconv/resource/ + envVarOtelService = "OTEL_SERVICE_NAME" + envVarOtelResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES" + // Docker label keys dockerLabelEnv = "com.datadoghq.tags.env" dockerLabelVersion = "com.datadoghq.tags.version" @@ -66,6 +72,16 @@ var ( envVarService: tagKeyService, } + otelStandardEnvKeys = map[string]string{ + envVarOtelService: tagKeyService, + } + + otelResourceAttributesMapping = map[string]string{ + "service.name": tagKeyService, + "service.version": tagKeyVersion, + "deployment.environment": tagKeyEnv, + } + lowCardOrchestratorEnvKeys = map[string]string{ "MARATHON_APP_ID": "marathon_app", @@ -212,6 +228,9 @@ func (c *WorkloadMetaCollector) handleContainer(ev workloadmeta.Event) []*types. // standard tags from environment c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard) + // standard tags in OpenTelemetry SDK format from environment + c.addOpenTelemetryStandardTags(container, tags) + // orchestrator tags from environment c.extractFromMapWithFn(container.EnvVars, lowCardOrchestratorEnvKeys, tags.AddLow) c.extractFromMapWithFn(container.EnvVars, orchCardOrchestratorEnvKeys, tags.AddOrchestrator) @@ -650,6 +669,9 @@ func (c *WorkloadMetaCollector) extractTagsFromPodContainer(pod *workloadmeta.Ku // enrich with standard tags from environment variables c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard) + // standard tags in OpenTelemetry SDK format from environment + c.addOpenTelemetryStandardTags(container, tags) + // container-specific tags provided through pod annotation annotation := fmt.Sprintf(podContainerTagsAnnotationFormat, containerName) c.extractTagsFromJSONInMap(annotation, pod.Annotations, tags) @@ -743,6 +765,18 @@ func (c *WorkloadMetaCollector) extractTagsFromJSONInMap(key string, input map[s } } +func (c *WorkloadMetaCollector) addOpenTelemetryStandardTags(container *workloadmeta.Container, tags *taglist.TagList) { + if otelResourceAttributes, ok := container.EnvVars[envVarOtelResourceAttributes]; ok { + for _, pair := range strings.Split(otelResourceAttributes, ",") { + fields := strings.SplitN(pair, "=", 2) + if tag, ok := otelResourceAttributesMapping[fields[0]]; ok { + tags.AddStandard(tag, fields[1]) + } + } + } + c.extractFromMapWithFn(container.EnvVars, otelStandardEnvKeys, tags.AddStandard) +} + func buildTaggerEntityID(entityID workloadmeta.EntityID) string { switch entityID.Kind { case workloadmeta.KindContainer: diff --git a/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go b/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go index cf05ffc883b9e..5258cb03445c8 100644 --- a/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go +++ b/comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go @@ -28,6 +28,7 @@ import ( func TestHandleKubePod(t *testing.T) { const ( fullyFleshedContainerID = "foobarquux" + otelEnvContainerID = "otelcontainer" noEnvContainerID = "foobarbaz" containerName = "agent" runtimeContainerName = "k8s_datadog-agent_agent" @@ -84,6 +85,20 @@ func TestHandleKubePod(t *testing.T) { "DD_VERSION": version, }, }) + store.Set(&workloadmeta.Container{ + EntityID: workloadmeta.EntityID{ + Kind: workloadmeta.KindContainer, + ID: otelEnvContainerID, + }, + EntityMeta: workloadmeta.EntityMeta{ + Name: runtimeContainerName, + }, + Image: image, + EnvVars: map[string]string{ + "OTEL_SERVICE_NAME": svc, + "OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env), + }, + }) store.Set(&workloadmeta.Container{ EntityID: workloadmeta.EntityID{ Kind: workloadmeta.KindContainer, @@ -279,6 +294,57 @@ func TestHandleKubePod(t *testing.T) { }, }, }, + { + name: "pod with fully formed container, standard tags from env with opentelemetry sdk", + pod: workloadmeta.KubernetesPod{ + EntityID: podEntityID, + EntityMeta: workloadmeta.EntityMeta{ + Name: podName, + Namespace: podNamespace, + }, + Containers: []workloadmeta.OrchestratorContainer{ + { + ID: otelEnvContainerID, + Name: containerName, + Image: image, + }, + }, + }, + expected: []*types.TagInfo{ + { + Source: podSource, + Entity: podTaggerEntityID, + HighCardTags: []string{}, + OrchestratorCardTags: []string{ + fmt.Sprintf("pod_name:%s", podName), + }, + LowCardTags: []string{ + fmt.Sprintf("kube_namespace:%s", podNamespace), + }, + StandardTags: []string{}, + }, + { + Source: podSource, + Entity: fmt.Sprintf("container_id://%s", otelEnvContainerID), + HighCardTags: []string{ + fmt.Sprintf("container_id:%s", otelEnvContainerID), + fmt.Sprintf("display_container_name:%s_%s", runtimeContainerName, podName), + }, + OrchestratorCardTags: []string{ + fmt.Sprintf("pod_name:%s", podName), + }, + LowCardTags: append([]string{ + fmt.Sprintf("kube_namespace:%s", podNamespace), + fmt.Sprintf("kube_container_name:%s", containerName), + "image_id:datadog/agent@sha256:a63d3f66fb2f69d955d4f2ca0b229385537a77872ffc04290acae65aed5317d2", + "image_name:datadog/agent", + "image_tag:latest", + "short_image:agent", + }, standardTags...), + StandardTags: standardTags, + }, + }, + }, { name: "pod with container, standard tags from labels", pod: workloadmeta.KubernetesPod{ @@ -990,6 +1056,42 @@ func TestHandleContainer(t *testing.T) { }, }, }, + { + name: "tags from environment with opentelemetry sdk", + container: workloadmeta.Container{ + EntityID: entityID, + EntityMeta: workloadmeta.EntityMeta{ + Name: containerName, + }, + EnvVars: map[string]string{ + // env as tags + "TEAM": "container-integrations", + "TIER": "node", + + // otel standard tags + "OTEL_SERVICE_NAME": svc, + "OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env), + }, + }, + envAsTags: map[string]string{ + "team": "owner_team", + }, + expected: []*types.TagInfo{ + { + Source: containerSource, + Entity: taggerEntityID, + HighCardTags: []string{ + fmt.Sprintf("container_name:%s", containerName), + fmt.Sprintf("container_id:%s", entityID.ID), + }, + OrchestratorCardTags: []string{}, + LowCardTags: append([]string{ + "owner_team:container-integrations", + }, standardTags...), + StandardTags: standardTags, + }, + }, + }, { name: "tags from labels", container: workloadmeta.Container{ diff --git a/pkg/util/containers/env_vars_filter.go b/pkg/util/containers/env_vars_filter.go index bad4a1ac2f1c2..3fb4bdaa1d466 100644 --- a/pkg/util/containers/env_vars_filter.go +++ b/pkg/util/containers/env_vars_filter.go @@ -14,22 +14,23 @@ import ( var ( defaultEnvVarsIncludeList = []string{ - "DD_ENV", - "DD_VERSION", - "DD_SERVICE", "CHRONOS_JOB_NAME", "CHRONOS_JOB_OWNER", - "NOMAD_TASK_NAME", - "NOMAD_JOB_NAME", - "NOMAD_GROUP_NAME", - "NOMAD_NAMESPACE", - "NOMAD_DC", - "MESOS_TASK_ID", + "DD_ENV", + "DD_SERVICE", + "DD_VERSION", + "DOCKER_DD_AGENT", // included to be able to detect agent containers "ECS_CONTAINER_METADATA_URI", "ECS_CONTAINER_METADATA_URI_V4", - "DOCKER_DD_AGENT", // included to be able to detect agent containers - // Included to ease unit tests without requiring a mock - "TEST_ENV", + "MESOS_TASK_ID", + "NOMAD_DC", + "NOMAD_GROUP_NAME", + "NOMAD_JOB_NAME", + "NOMAD_NAMESPACE", + "NOMAD_TASK_NAME", + "OTEL_RESOURCE_ATTRIBUTES", + "OTEL_SERVICE_NAME", + "TEST_ENV", // Included to ease unit tests without requiring a mock } envFilterOnce sync.Once diff --git a/releasenotes/notes/opentelemetry-resource-attributes-ust-e379136c3a5c8046.yaml b/releasenotes/notes/opentelemetry-resource-attributes-ust-e379136c3a5c8046.yaml new file mode 100644 index 0000000000000..8b649dcce38ce --- /dev/null +++ b/releasenotes/notes/opentelemetry-resource-attributes-ust-e379136c3a5c8046.yaml @@ -0,0 +1,12 @@ +# 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. +--- +features: + - | + Implement OpenTelemetry SDK resource attributes as unified service rags. + From 738973707f17c54260aff1eaf2e0fdecfcabd067 Mon Sep 17 00:00:00 2001 From: Dustin Long Date: Thu, 30 May 2024 12:31:40 -0400 Subject: [PATCH 43/54] go-deps-lint rule should ignore internal packages, since go version changes can affect them (#26136) --- cmd/serverless/dependencies_linux_amd64.txt | 26 --------------------- cmd/serverless/dependencies_linux_arm64.txt | 26 --------------------- tasks/go_deps.py | 10 ++++---- 3 files changed, 5 insertions(+), 57 deletions(-) diff --git a/cmd/serverless/dependencies_linux_amd64.txt b/cmd/serverless/dependencies_linux_amd64.txt index 30a8125e3371c..b5209492e0011 100644 --- a/cmd/serverless/dependencies_linux_amd64.txt +++ b/cmd/serverless/dependencies_linux_amd64.txt @@ -917,32 +917,6 @@ hash/crc32 hash/fnv html html/template -internal/abi -internal/bisect -internal/bytealg -internal/coverage/rtcov -internal/cpu -internal/fmtsort -internal/goarch -internal/godebug -internal/godebugs -internal/goexperiment -internal/goos -internal/intern -internal/itoa -internal/nettrace -internal/oserror -internal/poll -internal/race -internal/reflectlite -internal/safefilepath -internal/saferio -internal/singleflight -internal/syscall/execenv -internal/syscall/unix -internal/sysinfo -internal/testlog -internal/unsafeheader io io/fs io/ioutil diff --git a/cmd/serverless/dependencies_linux_arm64.txt b/cmd/serverless/dependencies_linux_arm64.txt index 65442897051cb..49379557da056 100644 --- a/cmd/serverless/dependencies_linux_arm64.txt +++ b/cmd/serverless/dependencies_linux_arm64.txt @@ -916,32 +916,6 @@ hash/crc32 hash/fnv html html/template -internal/abi -internal/bisect -internal/bytealg -internal/coverage/rtcov -internal/cpu -internal/fmtsort -internal/goarch -internal/godebug -internal/godebugs -internal/goexperiment -internal/goos -internal/intern -internal/itoa -internal/nettrace -internal/oserror -internal/poll -internal/race -internal/reflectlite -internal/safefilepath -internal/saferio -internal/singleflight -internal/syscall/execenv -internal/syscall/unix -internal/sysinfo -internal/testlog -internal/unsafeheader io io/fs io/ioutil diff --git a/tasks/go_deps.py b/tasks/go_deps.py index 70acadbd8c93a..ac83346fa8cb8 100644 --- a/tasks/go_deps.py +++ b/tasks/go_deps.py @@ -116,7 +116,7 @@ def compute_binary_dependencies_list( ) assert res - return res.stdout.strip() + return [dep for dep in res.stdout.strip().splitlines() if not dep.startswith("internal/")] @task @@ -185,14 +185,14 @@ def test_list( continue deps_file = open(filename) - deps = deps_file.read() + deps = deps_file.read().strip().splitlines() deps_file.close() list = compute_binary_dependencies_list(ctx, build, flavor, platform, arch) if list != deps: - new_dependencies_lines = len(list.splitlines()) - recorded_dependencies_lines = len(deps.splitlines()) + new_dependencies_lines = len(list) + recorded_dependencies_lines = len(deps) mismatch_binaries.add( MisMacthBinary(binary, goos, goarch, new_dependencies_lines - recorded_dependencies_lines) @@ -252,5 +252,5 @@ def generate( list = compute_binary_dependencies_list(ctx, build, flavor, platform, arch) f = open(filename, "w") - f.write(list) + f.write('\n'.join(list)) f.close() From 73f881a37b574b7b72c1233bc4b7cddb7d317efa Mon Sep 17 00:00:00 2001 From: Adel Haj Hassan <41540817+adel121@users.noreply.github.com> Date: Thu, 30 May 2024 18:36:45 +0200 Subject: [PATCH 44/54] [CONTP-219] Populating missing container id field in remote process collector (#26116) --- .../collectors/internal/remote/generic.go | 4 +- .../processcollector/process_collector.go | 99 ++++++++++++------- .../remote/workloadmeta/workloadmeta.go | 2 +- .../remote/workloadmeta/workloadmeta_test.go | 10 +- .../metrics/docker/collector_test.go | 2 - 5 files changed, 77 insertions(+), 40 deletions(-) diff --git a/comp/core/workloadmeta/collectors/internal/remote/generic.go b/comp/core/workloadmeta/collectors/internal/remote/generic.go index af057d240f234..29e49cd2882c1 100644 --- a/comp/core/workloadmeta/collectors/internal/remote/generic.go +++ b/comp/core/workloadmeta/collectors/internal/remote/generic.go @@ -58,7 +58,7 @@ type StreamHandler interface { // NewClient returns a client to connect to a remote gRPC server. NewClient(cc grpc.ClientConnInterface) GrpcClient // HandleResponse handles a response from the remote gRPC server. - HandleResponse(response interface{}) ([]workloadmeta.CollectorEvent, error) + HandleResponse(store workloadmeta.Component, response interface{}) ([]workloadmeta.CollectorEvent, error) // HandleResync is called on resynchronization. HandleResync(store workloadmeta.Component, events []workloadmeta.CollectorEvent) } @@ -230,7 +230,7 @@ func (c *GenericCollector) Run() { continue } - collectorEvents, err := c.StreamHandler.HandleResponse(response) + collectorEvents, err := c.StreamHandler.HandleResponse(c.store, response) if err != nil { log.Warnf("error processing event received from remote workloadmeta: %s", err) continue diff --git a/comp/core/workloadmeta/collectors/internal/remote/processcollector/process_collector.go b/comp/core/workloadmeta/collectors/internal/remote/processcollector/process_collector.go index d50a0f0735233..65e2458b22780 100644 --- a/comp/core/workloadmeta/collectors/internal/remote/processcollector/process_collector.go +++ b/comp/core/workloadmeta/collectors/internal/remote/processcollector/process_collector.go @@ -22,13 +22,16 @@ import ( "github.com/DataDog/datadog-agent/pkg/config" "github.com/DataDog/datadog-agent/pkg/languagedetection/languagemodels" pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/process" + "github.com/DataDog/datadog-agent/pkg/util/containers/metrics" "github.com/DataDog/datadog-agent/pkg/util/flavor" grpcutil "github.com/DataDog/datadog-agent/pkg/util/grpc" "github.com/DataDog/datadog-agent/pkg/util/log" + "github.com/DataDog/datadog-agent/pkg/util/optional" ) const ( - collectorID = "process-collector" + collectorID = "process-collector" + cacheValidityNoRT = 2 * time.Second ) func toLanguage(proto *pbgo.Language) *languagemodels.Language { @@ -40,6 +43,37 @@ func toLanguage(proto *pbgo.Language) *languagemodels.Language { } } +type client struct { + cl pbgo.ProcessEntityStreamClient + parentCollector *streamHandler +} + +func (c *client) StreamEntities(ctx context.Context, opts ...grpc.CallOption) (remote.Stream, error) { //nolint:revive // TODO fix revive unused-parameter + log.Debug("starting a new stream") + streamcl, err := c.cl.StreamEntities( + ctx, + &pbgo.ProcessStreamEntitiesRequest{}, + ) + if err != nil { + return nil, err + } + return &stream{cl: streamcl}, nil +} + +type stream struct { + cl pbgo.ProcessEntityStream_StreamEntitiesClient +} + +func (s *stream) Recv() (interface{}, error) { + log.Trace("calling stream recv") + return s.cl.Recv() +} + +type streamHandler struct { + port int + config.Reader +} + // WorkloadmetaEventFromProcessEventSet converts the given ProcessEventSet into a workloadmeta.Event func WorkloadmetaEventFromProcessEventSet(protoEvent *pbgo.ProcessEventSet) (workloadmeta.Event, error) { if protoEvent == nil { @@ -78,37 +112,6 @@ func WorkloadmetaEventFromProcessEventUnset(protoEvent *pbgo.ProcessEventUnset) }, nil } -type client struct { - cl pbgo.ProcessEntityStreamClient - parentCollector *streamHandler -} - -func (c *client) StreamEntities(ctx context.Context, opts ...grpc.CallOption) (remote.Stream, error) { //nolint:revive // TODO fix revive unused-parameter - log.Debug("starting a new stream") - streamcl, err := c.cl.StreamEntities( - ctx, - &pbgo.ProcessStreamEntitiesRequest{}, - ) - if err != nil { - return nil, err - } - return &stream{cl: streamcl}, nil -} - -type stream struct { - cl pbgo.ProcessEntityStream_StreamEntitiesClient -} - -func (s *stream) Recv() (interface{}, error) { - log.Trace("calling stream recv") - return s.cl.Recv() -} - -type streamHandler struct { - port int - config.Reader -} - // NewCollector returns a remote process collector for workloadmeta if any func NewCollector() (workloadmeta.CollectorProvider, error) { return workloadmeta.CollectorProvider{ @@ -152,7 +155,7 @@ func (s *streamHandler) NewClient(cc grpc.ClientConnInterface) remote.GrpcClient return &client{cl: pbgo.NewProcessEntityStreamClient(cc), parentCollector: s} } -func (s *streamHandler) HandleResponse(resp interface{}) ([]workloadmeta.CollectorEvent, error) { +func (s *streamHandler) HandleResponse(store workloadmeta.Component, resp interface{}) ([]workloadmeta.CollectorEvent, error) { log.Trace("handling response") response, ok := resp.(*pbgo.ProcessStreamResponse) if !ok { @@ -160,9 +163,9 @@ func (s *streamHandler) HandleResponse(resp interface{}) ([]workloadmeta.Collect } collectorEvents := make([]workloadmeta.CollectorEvent, 0, len(response.SetEvents)+len(response.UnsetEvents)) - collectorEvents = handleEvents(collectorEvents, response.UnsetEvents, WorkloadmetaEventFromProcessEventUnset) collectorEvents = handleEvents(collectorEvents, response.SetEvents, WorkloadmetaEventFromProcessEventSet) + s.populateMissingContainerID(collectorEvents, store) log.Tracef("collected [%d] events", len(collectorEvents)) return collectorEvents, nil } @@ -200,3 +203,31 @@ func (s *streamHandler) HandleResync(store workloadmeta.Component, events []work log.Debugf("resync, handling [%d] events", len(processes)) store.ResetProcesses(processes, workloadmeta.SourceRemoteProcessCollector) } + +// populateMissingContainerID populates any missing containerID field in the entities of Set events if it is possible to get the +// container id from the shared container provider +func (s *streamHandler) populateMissingContainerID(collectorEvents []workloadmeta.CollectorEvent, store workloadmeta.Component) { + for idx, event := range collectorEvents { + if event.Type != workloadmeta.EventTypeSet { + continue + } + + processEntity := event.Entity.(*workloadmeta.Process) + pid := processEntity.GetID().ID + ctrID := processEntity.ContainerID + + if ctrID == "" { + pidAsInt, _ := strconv.Atoi(pid) + containerProvider := metrics.GetProvider(optional.NewOption(store)) + ctrIDFromProvider, err := containerProvider.GetMetaCollector().GetContainerIDForPID(pidAsInt, cacheValidityNoRT) + if err != nil { + log.Debugf("failed to get container id for process %s: %v", pid, err) + continue + } + processEntity.ContainerID = ctrIDFromProvider + } + + event.Entity = processEntity + collectorEvents[idx] = event + } +} diff --git a/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta.go b/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta.go index c176a52e88b5c..0e59266436733 100644 --- a/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta.go +++ b/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta.go @@ -139,7 +139,7 @@ func (s *streamHandler) IsEnabled() bool { return true } -func (s *streamHandler) HandleResponse(resp interface{}) ([]workloadmeta.CollectorEvent, error) { +func (s *streamHandler) HandleResponse(_ workloadmeta.Component, resp interface{}) ([]workloadmeta.CollectorEvent, error) { response, ok := resp.(*pb.WorkloadmetaStreamResponse) if !ok { return nil, fmt.Errorf("incorrect response type") diff --git a/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta_test.go b/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta_test.go index ef5ce553861ab..00031f3b48805 100644 --- a/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta_test.go +++ b/comp/core/workloadmeta/collectors/internal/remote/workloadmeta/workloadmeta_test.go @@ -167,6 +167,14 @@ func TestHandleWorkloadmetaStreamResponse(t *testing.T) { }, }, } + // workloadmeta client store + mockClientStore := fxutil.Test[workloadmeta.Mock](t, fx.Options( + core.MockBundle(), + fx.Supply(workloadmeta.Params{ + AgentType: workloadmeta.Remote, + }), + workloadmeta.MockModuleV2(), + )) expectedEvent, err := proto.WorkloadmetaEventFromProtoEvent(protoWorkloadmetaEvent) require.NoError(t, err) @@ -176,7 +184,7 @@ func TestHandleWorkloadmetaStreamResponse(t *testing.T) { } streamhandler := &streamHandler{} - collectorEvents, err := streamhandler.HandleResponse(mockResponse) + collectorEvents, err := streamhandler.HandleResponse(mockClientStore, mockResponse) require.NoError(t, err) assert.Len(t, collectorEvents, 1) diff --git a/pkg/util/containers/metrics/docker/collector_test.go b/pkg/util/containers/metrics/docker/collector_test.go index af248a23b342c..cdc86b3d39da9 100644 --- a/pkg/util/containers/metrics/docker/collector_test.go +++ b/pkg/util/containers/metrics/docker/collector_test.go @@ -20,7 +20,6 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/log/logimpl" "github.com/DataDog/datadog-agent/comp/core/workloadmeta" - "github.com/DataDog/datadog-agent/comp/core/workloadmeta/collectors" "github.com/DataDog/datadog-agent/pkg/util/containers/metrics/provider" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/optional" @@ -87,7 +86,6 @@ func TestGetContainerIDForPID(t *testing.T) { mockStore := fxutil.Test[workloadmeta.Mock](t, fx.Options( config.MockModule(), logimpl.MockModule(), - collectors.GetCatalog(), fx.Supply(workloadmeta.NewParams()), workloadmeta.MockModuleV2(), )) From bb9c488eeb9dd11eaab2119ee13f988a30e61057 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Thu, 30 May 2024 19:12:03 +0200 Subject: [PATCH 45/54] don't touch prev agent (#26137) --- pkg/fleet/installer/service/datadog_agent.go | 11 ----------- test/new-e2e/tests/installer/package_agent_test.go | 1 - 2 files changed, 12 deletions(-) diff --git a/pkg/fleet/installer/service/datadog_agent.go b/pkg/fleet/installer/service/datadog_agent.go index 15067527c2d69..4b49d2d8aa003 100644 --- a/pkg/fleet/installer/service/datadog_agent.go +++ b/pkg/fleet/installer/service/datadog_agent.go @@ -118,9 +118,6 @@ func SetupAgent(ctx context.Context, _ []string) (err error) { return err } } - if err = removeOldAgentFiles(); err != nil { - return fmt.Errorf("failed to remove old agent files: %v", err) - } return nil } @@ -191,14 +188,6 @@ func stopOldAgentUnits(ctx context.Context) error { return nil } -// removeOldAgentFiles removes old agent files -func removeOldAgentFiles() error { - if !oldAgentInstalled() { - return nil - } - return os.RemoveAll(pathOldAgent) -} - // StartAgentExperiment starts the agent experiment func StartAgentExperiment(ctx context.Context) error { return startUnit(ctx, agentExp, "--no-block") diff --git a/test/new-e2e/tests/installer/package_agent_test.go b/test/new-e2e/tests/installer/package_agent_test.go index 9fd65b8bddc8e..6bd89b73d934f 100644 --- a/test/new-e2e/tests/installer/package_agent_test.go +++ b/test/new-e2e/tests/installer/package_agent_test.go @@ -94,7 +94,6 @@ func (s *packageAgentSuite) TestUpgrade_AgentDebRPM_to_OCI() { state = s.host.State() s.assertUnits(state, false) - state.AssertPathDoesNotExist("/opt/datadog-agent") s.host.AssertPackageInstalledByInstaller("datadog-agent") s.host.AssertPackageInstalledByPackageManager("datadog-agent") } From 90eb3685b9e93302d1a189159c96d4b7d5f79902 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Juli=C3=A1n?= Date: Thu, 30 May 2024 19:26:43 +0200 Subject: [PATCH 46/54] [EBPF] Avoid rebuilds and improve kmt.prepare speed (#26122) --- tasks/kernel_matrix_testing/tool.py | 13 ++++-- tasks/kmt.py | 71 +++++++++++++++++++++++------ tasks/system_probe.py | 20 +++----- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/tasks/kernel_matrix_testing/tool.py b/tasks/kernel_matrix_testing/tool.py index a6d332873f5f8..69b4f3de1e7d4 100644 --- a/tasks/kernel_matrix_testing/tool.py +++ b/tasks/kernel_matrix_testing/tool.py @@ -2,6 +2,7 @@ import os import platform +import sys from typing import TYPE_CHECKING import invoke.exceptions as ie @@ -20,24 +21,28 @@ def colored(text: str, color: str | None) -> str: # noqa: U100 return text +def _logprint(msg: str): + print(msg, flush=True, file=sys.stderr) + + def ask(question: str) -> str: return input(colored(question, "blue")) def debug(msg: str): - print(colored(msg, "white")) + _logprint(colored(msg, "white")) def info(msg: str): - print(colored(msg, "green")) + _logprint(colored(msg, "green")) def warn(msg: str): - print(colored(msg, "yellow")) + _logprint(colored(msg, "yellow")) def error(msg: str): - print(colored(msg, "red")) + _logprint(colored(msg, "red")) def Exit(msg: str): diff --git a/tasks/kmt.py b/tasks/kmt.py index 3bc8515a33631..eda7805d592fe 100644 --- a/tasks/kmt.py +++ b/tasks/kmt.py @@ -709,6 +709,37 @@ def build_object_files(ctx, fp): ctx.run(f"ninja -d explain -f {fp}") +def compute_package_dependencies(ctx: Context, packages: list[str]) -> dict[str, set[str]]: + dd_pkg_name = "github.com/DataDog/datadog-agent/" + build_tags = get_sysprobe_buildtags(False, False) + pkg_deps: dict[str, set[str]] = defaultdict(set) + + packages_list = " ".join(packages) + list_format = "{{ .ImportPath }}: {{ join .Deps \" \" }}" + res = ctx.run(f"go list -test -f '{list_format}' -tags \"{build_tags}\" {packages_list}", hide=True) + if res is None or not res.ok: + raise Exit("Failed to get dependencies for system-probe") + + for line in res.stdout.split("\n"): + if ":" not in line: + continue + + pkg, deps = line.split(":", 1) + deps = [d.strip() for d in deps.split(" ")] + dd_deps = [d[len(dd_pkg_name) :] for d in deps if d.startswith(dd_pkg_name)] + + # The import path printed by "go list" is usually path/to/pkg (e.g., pkg/ebpf/verifier). + # However, for test packages it might be either: + # - path/to/pkg.test + # - path/to/pkg [path/to/pkg.test] + # In any case all variants refer to the same variant. This code controls for that + # so that we keep the usual package name. + pkg = pkg.split(" ")[0].removeprefix(dd_pkg_name).removesuffix(".test") + pkg_deps[pkg].update(dd_deps) + + return pkg_deps + + @task def kmt_sysprobe_prepare( ctx: Context, @@ -732,7 +763,6 @@ def kmt_sysprobe_prepare( if packages: filter_pkgs = [os.path.relpath(p) for p in packages.split(",")] - target_packages = build_target_packages(filter_pkgs) kmt_paths = KMTPaths(stack, arch) nf_path = os.path.join(kmt_paths.arch_dir, "kmt-sysprobe.ninja") @@ -745,6 +775,12 @@ def kmt_sysprobe_prepare( go_path = os.path.join(go_root, "bin", "go") build_object_files(ctx, f"{kmt_paths.arch_dir}/kmt-object-files.ninja") + + info("[+] Computing Go dependencies for test packages...") + target_packages = build_target_packages(filter_pkgs) + pkg_deps = compute_package_dependencies(ctx, target_packages) + + info("[+] Generating build instructions..") with open(nf_path, 'w') as ninja_file: nw = NinjaWriter(ninja_file) @@ -762,7 +798,8 @@ def kmt_sysprobe_prepare( ninja_copy_ebpf_files(nw, "system-probe", kmt_paths) for pkg in target_packages: - target_path = os.path.join(kmt_paths.sysprobe_tests, os.path.relpath(pkg, os.getcwd())) + pkg_name = os.path.relpath(pkg, os.getcwd()) + target_path = os.path.join(kmt_paths.sysprobe_tests, pkg_name) output_path = os.path.join(target_path, "testsuite") variables = { "env": env_str, @@ -775,19 +812,22 @@ def kmt_sysprobe_prepare( if extra_arguments: variables["extra_arguments"] = extra_arguments - go_files = [os.path.abspath(i) for i in glob(f"{pkg}/*.go")] - - # We delete the output file to force ninja to rebuild the testsuite everytime - # because it cannot track go dependencies correctly. - ctx.run(f"rm -f {output_path}") - nw.build( - inputs=[pkg], - outputs=[output_path], - implicit=go_files, - rule="gotestsuite", - pool="gobuild", - variables=variables, - ) + go_files = set(glob(f"{pkg}/*.go")) + has_test_files = any(x.lower().endswith("_test.go") for x in go_files) + + # skip packages without test files + if has_test_files: + for deps in pkg_deps[pkg_name]: + go_files.update(os.path.abspath(p) for p in glob(f"{deps}/*.go")) + + nw.build( + inputs=[pkg], + outputs=[output_path], + implicit=list(go_files), + rule="gotestsuite", + pool="gobuild", + variables=variables, + ) if pkg.endswith("java"): nw.build( @@ -829,6 +869,7 @@ def kmt_sysprobe_prepare( }, ) + info("[+] Compiling tests...") ctx.run(f"ninja -d explain -v -f {nf_path}") diff --git a/tasks/system_probe.py b/tasks/system_probe.py index 44d96f71fc159..25a9f49b09029 100644 --- a/tasks/system_probe.py +++ b/tasks/system_probe.py @@ -787,21 +787,13 @@ def go_package_dirs(packages, build_tags): """ target_packages = [] - for pkg in packages: - dirs = ( - check_output( - f"go list -find -f \"{{{{ .Dir }}}}\" -mod=mod -tags \"{','.join(build_tags)}\" {pkg}", - shell=True, - ) - .decode('utf-8') - .strip() - .split("\n") - ) - # Some packages may not be available on all architectures, ignore them - # instead of reporting empty path names - target_packages += [dir for dir in dirs if len(dir) > 0] + format_arg = '{{ .Dir }}' + buildtags_arg = ",".join(build_tags) + packages_arg = " ".join(packages) + cmd = f"go list -find -f \"{format_arg}\" -mod=mod -tags \"{buildtags_arg}\" {packages_arg}" - return target_packages + target_packages = [p.strip() for p in check_output(cmd, shell=True, encoding='utf-8').split("\n")] + return [p for p in target_packages if len(p) > 0] BUILD_COMMIT = os.path.join(KITCHEN_ARTIFACT_DIR, "build.commit") From 4cdfe61a326f2e518649c63c824e82601b4131b1 Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Thu, 30 May 2024 19:26:48 +0200 Subject: [PATCH 47/54] Force file encoding (#26143) --- tasks/libs/common/junit_upload_core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/libs/common/junit_upload_core.py b/tasks/libs/common/junit_upload_core.py index d727be6e0bad9..194223681d78f 100644 --- a/tasks/libs/common/junit_upload_core.py +++ b/tasks/libs/common/junit_upload_core.py @@ -112,7 +112,7 @@ def get_flaky_from_test_output(): for module in DEFAULT_MODULES: test_file = Path(module, TEST_OUTPUT_FILE) if test_file.is_file(): - with test_file.open() as f: + with test_file.open(encoding="utf8") as f: for line in f.readlines(): test_output.append(json.loads(line)) flaky_tests.update( From 098df412bf72b86f9b664fb7420a968d7e054874 Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Thu, 30 May 2024 17:26:53 +0000 Subject: [PATCH 48/54] Sort the tests that are not supposed to be flaky in the TestWasher (#26133) --- tasks/testwasher.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tasks/testwasher.py b/tasks/testwasher.py index d6c3f28730606..3dd51c547f8e7 100644 --- a/tasks/testwasher.py +++ b/tasks/testwasher.py @@ -91,17 +91,15 @@ def process_module_results(self, module_results: list[ModuleTestResult]): """ should_succeed = True - failed_tests_string = "" + failed_tests = [] for module_result in module_results: - non_flaky_failing_tests = self.get_non_flaky_failing_tests(module_result.path) - if non_flaky_failing_tests: + if non_flaky_failing_tests := self.get_non_flaky_failing_tests(module_result.path): should_succeed = False for package, tests in non_flaky_failing_tests.items(): - for test in tests: - failed_tests_string += f"- {package} {test}\n" - if failed_tests_string: + failed_tests.extend(f"- {package} {test}" for test in tests) + if failed_tests: print("The test command failed, the following tests failed and are not supposed to be flaky:") - print(failed_tests_string) + print("\n".join(sorted(failed_tests))) return should_succeed From f3c64e0a7d100ec3f839169a050f05dc5effeff7 Mon Sep 17 00:00:00 2001 From: Jonathan Ribas Date: Thu, 30 May 2024 19:29:38 +0200 Subject: [PATCH 49/54] [CWS] Add a reconnection mechanism for the ptracer (#26090) --- pkg/security/probe/probe_ebpfless.go | 7 +- pkg/security/proto/ebpfless/msg.go | 2 - pkg/security/ptracer/cws.go | 145 +++++++++++++++++---------- 3 files changed, 91 insertions(+), 63 deletions(-) diff --git a/pkg/security/probe/probe_ebpfless.go b/pkg/security/probe/probe_ebpfless.go index cc1a47669b034..5a9393a7fcec8 100644 --- a/pkg/security/probe/probe_ebpfless.go +++ b/pkg/security/probe/probe_ebpfless.go @@ -45,7 +45,6 @@ const ( type client struct { conn net.Conn probe *EBPFLessProbe - seqNum uint64 nsID uint64 containerID string containerName string @@ -81,11 +80,6 @@ type EBPFLessProbe struct { } func (p *EBPFLessProbe) handleClientMsg(cl *client, msg *ebpfless.Message) { - if cl.seqNum != msg.SeqNum { - seclog.Errorf("communication out of sync %d vs %d", cl.seqNum, msg.SeqNum) - } - cl.seqNum++ - switch msg.Type { case ebpfless.MessageTypeHello: if cl.nsID == 0 { @@ -443,6 +437,7 @@ func (p *EBPFLessProbe) handleNewClient(conn net.Conn, ch chan clientMsg) { p.Lock() delete(p.clients, conn) p.Unlock() + conn.Close() msg.Type = ebpfless.MessageTypeGoodbye ch <- msg diff --git a/pkg/security/proto/ebpfless/msg.go b/pkg/security/proto/ebpfless/msg.go index ed4341b7cb726..297c9425031f0 100644 --- a/pkg/security/proto/ebpfless/msg.go +++ b/pkg/security/proto/ebpfless/msg.go @@ -332,7 +332,6 @@ type HelloMsg struct { // Message defines a message type Message struct { - SeqNum uint64 Type MessageType Hello *HelloMsg `json:",omitempty"` Syscall *SyscallMsg `json:",omitempty"` @@ -346,7 +345,6 @@ func (m Message) String() string { // Reset resets a message func (m *Message) Reset() { - m.SeqNum = 0 m.Type = MessageTypeUnknown m.Hello = nil m.Syscall = nil diff --git a/pkg/security/ptracer/cws.go b/pkg/security/ptracer/cws.go index 519cfc0ab931e..a6e7020efdb99 100644 --- a/pkg/security/ptracer/cws.go +++ b/pkg/security/ptracer/cws.go @@ -22,8 +22,6 @@ import ( "syscall" "time" - "go.uber.org/atomic" - "github.com/avast/retry-go/v4" "github.com/vmihailenco/msgpack/v5" "golang.org/x/sys/unix" @@ -169,26 +167,30 @@ func StartCWSPtracer(args []string, envs []string, probeAddr string, opts Opts) wg sync.WaitGroup ) + connectClient := func() error { + var err error + client, err = initConn(probeAddr, 600) + if err != nil { + clientReady <- false + logger.Errorf("connection to system-probe failed!") + return err + } + clientReady <- true + logger.Debugf("connection to system-probe initiated!") + return nil + } + if probeAddr != "" { logger.Debugf("connection to system-probe...") if opts.Async { go func() { - // use a local err variable to avoid race condition - var err error - client, err = initConn(probeAddr, 600) - if err != nil { - return - } - clientReady <- true - logger.Debugf("connection to system-probe initiated!") + _ = connectClient() }() } else { - client, err = initConn(probeAddr, 120) + err = connectClient() if err != nil { return err } - clientReady <- true - logger.Debugf("connection to system-probe initiated!") } } @@ -230,15 +232,10 @@ func StartCWSPtracer(args []string, envs []string, probeAddr string, opts Opts) var ( msgDataChan = make(chan []byte, 100000) - traceChan = make(chan bool) ctx, cancelFnc = context.WithCancel(context.Background()) - seq = atomic.NewUint64(0) ) send := func(msg *ebpfless.Message) { - msg.SeqNum = seq.Load() - seq.Inc() - logger.Debugf("sending message: %s", msg) if probeAddr == "" { @@ -264,47 +261,87 @@ func StartCWSPtracer(args []string, envs []string, probeAddr string, opts Opts) process := NewProcess(tracer.PID) pc.Add(tracer.PID, process) - wg.Add(1) - go func() { - defer wg.Done() - - // start tracing - traceChan <- true + if probeAddr == "" { + send(&ebpfless.Message{ + Type: ebpfless.MessageTypeHello, + Hello: &ebpfless.HelloMsg{ + NSID: getNSID(), + ContainerContext: containerCtx, + EntrypointArgs: args, + }, + }) + } else /* probeAddr != "" */ { + wg.Add(1) + go func() { + defer wg.Done() - if probeAddr != "" { - LOOP: - // wait for the client to be ready of stopped for { - select { - case <-ctx.Done(): - return - case <-clientReady: - break LOOP + LOOP: // wait for the client to be ready or stopped + for { + select { + case <-ctx.Done(): + return + case ready := <-clientReady: + if !ready { + time.Sleep(time.Second) + // re-init connection + logger.Debugf("try to reconnect to system-probe...") + go func() { + _ = connectClient() + }() + continue + } + + defer client.Close() + + // if ready, send an hello message + helloMsg := &ebpfless.Message{ + Type: ebpfless.MessageTypeHello, + Hello: &ebpfless.HelloMsg{ + NSID: getNSID(), + ContainerContext: containerCtx, + EntrypointArgs: args, + }, + } + logger.Debugf("sending message: %s", helloMsg) + data, err := msgpack.Marshal(helloMsg) + if err != nil { + logger.Errorf("unable to marshal message: %v", err) + return + } + if err = sendMsgData(client, data); err != nil { + logger.Debugf("error sending hallo msg: %v", err) + go func() { + _ = connectClient() + }() + continue + } + break LOOP + } } - } - defer client.Close() - } - for { - select { - case data := <-msgDataChan: - if err := sendMsgData(client, data); err != nil { - logger.Debugf("%v", err) + LOOP2: // unqueue and try to send messages or wait client to be stopped + for { + select { + case data := <-msgDataChan: + if err := sendMsgData(client, data); err != nil { + logger.Debugf("error sending msg: %v", err) + msgDataChan <- data + break LOOP2 + } + case <-ctx.Done(): + return + } } - case <-ctx.Done(): - return - } - } - }() - send(&ebpfless.Message{ - Type: ebpfless.MessageTypeHello, - Hello: &ebpfless.HelloMsg{ - NSID: getNSID(), - ContainerContext: containerCtx, - EntrypointArgs: args, - }, - }) + // re-init connection + logger.Debugf("try to reconnect to system-probe...") + go func() { + _ = connectClient() + }() + } + }() + } if !opts.ProcScanDisabled { every := opts.ScanProcEvery @@ -504,8 +541,6 @@ func StartCWSPtracer(args []string, envs []string, probeAddr string, opts Opts) } } - <-traceChan - defer func() { // stop client and msg chan reader cancelFnc() From d71ba9d72b38fab5d4dfd09c31202a38caa186cc Mon Sep 17 00:00:00 2001 From: maxime mouial Date: Thu, 30 May 2024 19:52:48 +0200 Subject: [PATCH 50/54] Adding a new metadata payload for system-probe (#26129) --- cmd/agent/subcommands/diagnose/command.go | 33 +++- .../subcommands/diagnose/command_test.go | 10 + cmd/agent/subcommands/run/command.go | 2 + comp/README.md | 4 + .../settings/settingsimpl/settingsimpl.go | 3 +- comp/metadata/bundle.go | 2 + .../inventoryagentimpl/inventoryagent.go | 12 +- comp/metadata/systemprobe/README.md | 69 +++++++ comp/metadata/systemprobe/def/component.go | 12 ++ comp/metadata/systemprobe/fx/fx.go | 21 ++ .../metadata/systemprobe/impl/system_probe.go | 181 ++++++++++++++++++ .../systemprobe/impl/system_probe_test.go | 143 ++++++++++++++ pkg/config/fetcher/from_processes.go | 8 + pkg/config/model/viper.go | 29 ++- 14 files changed, 507 insertions(+), 22 deletions(-) create mode 100644 comp/metadata/systemprobe/README.md create mode 100644 comp/metadata/systemprobe/def/component.go create mode 100644 comp/metadata/systemprobe/fx/fx.go create mode 100644 comp/metadata/systemprobe/impl/system_probe.go create mode 100644 comp/metadata/systemprobe/impl/system_probe_test.go diff --git a/cmd/agent/subcommands/diagnose/command.go b/cmd/agent/subcommands/diagnose/command.go index 422bb09840518..1142645013d89 100644 --- a/cmd/agent/subcommands/diagnose/command.go +++ b/cmd/agent/subcommands/diagnose/command.go @@ -90,7 +90,8 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { fx.Supply(cliParams), fx.Supply(core.BundleParams{ ConfigParams: config.NewAgentParams(globalParams.ConfFilePath), - LogParams: logimpl.ForOneShot("CORE", "off", true)}), + LogParams: logimpl.ForOneShot("CORE", "off", true), + }), core.Bundle(), // workloadmeta setup collectors.GetCatalog(), @@ -142,7 +143,7 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { payloadV5Cmd := &cobra.Command{ Use: "v5", - Short: "Print the metadata payload for the agent.", + Short: "[internal] Print the metadata payload for the agent.", Long: ` This command print the V5 metadata payload for the Agent. This payload is used to populate the infra list and host map in Datadog. It's called 'V5' because it's the same payload sent since Agent V5. This payload is mandatory in order to create a new host in Datadog.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -156,7 +157,7 @@ This command print the V5 metadata payload for the Agent. This payload is used t payloadGohaiCmd := &cobra.Command{ Use: "gohai", - Short: "Print the gohai payload for the agent.", + Short: "[internal] Print the gohai payload for the agent.", Long: ` This command prints the gohai data sent by the Agent, including current processes running on the machine.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -170,7 +171,7 @@ This command prints the gohai data sent by the Agent, including current processe payloadInventoriesAgentCmd := &cobra.Command{ Use: "inventory-agent", - Short: "Print the Inventory agent metadata payload.", + Short: "[internal] Print the Inventory agent metadata payload.", Long: ` This command print the inventory-agent metadata payload. This payload is used by the 'inventories/sql' product.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -184,7 +185,7 @@ This command print the inventory-agent metadata payload. This payload is used by payloadInventoriesHostCmd := &cobra.Command{ Use: "inventory-host", - Short: "Print the Inventory host metadata payload.", + Short: "[internal] Print the Inventory host metadata payload.", Long: ` This command print the inventory-host metadata payload. This payload is used by the 'inventories/sql' product.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -198,7 +199,7 @@ This command print the inventory-host metadata payload. This payload is used by payloadInventoriesChecksCmd := &cobra.Command{ Use: "inventory-checks", - Short: "Print the Inventory checks metadata payload.", + Short: "[internal] Print the Inventory checks metadata payload.", Long: ` This command print the inventory-checks metadata payload. This payload is used by the 'inventories/sql' product.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -212,7 +213,7 @@ This command print the inventory-checks metadata payload. This payload is used b payloadInventoriesPkgSigningCmd := &cobra.Command{ Use: "package-signing", - Short: "Print the Inventory package signing payload.", + Short: "[internal] Print the Inventory package signing payload.", Long: ` This command print the package-signing metadata payload. This payload is used by the 'fleet automation' product.`, RunE: func(cmd *cobra.Command, args []string) error { @@ -224,12 +225,27 @@ This command print the package-signing metadata payload. This payload is used by }, } + payloadSystemProbeCmd := &cobra.Command{ + Use: "system-probe", + Short: "[internal] Print the inventory systemprobe metadata payload.", + Long: ` +This command print the system-probe metadata payload. This payload is used by the 'fleet automation' product.`, + RunE: func(cmd *cobra.Command, args []string) error { + return fxutil.OneShot(printPayload, + fx.Supply(payloadName("system-probe")), + fx.Supply(command.GetDefaultCoreBundleParams(cliParams.GlobalParams)), + core.Bundle(), + ) + }, + } + showPayloadCommand.AddCommand(payloadV5Cmd) showPayloadCommand.AddCommand(payloadGohaiCmd) showPayloadCommand.AddCommand(payloadInventoriesAgentCmd) showPayloadCommand.AddCommand(payloadInventoriesHostCmd) showPayloadCommand.AddCommand(payloadInventoriesChecksCmd) showPayloadCommand.AddCommand(payloadInventoriesPkgSigningCmd) + showPayloadCommand.AddCommand(payloadSystemProbeCmd) diagnoseCommand.AddCommand(showPayloadCommand) return []*cobra.Command{diagnoseCommand} @@ -239,7 +255,8 @@ func cmdDiagnose(cliParams *cliParams, senderManager diagnosesendermanager.Component, wmeta optional.Option[workloadmeta.Component], ac autodiscovery.Component, - secretResolver secrets.Component) error { + secretResolver secrets.Component, +) error { diagCfg := diagnosis.Config{ Verbose: cliParams.verbose, RunLocal: cliParams.runLocal, diff --git a/cmd/agent/subcommands/diagnose/command_test.go b/cmd/agent/subcommands/diagnose/command_test.go index ffe1b4a5b94c0..c9f303f8e7745 100644 --- a/cmd/agent/subcommands/diagnose/command_test.go +++ b/cmd/agent/subcommands/diagnose/command_test.go @@ -85,3 +85,13 @@ func TestShowMetadataPkgSigningCommand(t *testing.T) { require.Equal(t, false, secretParams.Enabled) }) } + +func TestShowMetadataSystemProbeCommand(t *testing.T) { + fxutil.TestOneShotSubcommand(t, + Commands(&command.GlobalParams{}), + []string{"diagnose", "show-metadata", "system-probe"}, + printPayload, + func(coreParams core.BundleParams, secretParams secrets.Params) { + require.Equal(t, false, secretParams.Enabled) + }) +} diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index 37982354bf397..f6d4df52d4dc7 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -102,6 +102,7 @@ import ( "github.com/DataDog/datadog-agent/comp/metadata/inventoryhost" "github.com/DataDog/datadog-agent/comp/metadata/packagesigning" "github.com/DataDog/datadog-agent/comp/metadata/runner" + systemprobemetadata "github.com/DataDog/datadog-agent/comp/metadata/systemprobe/def" "github.com/DataDog/datadog-agent/comp/ndmtmp" "github.com/DataDog/datadog-agent/comp/netflow" netflowServer "github.com/DataDog/datadog-agent/comp/netflow/server" @@ -233,6 +234,7 @@ func run(log log.Component, _ langDetectionCl.Component, agentAPI internalAPI.Component, _ packagesigning.Component, + _ systemprobemetadata.Component, statusComponent status.Component, collector collector.Component, cloudfoundrycontainer cloudfoundrycontainer.Component, diff --git a/comp/README.md b/comp/README.md index 51b02a7fe1d4d..74bbcf539b1fb 100644 --- a/comp/README.md +++ b/comp/README.md @@ -309,6 +309,10 @@ Package resources implements a component to generate the 'resources' metadata pa Package runner implements a component to generate metadata payload at the right interval. +### [comp/metadata/systemprobe](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/metadata/systemprobe) + +Package def is the metadata provider for system-probe process + ## [comp/ndmtmp](https://pkg.go.dev/github.com/DataDog/datadog-agent/comp/ndmtmp) (Component Bundle) *Datadog Team*: network-device-monitoring diff --git a/comp/core/settings/settingsimpl/settingsimpl.go b/comp/core/settings/settingsimpl/settingsimpl.go index c88c9feed8e25..81e50318fa72f 100644 --- a/comp/core/settings/settingsimpl/settingsimpl.go +++ b/comp/core/settings/settingsimpl/settingsimpl.go @@ -144,9 +144,10 @@ func (s *settingsRegistry) GetFullConfig(namespaces ...string) http.HandlerFunc func (s *settingsRegistry) GetFullConfigBySource() http.HandlerFunc { return func(w http.ResponseWriter, _ *http.Request) { - settings := s.config.AllSettingsBySource() w.Header().Set("Content-Type", "application/json") + settings := s.config.AllSettingsBySource() + jsonData, err := json.Marshal(settings) if err != nil { s.log.Errorf("Unable to marshal config by layer: %s", err) diff --git a/comp/metadata/bundle.go b/comp/metadata/bundle.go index f2896f4018116..87f4518f62c1e 100644 --- a/comp/metadata/bundle.go +++ b/comp/metadata/bundle.go @@ -15,6 +15,7 @@ import ( "github.com/DataDog/datadog-agent/comp/metadata/packagesigning/packagesigningimpl" "github.com/DataDog/datadog-agent/comp/metadata/resources/resourcesimpl" "github.com/DataDog/datadog-agent/comp/metadata/runner/runnerimpl" + systemprobe "github.com/DataDog/datadog-agent/comp/metadata/systemprobe/fx" "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) @@ -30,6 +31,7 @@ func Bundle() fxutil.BundleOptions { inventoryhostimpl.Module(), inventorychecksimpl.Module(), packagesigningimpl.Module(), + systemprobe.Module(), ) } diff --git a/comp/metadata/inventoryagent/inventoryagentimpl/inventoryagent.go b/comp/metadata/inventoryagent/inventoryagentimpl/inventoryagent.go index 02ac8d644f3ca..3db2b6f534b4a 100644 --- a/comp/metadata/inventoryagent/inventoryagentimpl/inventoryagent.go +++ b/comp/metadata/inventoryagent/inventoryagentimpl/inventoryagent.go @@ -432,15 +432,15 @@ func (ia *inventoryagent) getPayload() marshaler.JSONMarshaler { } for source, conf := range layers { - if json, err := ia.marshalAndScrub(conf); err == nil { - data[layersName[source]] = json + if yaml, err := ia.marshalAndScrub(conf); err == nil { + data[layersName[source]] = yaml } } - if json, err := ia.marshalAndScrub(ia.conf.AllSettings()); err == nil { - data["full_configuration"] = json + if yaml, err := ia.marshalAndScrub(ia.conf.AllSettings()); err == nil { + data["full_configuration"] = yaml } - if json, err := ia.marshalAndScrub(ia.conf.AllSettingsWithoutDefault()); err == nil { - data["provided_configuration"] = json + if yaml, err := ia.marshalAndScrub(ia.conf.AllSettingsWithoutDefault()); err == nil { + data["provided_configuration"] = yaml } } diff --git a/comp/metadata/systemprobe/README.md b/comp/metadata/systemprobe/README.md new file mode 100644 index 0000000000000..8849646c137c5 --- /dev/null +++ b/comp/metadata/systemprobe/README.md @@ -0,0 +1,69 @@ +# System-probe metadata Payload + +This package populates some of the system-probe related fields in the `inventories` product in DataDog. More specifically the +`system-probe` table. + +This is enabled by default but can be turned off using `inventories_enabled` config. + +The payload is sent every 10min (see `inventories_max_interval` in the config). + +## System-probe Configuration + +System-probe configurations are scrubbed from any sensitive information (same logic than for the flare). +This include the following: +`full_configuration` +`provided_configuration` +`file_configuration` +`environment_variable_configuration` +`agent_runtime_configuration` +`remote_configuration` +`cli_configuration` +`source_local_configuration` + +Sending System-Probe configuration can be disabled using `inventories_configuration_enabled`. + +# Format + +The payload is a JSON dict with the following fields + +- `hostname` - **string**: the hostname of the agent as shown on the status page. +- `timestamp` - **int**: the timestamp when the payload was created. +- `system_probe_metadata` - **dict of string to JSON type**: + - `agent_version` - **string**: the version of the Agent sending this payload. + - `full_configuration` - **string**: the current System-Probe configuration scrubbed, including all the defaults, as a YAML + string. + - `provided_configuration` - **string**: the current System-Probe configuration (scrubbed), without the defaults, as a YAML + string. This includes the settings configured by the user (throuh the configuration file, the environment, CLI...). + - `file_configuration` - **string**: the System-Probe configuration specified by the configuration file (scrubbed), as a YAML string. + Only the settings written in the configuration file are included, and their value might not match what's applyed by the agent since they can be overriden by other sources. + - `environment_variable_configuration` - **string**: the System-Probe configuration specified by the environment variables (scrubbed), as a YAML string. + Only the settings written in the environment variables are included, and their value might not match what's applyed by the agent somce they can be overriden by other sources. + - `agent_runtime_configuration` - **string**: the System-Probe configuration set by the agent itself (scrubbed), as a YAML string. + Only the settings set by the agent itself are included, and their value might not match what's applyed by the agent since they can be overriden by other sources. + - `remote_configuration` - **string**: the System-Probe configuration specified by the Remote Configuration (scrubbed), as a YAML string. + Only the settings currently used by Remote Configuration are included, and their value might not match what's applyed by the agent since they can be overriden by other sources. + - `cli_configuration` - **string**: the System-Probe configuration specified by the CLI (scrubbed), as a YAML string. + Only the settings set in the CLI are included. + - `source_local_configuration` - **string**: the System-Probe configuration synchronized from the local Agent process, as a YAML string. + +("scrubbed" indicates that secrets are removed from the field value just as they are in logs) + +## Example Payload + +Here an example of an inventory payload: + +``` +{ + "system_probe_metadata": { + "agent_version": "7.55.0", + "full_configuration": "", + "provided_configuration": "system_probe_config:\n sysprobe_socket: /tmp/sysprobe.sock", + "file_configuration": "system_probe_config:\n sysprobe_socket: /tmp/sysprobe.sock", + "environment_variable_configuration": "{}", + "remote_configuration": "{}", + "cli_configuration": "{}" + } + "hostname": "my-host", + "timestamp": 1631281754507358895 +} +``` diff --git a/comp/metadata/systemprobe/def/component.go b/comp/metadata/systemprobe/def/component.go new file mode 100644 index 0000000000000..be510ee393048 --- /dev/null +++ b/comp/metadata/systemprobe/def/component.go @@ -0,0 +1,12 @@ +// 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 2024-present Datadog, Inc. + +// Package def is the metadata provider for system-probe process +package def + +// team: agent-shared-components + +// Component is the component type. +type Component interface{} diff --git a/comp/metadata/systemprobe/fx/fx.go b/comp/metadata/systemprobe/fx/fx.go new file mode 100644 index 0000000000000..bd3c05f63d6b5 --- /dev/null +++ b/comp/metadata/systemprobe/fx/fx.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 2024-present Datadog, Inc. + +// Package fx provides the fx module for the systemprobe metadata component +package fx + +import ( + systemprobeimpl "github.com/DataDog/datadog-agent/comp/metadata/systemprobe/impl" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" +) + +// Module defines the fx options for this component +func Module() fxutil.Module { + return fxutil.Component( + fxutil.ProvideComponentConstructor( + systemprobeimpl.NewComponent, + ), + ) +} diff --git a/comp/metadata/systemprobe/impl/system_probe.go b/comp/metadata/systemprobe/impl/system_probe.go new file mode 100644 index 0000000000000..f96ddb7cec235 --- /dev/null +++ b/comp/metadata/systemprobe/impl/system_probe.go @@ -0,0 +1,181 @@ +// 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 impl implements the systemprobe metadata providers interface +package impl + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "time" + + "github.com/DataDog/datadog-agent/comp/api/api" + "github.com/DataDog/datadog-agent/comp/api/api/utils" + "github.com/DataDog/datadog-agent/comp/api/authtoken" + "github.com/DataDog/datadog-agent/comp/core/config" + flaretypes "github.com/DataDog/datadog-agent/comp/core/flare/types" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + "github.com/DataDog/datadog-agent/comp/metadata/internal/util" + "github.com/DataDog/datadog-agent/comp/metadata/runner/runnerimpl" + "github.com/DataDog/datadog-agent/comp/metadata/systemprobe/def" + configFetcher "github.com/DataDog/datadog-agent/pkg/config/fetcher" + "github.com/DataDog/datadog-agent/pkg/config/model" + "github.com/DataDog/datadog-agent/pkg/serializer" + "github.com/DataDog/datadog-agent/pkg/serializer/marshaler" + "github.com/DataDog/datadog-agent/pkg/util/hostname" + "github.com/DataDog/datadog-agent/pkg/util/optional" + "github.com/DataDog/datadog-agent/pkg/version" + "gopkg.in/yaml.v2" +) + +var ( + // for testing + fetchSystemProbeConfig = configFetcher.SystemProbeConfig + fetchSystemProbeConfigBySource = configFetcher.SystemProbeConfigBySource +) + +// Payload handles the JSON unmarshalling of the metadata payload +type Payload struct { + Hostname string `json:"hostname"` + Timestamp int64 `json:"timestamp"` + Metadata map[string]interface{} `json:"system_probe_metadata"` +} + +// MarshalJSON serialization a Payload to JSON +func (p *Payload) MarshalJSON() ([]byte, error) { + type PayloadAlias Payload + return json.Marshal((*PayloadAlias)(p)) +} + +// SplitPayload implements marshaler.AbstractMarshaler#SplitPayload. +// +// In this case, the payload can't be split any further. +func (p *Payload) SplitPayload(_ int) ([]marshaler.AbstractMarshaler, error) { + return nil, fmt.Errorf("could not split inventories agent payload any more, payload is too big for intake") +} + +type systemprobe struct { + util.InventoryPayload + + log log.Component + conf config.Component + sysprobeConf optional.Option[sysprobeconfig.Component] + hostname string +} + +// Requires defines the dependencies for the systemprobe metadata component +type Requires struct { + Log log.Component + Config config.Component + Serializer serializer.MetricSerializer + // We need the authtoken to be created so we requires the comp. It will be used by configFetcher. + AuthToken authtoken.Component + SysProbeConfig optional.Option[sysprobeconfig.Component] +} + +// Provides defines the output of the systemprobe metadatacomponent +type Provides struct { + Comp def.Component + MetadataProvider runnerimpl.Provider + FlareProvider flaretypes.Provider + Endpoint api.AgentEndpointProvider +} + +// NewComponent creates a new systemprobe metadata Component +func NewComponent(deps Requires) Provides { + hname, _ := hostname.Get(context.Background()) + sb := &systemprobe{ + log: deps.Log, + conf: deps.Config, + hostname: hname, + sysprobeConf: deps.SysProbeConfig, + } + sb.InventoryPayload = util.CreateInventoryPayload(deps.Config, deps.Log, deps.Serializer, sb.getPayload, "system-probe.json") + + return Provides{ + Comp: sb, + MetadataProvider: sb.MetadataProvider(), + FlareProvider: sb.FlareProvider(), + Endpoint: api.NewAgentEndpointProvider(sb.writePayloadAsJSON, "/metadata/system-probe", "GET"), + } +} + +func (sb *systemprobe) writePayloadAsJSON(w http.ResponseWriter, _ *http.Request) { + // GetAsJSON calls getPayload which already scrub the data + scrubbed, err := sb.GetAsJSON() + if err != nil { + utils.SetJSONError(w, err, 500) + return + } + w.Write(scrubbed) +} + +func (sb *systemprobe) getConfigLayers() map[string]interface{} { + metadata := map[string]interface{}{ + "agent_version": version.AgentVersion, + } + + if !sb.conf.GetBool("inventories_configuration_enabled") { + return metadata + } + + sysprobeConf, isSet := sb.sysprobeConf.Get() + if !isSet { + sb.log.Debugf("system-probe config not available: disabling systemprobe metadata") + return metadata + } + + rawLayers, err := fetchSystemProbeConfigBySource(sysprobeConf) + if err != nil { + sb.log.Debugf("error fetching system-probe config layers: %s", err) + return metadata + } + + configBySources := map[string]interface{}{} + if err := json.Unmarshal([]byte(rawLayers), &configBySources); err != nil { + sb.log.Debugf("error unmarshalling systemprobe config by source: %s", err) + return metadata + } + + layersName := map[model.Source]string{ + model.SourceFile: "file_configuration", + model.SourceEnvVar: "environment_variable_configuration", + model.SourceAgentRuntime: "agent_runtime_configuration", + model.SourceLocalConfigProcess: "source_local_configuration", + model.SourceRC: "remote_configuration", + model.SourceCLI: "cli_configuration", + model.SourceProvided: "provided_configuration", + } + for source, conf := range configBySources { + if layer, ok := layersName[model.Source(source)]; ok { + if yamlStr, err := yaml.Marshal(conf); err == nil { + metadata[layer] = string(yamlStr) + } else { + sb.log.Debugf("error serializing systemprobe '%s' config layer: %s", source, err) + } + } else { + sb.log.Debugf("error unknown config layer from system-probe '%s'", source) + } + } + + if str, err := fetchSystemProbeConfig(sysprobeConf); err == nil { + metadata["full_configuration"] = str + } else { + sb.log.Debugf("error fetching system-probe config: %s", err) + } + + return metadata +} + +func (sb *systemprobe) getPayload() marshaler.JSONMarshaler { + return &Payload{ + Hostname: sb.hostname, + Timestamp: time.Now().UnixNano(), + Metadata: sb.getConfigLayers(), + } +} diff --git a/comp/metadata/systemprobe/impl/system_probe_test.go b/comp/metadata/systemprobe/impl/system_probe_test.go new file mode 100644 index 0000000000000..28e940a8a7102 --- /dev/null +++ b/comp/metadata/systemprobe/impl/system_probe_test.go @@ -0,0 +1,143 @@ +// 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 impl implements the systemprobe metadata providers interface +package impl + +import ( + "encoding/json" + "io" + "net/http/httptest" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/fx" + + "github.com/DataDog/datadog-agent/comp/api/authtoken" + authtokenimpl "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/DataDog/datadog-agent/comp/core/log" + "github.com/DataDog/datadog-agent/comp/core/log/logimpl" + "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig" + "github.com/DataDog/datadog-agent/comp/core/sysprobeconfig/sysprobeconfigimpl" + configFetcher "github.com/DataDog/datadog-agent/pkg/config/fetcher" + "github.com/DataDog/datadog-agent/pkg/config/model" + "github.com/DataDog/datadog-agent/pkg/serializer" + "github.com/DataDog/datadog-agent/pkg/util/fxutil" + "github.com/DataDog/datadog-agent/pkg/util/optional" + "github.com/DataDog/datadog-agent/pkg/version" +) + +func setupFecther(t *testing.T) { + t.Cleanup(func() { + fetchSystemProbeConfig = configFetcher.SystemProbeConfig + fetchSystemProbeConfigBySource = configFetcher.SystemProbeConfigBySource + }) + + fetchSystemProbeConfig = func(_ model.Reader) (string, error) { return "full config", nil } + fetchSystemProbeConfigBySource = func(_ model.Reader) (string, error) { + data, err := json.Marshal(map[string]interface{}{ + string(model.SourceFile): map[string]bool{"file": true}, + string(model.SourceEnvVar): map[string]bool{"env": true}, + string(model.SourceAgentRuntime): map[string]bool{"runtime": true}, + string(model.SourceLocalConfigProcess): map[string]bool{"local": true}, + string(model.SourceRC): map[string]bool{"rc": true}, + string(model.SourceCLI): map[string]bool{"cli": true}, + string(model.SourceProvided): map[string]bool{"provided": true}, + }) + return string(data), err + } +} + +func getSystemProbeComp(t *testing.T, enableConfig bool) *systemprobe { + l := fxutil.Test[log.Component](t, logimpl.MockModule()) + + cfg := fxutil.Test[config.Component](t, config.MockModule()) + cfg.Set("inventories_configuration_enabled", enableConfig, model.SourceUnknown) + + r := Requires{ + Log: l, + Config: cfg, + Serializer: &serializer.MockSerializer{}, + AuthToken: fxutil.Test[authtoken.Component](t, + authtokenimpl.Module(), + fx.Provide(func() log.Component { return l }), + fx.Provide(func() config.Component { return cfg }), + ), + SysProbeConfig: fxutil.Test[optional.Option[sysprobeconfig.Component]](t, sysprobeconfigimpl.MockModule()), + } + + comp := NewComponent(r).Comp + return comp.(*systemprobe) +} + +func assertPayload(t *testing.T, p *Payload) { + assert.Equal(t, "test hostname", p.Hostname) + assert.True(t, p.Timestamp < time.Now().UnixNano()) + assert.Equal(t, + map[string]interface{}{ + "agent_runtime_configuration": "runtime: true\n", + "cli_configuration": "cli: true\n", + "environment_variable_configuration": "env: true\n", + "file_configuration": "file: true\n", + "full_configuration": "full config", + "provided_configuration": "provided: true\n", + "remote_configuration": "rc: true\n", + "source_local_configuration": "local: true\n", + "agent_version": version.AgentVersion, + }, + p.Metadata) +} + +func TestGetPayload(t *testing.T) { + setupFecther(t) + sb := getSystemProbeComp(t, true) + + sb.hostname = "test hostname" + + p := sb.getPayload().(*Payload) + assertPayload(t, p) +} + +func TestGetPayloadNoConfig(t *testing.T) { + setupFecther(t) + sb := getSystemProbeComp(t, false) + + sb.hostname = "test hostname" + + p := sb.getPayload().(*Payload) + assert.Equal(t, "test hostname", p.Hostname) + assert.True(t, p.Timestamp <= time.Now().UnixNano()) + assert.Equal(t, + map[string]interface{}{ + "agent_version": version.AgentVersion, + }, + p.Metadata) +} + +func TestWritePayload(t *testing.T) { + setupFecther(t) + sb := getSystemProbeComp(t, true) + + sb.hostname = "test hostname" + + req := httptest.NewRequest("GET", "http://fake_url.com", nil) + w := httptest.NewRecorder() + + sb.writePayloadAsJSON(w, req) + + resp := w.Result() + body, err := io.ReadAll(resp.Body) + resp.Body.Close() + require.NoError(t, err) + + p := Payload{} + err = json.Unmarshal(body, &p) + require.NoError(t, err) + + assertPayload(t, &p) +} diff --git a/pkg/config/fetcher/from_processes.go b/pkg/config/fetcher/from_processes.go index 02066dc726c13..59d6e3ebb7250 100644 --- a/pkg/config/fetcher/from_processes.go +++ b/pkg/config/fetcher/from_processes.go @@ -95,3 +95,11 @@ func SystemProbeConfig(config config.Reader) (string, error) { c := settingshttp.NewClient(hc, "http://localhost/config", "system-probe", settingshttp.NewHTTPClientOptions(util.CloseConnection)) return c.FullConfig() } + +// SystemProbeConfigBySource fetch the all configuration layers from the system-probe process by querying its API +func SystemProbeConfigBySource(config config.Reader) (string, error) { + hc := client.Get(config.GetString("system_probe_config.sysprobe_socket")) + + c := settingshttp.NewClient(hc, "http://localhost/config", "system-probe", settingshttp.NewHTTPClientOptions(util.CloseConnection)) + return c.FullConfigBySource() +} diff --git a/pkg/config/model/viper.go b/pkg/config/model/viper.go index 6f3d4942fcc8e..acb5b004f48ef 100644 --- a/pkg/config/model/viper.go +++ b/pkg/config/model/viper.go @@ -29,14 +29,28 @@ type Source string // Declare every known Source const ( - SourceDefault Source = "default" - SourceUnknown Source = "unknown" - SourceFile Source = "file" - SourceEnvVar Source = "environment-variable" - SourceAgentRuntime Source = "agent-runtime" + // SourceDefault are the values from defaults. + SourceDefault Source = "default" + // SourceUnknown are the values from unknown source. This should only be used in tests when calling + // SetWithoutSource. + SourceUnknown Source = "unknown" + // SourceFile are the values loaded from configuration file. + SourceFile Source = "file" + // SourceEnvVar are the values loaded from the environment variables. + SourceEnvVar Source = "environment-variable" + // SourceAgentRuntime are the values configured by the agent itself. The agent can dynamically compute the best + // value for some settings when not set by the user. + SourceAgentRuntime Source = "agent-runtime" + // SourceLocalConfigProcess are the values mirrored from the config process. The config process is the + // core-agent. This is used when side process like security-agent or trace-agent pull their configuration from + // the core-agent. SourceLocalConfigProcess Source = "local-config-process" - SourceRC Source = "remote-config" - SourceCLI Source = "cli" + // SourceRC are the values loaded from remote-config (aka Datadog backend) + SourceRC Source = "remote-config" + // SourceCLI are the values set by the user at runtime through the CLI. + SourceCLI Source = "cli" + // SourceProvided are all values set by any source but default. + SourceProvided Source = "provided" // everything but defaults ) // sources list the known sources, following the order of hierarchy between them @@ -644,6 +658,7 @@ func (c *safeConfig) AllSettingsBySource() map[Source]interface{} { for _, source := range sources { res[source] = c.configSources[source].AllSettingsWithoutDefault() } + res[SourceProvided] = c.AllSettingsWithoutDefault() return res } From cded074961f2232fa5846e77c48a149b0f4d61b9 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Thu, 30 May 2024 13:54:29 -0400 Subject: [PATCH 51/54] add SystemTemp to ignore list (#26141) --- test/new-e2e/tests/windows/install-test/system_file_tester.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/new-e2e/tests/windows/install-test/system_file_tester.go b/test/new-e2e/tests/windows/install-test/system_file_tester.go index 70fa8d663783c..623b11abbd24b 100644 --- a/test/new-e2e/tests/windows/install-test/system_file_tester.go +++ b/test/new-e2e/tests/windows/install-test/system_file_tester.go @@ -38,6 +38,7 @@ func SystemPaths() []string { `c:\windows\ServiceProfiles\NetworkService\AppData\`, `C:\Windows\System32\Tasks\`, `C:\Windows\System32\spp\`, + `C:\Windows\SystemTemp\`, } } From 9f16962a8d84cfd7035e050617666fc24e32e71d Mon Sep 17 00:00:00 2001 From: JoJo Date: Thu, 30 May 2024 14:18:20 -0400 Subject: [PATCH 52/54] Decrease database instance collection interval (#26142) --- pkg/collector/corechecks/oracle/config/config.go | 4 ++-- pkg/collector/corechecks/oracle/metadata.go | 2 +- pkg/collector/corechecks/oracle/oracle.go | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/collector/corechecks/oracle/config/config.go b/pkg/collector/corechecks/oracle/config/config.go index ce1be8b857281..28695f3caf032 100644 --- a/pkg/collector/corechecks/oracle/config/config.go +++ b/pkg/collector/corechecks/oracle/config/config.go @@ -158,7 +158,7 @@ type InstanceConfig struct { UseGlobalCustomQueries string `yaml:"use_global_custom_queries"` CustomQueries []CustomQuery `yaml:"custom_queries"` MetricCollectionInterval int64 `yaml:"metric_collection_interval"` - DatabaseInstanceCollectionInterval uint64 `yaml:"database_instance_collection_interval"` + DatabaseInstanceCollectionInterval int64 `yaml:"database_instance_collection_interval"` Asm asmConfig `yaml:"asm"` ResourceManager resourceManagerConfig `yaml:"resource_manager"` Locks locksConfig `yaml:"locks"` @@ -229,7 +229,7 @@ func NewCheckConfig(rawInstance integration.Data, rawInitConfig integration.Data instance.UseGlobalCustomQueries = "true" - instance.DatabaseInstanceCollectionInterval = 1800 + instance.DatabaseInstanceCollectionInterval = 300 instance.Loader = defaultLoader initCfg.Loader = defaultLoader diff --git a/pkg/collector/corechecks/oracle/metadata.go b/pkg/collector/corechecks/oracle/metadata.go index 2f06a76d66193..d81a66f325a7d 100644 --- a/pkg/collector/corechecks/oracle/metadata.go +++ b/pkg/collector/corechecks/oracle/metadata.go @@ -28,7 +28,7 @@ type dbInstanceEvent struct { AgentVersion string `json:"agent_version"` Dbms string `json:"dbms"` Kind string `json:"kind"` - CollectionInterval uint64 `json:"collection_interval"` + CollectionInterval int64 `json:"collection_interval"` DbmsVersion string `json:"dbms_version"` Tags []string `json:"tags"` Timestamp float64 `json:"timestamp"` diff --git a/pkg/collector/corechecks/oracle/oracle.go b/pkg/collector/corechecks/oracle/oracle.go index 94779cb4a5c82..b52683c17f46a 100644 --- a/pkg/collector/corechecks/oracle/oracle.go +++ b/pkg/collector/corechecks/oracle/oracle.go @@ -182,7 +182,7 @@ func (c *Check) Run() error { c.connection = conn } - dbInstanceIntervalExpired := checkIntervalExpired(&c.dbInstanceLastRun, 1800) + dbInstanceIntervalExpired := checkIntervalExpired(&c.dbInstanceLastRun, c.config.DatabaseInstanceCollectionInterval) if dbInstanceIntervalExpired && !c.legacyIntegrationCompatibilityMode && !c.config.OnlyCustomQueries { err := sendDbInstanceMetadata(c) From 49deb2606228bf050db9f9354ddd81a1fe5a7f8d Mon Sep 17 00:00:00 2001 From: Zarir Hamza Date: Thu, 30 May 2024 14:21:26 -0400 Subject: [PATCH 53/54] Implements file reading for peer tags to synchronize cross repo usage (#25600) --- cmd/serverless/dependencies_linux_amd64.txt | 1 + cmd/serverless/dependencies_linux_arm64.txt | 1 + .../otlp/components/statsprocessor/go.mod | 1 + .../otlp/components/statsprocessor/go.sum | 2 + pkg/trace/go.mod | 1 + pkg/trace/go.sum | 2 + pkg/trace/stats/concentrator.go | 56 +++++++++---------- pkg/trace/stats/concentrator_test.go | 49 ++++++++++++++++ pkg/trace/stats/oteltest/go.mod | 1 + pkg/trace/stats/oteltest/go.sum | 2 + pkg/trace/stats/peer_tags.ini | 21 +++++++ 11 files changed, 106 insertions(+), 31 deletions(-) create mode 100644 pkg/trace/stats/peer_tags.ini diff --git a/cmd/serverless/dependencies_linux_amd64.txt b/cmd/serverless/dependencies_linux_amd64.txt index b5209492e0011..2d22c14306121 100644 --- a/cmd/serverless/dependencies_linux_amd64.txt +++ b/cmd/serverless/dependencies_linux_amd64.txt @@ -909,6 +909,7 @@ gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof gopkg.in/DataDog/dd-trace-go.v1/internal/version +gopkg.in/ini.v1 gopkg.in/yaml.v2 gopkg.in/yaml.v3 hash diff --git a/cmd/serverless/dependencies_linux_arm64.txt b/cmd/serverless/dependencies_linux_arm64.txt index 49379557da056..9158ba1484cbd 100644 --- a/cmd/serverless/dependencies_linux_arm64.txt +++ b/cmd/serverless/dependencies_linux_arm64.txt @@ -908,6 +908,7 @@ gopkg.in/DataDog/dd-trace-go.v1/internal/samplernames gopkg.in/DataDog/dd-trace-go.v1/internal/telemetry gopkg.in/DataDog/dd-trace-go.v1/internal/traceprof gopkg.in/DataDog/dd-trace-go.v1/internal/version +gopkg.in/ini.v1 gopkg.in/yaml.v2 gopkg.in/yaml.v3 hash diff --git a/comp/otelcol/otlp/components/statsprocessor/go.mod b/comp/otelcol/otlp/components/statsprocessor/go.mod index f79c5e7056aab..2769abb4681b4 100644 --- a/comp/otelcol/otlp/components/statsprocessor/go.mod +++ b/comp/otelcol/otlp/components/statsprocessor/go.mod @@ -101,6 +101,7 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/grpc v1.63.2 // indirect google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/comp/otelcol/otlp/components/statsprocessor/go.sum b/comp/otelcol/otlp/components/statsprocessor/go.sum index ff32884e6938c..0ef2955aec8e3 100644 --- a/comp/otelcol/otlp/components/statsprocessor/go.sum +++ b/comp/otelcol/otlp/components/statsprocessor/go.sum @@ -286,6 +286,8 @@ google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/trace/go.mod b/pkg/trace/go.mod index 5ecd607237c5b..b1c690ec3f6de 100644 --- a/pkg/trace/go.mod +++ b/pkg/trace/go.mod @@ -45,6 +45,7 @@ require ( golang.org/x/time v0.5.0 google.golang.org/grpc v1.63.2 google.golang.org/protobuf v1.34.0 + gopkg.in/ini.v1 v1.67.0 k8s.io/apimachinery v0.25.5 ) diff --git a/pkg/trace/go.sum b/pkg/trace/go.sum index 8776938637f0f..c65e5535c41f7 100644 --- a/pkg/trace/go.sum +++ b/pkg/trace/go.sum @@ -435,6 +435,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/trace/stats/concentrator.go b/pkg/trace/stats/concentrator.go index ef5fe2cc98c0a..138156d6b7d42 100644 --- a/pkg/trace/stats/concentrator.go +++ b/pkg/trace/stats/concentrator.go @@ -6,11 +6,14 @@ package stats import ( + _ "embed" "sort" "strings" "sync" "time" + "gopkg.in/ini.v1" + pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/trace" "github.com/DataDog/datadog-agent/pkg/trace/config" "github.com/DataDog/datadog-agent/pkg/trace/log" @@ -55,37 +58,28 @@ type Concentrator struct { statsd statsd.ClientInterface } -var defaultPeerTags = []string{ - "_dd.base_service", - "amqp.destination", - "amqp.exchange", - "amqp.queue", - "aws.queue.name", - "bucketname", - "cassandra.cluster", - "db.cassandra.contact.points", - "db.couchbase.seed.nodes", - "db.hostname", - "db.instance", - "db.name", - "db.system", - "hazelcast.instance", - "messaging.kafka.bootstrap.servers", - "mongodb.db", - "msmq.queue.path", - "net.peer.name", - "network.destination.name", - "peer.hostname", - "peer.service", - "queuename", - "rpc.service", - "rulename", - "server.address", - "statemachinename", - "streamname", - "tablename", - "topicname", -} +//go:embed peer_tags.ini +var peerTagFile []byte + +var defaultPeerTags = func() []string { + var tags []string = []string{"_dd.base_service"} + + cfg, err := ini.Load(peerTagFile) + if err != nil { + log.Error("Error loading file for peer tags: ", err) + return tags + } + keys := cfg.Section("dd.apm.peer.tags").Keys() + + for _, key := range keys { + value := strings.Split(key.Value(), ",") + tags = append(tags, value...) + } + + sort.Strings(tags) + + return tags +}() func preparePeerTags(tags ...string) []string { if len(tags) == 0 { diff --git a/pkg/trace/stats/concentrator_test.go b/pkg/trace/stats/concentrator_test.go index 1bc45cc532d42..274323c2aa7f6 100644 --- a/pkg/trace/stats/concentrator_test.go +++ b/pkg/trace/stats/concentrator_test.go @@ -1062,3 +1062,52 @@ func TestPreparePeerTags(t *testing.T) { assert.Equal(t, tc.output, preparePeerTags(tc.input...)) } } + +func TestDefaultPeerTags(t *testing.T) { + expectedListOfPeerTags := []string{ + "_dd.base_service", + "amqp.destination", + "amqp.exchange", + "amqp.queue", + "aws.queue.name", + "aws.s3.bucket", + "bucketname", + "cassandra.keyspace", + "db.cassandra.contact.points", + "db.couchbase.seed.nodes", + "db.hostname", + "db.instance", + "db.name", + "db.namespace", + "db.system", + "grpc.host", + "hostname", + "http.host", + "http.server_name", + "messaging.destination", + "messaging.destination.name", + "messaging.kafka.bootstrap.servers", + "messaging.rabbitmq.exchange", + "messaging.system", + "mongodb.db", + "msmq.queue.path", + "net.peer.name", + "network.destination.name", + "peer.hostname", + "peer.service", + "queuename", + "rpc.service", + "rpc.system", + "server.address", + "streamname", + "tablename", + "topicname", + } + actualListOfPeerTags := defaultPeerTags + + // Sort both arrays for comparison + sort.Strings(actualListOfPeerTags) + sort.Strings(expectedListOfPeerTags) + + assert.Equal(t, expectedListOfPeerTags, actualListOfPeerTags) +} diff --git a/pkg/trace/stats/oteltest/go.mod b/pkg/trace/stats/oteltest/go.mod index 6a3c378f83f54..f2e86350ceeb3 100644 --- a/pkg/trace/stats/oteltest/go.mod +++ b/pkg/trace/stats/oteltest/go.mod @@ -90,6 +90,7 @@ require ( golang.org/x/tools v0.16.1 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect google.golang.org/grpc v1.63.2 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/pkg/trace/stats/oteltest/go.sum b/pkg/trace/stats/oteltest/go.sum index c41384764df4a..37b2159b19d76 100644 --- a/pkg/trace/stats/oteltest/go.sum +++ b/pkg/trace/stats/oteltest/go.sum @@ -288,6 +288,8 @@ google.golang.org/protobuf v1.34.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHh gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/trace/stats/peer_tags.ini b/pkg/trace/stats/peer_tags.ini new file mode 100644 index 0000000000000..d1b36aacf7e5c --- /dev/null +++ b/pkg/trace/stats/peer_tags.ini @@ -0,0 +1,21 @@ +# if a peer entity tag is expected to be set at the tracer, and we want to keep it, then it must be included +# in the list. I.e., since we have at least one tracer which actually sets `peer.hostname` directly, we need +# to set `peer.hostname = "peer.hostname" else it will get stripped out +# the order of the precursors matters, as it is "first one wins". so if we expect some spans to have more than one +# precursor, and the precursors may not always have the exact same meaning, then put the higher priority one at the front +[dd.apm.peer.tags] +peer.aws.dynamodb.table = "tablename" +peer.aws.kinesis.stream = "streamname" +peer.aws.s3.bucket = "bucketname,aws.s3.bucket" +peer.aws.sqs.queue = "queuename" +peer.cassandra.contact.points = "db.cassandra.contact.points" +peer.couchbase.seed.nodes = "db.couchbase.seed.nodes" +peer.db.name = "db.name,mongodb.db,db.instance,cassandra.keyspace,db.namespace" +peer.db.system = "db.system" +peer.hostname = "peer.hostname,hostname,net.peer.name,db.hostname,network.destination.name,grpc.host,http.host,server.address,http.server_name" +peer.kafka.bootstrap.servers = "messaging.kafka.bootstrap.servers" +peer.messaging.destination = "topicname,messaging.destination,messaging.destination.name,messaging.rabbitmq.exchange,amqp.destination,amqp.queue,amqp.exchange,msmq.queue.path,aws.queue.name" +peer.messaging.system = "messaging.system" +peer.rpc.service = "rpc.service" +peer.rpc.system = "rpc.system" +peer.service = "peer.service" From 696595c25874bdf111287fb12a04b4ffd5e1c5cb Mon Sep 17 00:00:00 2001 From: Brian Floersch Date: Thu, 30 May 2024 14:25:24 -0400 Subject: [PATCH 54/54] Use RWMutex in demultiplexer to prevent check delays (#26148) --- pkg/aggregator/demultiplexer_agent.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pkg/aggregator/demultiplexer_agent.go b/pkg/aggregator/demultiplexer_agent.go index c541d95a108a0..1512c756d86fe 100644 --- a/pkg/aggregator/demultiplexer_agent.go +++ b/pkg/aggregator/demultiplexer_agent.go @@ -46,7 +46,7 @@ type DemultiplexerWithAggregator interface { type AgentDemultiplexer struct { log log.Component - m sync.Mutex + m sync.RWMutex // stopChan completely stops the flushLoop of the Demultiplexer when receiving // a message, not doing anything else. @@ -391,8 +391,8 @@ func (d *AgentDemultiplexer) ForceFlushToSerializer(start time.Time, waitForSeri // - to have an implementation of SendIterableSeries listening on multiple sinks in parallel, or, // - to have a thread-safe implementation of the underlying `util.BufferedChan`. func (d *AgentDemultiplexer) flushToSerializer(start time.Time, waitForSerializer bool) { - d.m.Lock() - defer d.m.Unlock() + d.m.RLock() + defer d.m.RUnlock() if d.aggregator == nil { // NOTE(remy): we could consider flushing only the time samplers @@ -561,8 +561,8 @@ func (d *AgentDemultiplexer) DumpDogstatsdContexts(dest io.Writer) error { // If no error is returned here, DestroySender must be called with the same ID // once the sender is not used anymore func (d *AgentDemultiplexer) GetSender(id checkid.ID) (sender.Sender, error) { - d.m.Lock() - defer d.m.Unlock() + d.m.RLock() + defer d.m.RUnlock() if d.senders == nil { return nil, errors.New("demultiplexer is stopped") @@ -574,8 +574,8 @@ func (d *AgentDemultiplexer) GetSender(id checkid.ID) (sender.Sender, error) { // SetSender returns the passed sender with the passed ID. // This is largely for testing purposes func (d *AgentDemultiplexer) SetSender(s sender.Sender, id checkid.ID) error { - d.m.Lock() - defer d.m.Unlock() + d.m.RLock() + defer d.m.RUnlock() if d.senders == nil { return errors.New("demultiplexer is stopped") } @@ -587,8 +587,8 @@ func (d *AgentDemultiplexer) SetSender(s sender.Sender, id checkid.ID) error { // Should be called when no sender with this ID is used anymore // The metrics of this (these) sender(s) that haven't been flushed yet will be lost func (d *AgentDemultiplexer) DestroySender(id checkid.ID) { - d.m.Lock() - defer d.m.Unlock() + d.m.RLock() + defer d.m.RUnlock() if d.senders == nil { return @@ -599,8 +599,8 @@ func (d *AgentDemultiplexer) DestroySender(id checkid.ID) { // GetDefaultSender returns a default sender. func (d *AgentDemultiplexer) GetDefaultSender() (sender.Sender, error) { - d.m.Lock() - defer d.m.Unlock() + d.m.RLock() + defer d.m.RUnlock() if d.senders == nil { return nil, errors.New("demultiplexer is stopped")