From 8dc9c9de03ecbc56f47a301181f7e25aa0ebb009 Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Fri, 13 Dec 2024 17:40:37 +0100 Subject: [PATCH 01/78] fix(package_size): Sort the size in display message (#32014) --- tasks/libs/package/size.py | 34 +++++--------- tasks/libs/package/utils.py | 65 ++++++++++++++++++++++++++- tasks/package.py | 26 ++++++----- tasks/unit_tests/package_lib_tests.py | 51 +++++++++++++++------ tasks/unit_tests/package_tests.py | 18 +++++--- 5 files changed, 140 insertions(+), 54 deletions(-) diff --git a/tasks/libs/package/size.py b/tasks/libs/package/size.py index dad67823d3223..4743dfcd906de 100644 --- a/tasks/libs/package/size.py +++ b/tasks/libs/package/size.py @@ -7,7 +7,7 @@ from tasks.libs.common.constants import ORIGIN_CATEGORY, ORIGIN_PRODUCT, ORIGIN_SERVICE from tasks.libs.common.git import get_default_branch from tasks.libs.common.utils import get_metric_origin -from tasks.libs.package.utils import get_package_path +from tasks.libs.package.utils import find_package DEBIAN_OS = "debian" CENTOS_OS = "centos" @@ -158,33 +158,23 @@ def compute_package_size_metrics( return series -def compare(ctx, package_sizes, ancestor, arch, flavor, os_name, threshold): +def compare(ctx, package_sizes, ancestor, pkg_size): """ - Compare (or update) a package size with the ancestor package size. + Compare (or update, when on main branch) a package size with the ancestor package size. """ - if os_name == 'suse': - dir = os.environ['OMNIBUS_PACKAGE_DIR_SUSE'] - path = f'{dir}/{flavor}-7*{arch}.rpm' - else: - dir = os.environ['OMNIBUS_PACKAGE_DIR'] - separator = '_' if os_name == 'deb' else '-' - path = f'{dir}/{flavor}{separator}7*{arch}.{os_name}' - package_size = _get_uncompressed_size(ctx, get_package_path(path), os_name) + current_size = _get_uncompressed_size(ctx, find_package(pkg_size.path()), pkg_size.os) if os.environ['CI_COMMIT_REF_NAME'] == get_default_branch(): - package_sizes[ancestor][arch][flavor][os_name] = package_size + # On main, ancestor is the current commit, so we set the current value + package_sizes[ancestor][pkg_size.arch][pkg_size.flavor][pkg_size.os] = current_size return - previous_size = package_sizes[ancestor][arch][flavor][os_name] - diff = package_size - previous_size - - message = f"{flavor}-{arch}-{os_name} size {mb(package_size)} is OK: {mb(diff)} diff with previous {mb(previous_size)} (max: {mb(threshold)})" + previous_size = package_sizes[ancestor][pkg_size.arch][pkg_size.flavor][pkg_size.os] + pkg_size.compare(current_size, previous_size) - if diff > threshold: - emoji = "❌" - print(color_message(message.replace('OK', 'too large'), Color.RED), file=sys.stderr) + if pkg_size.ko(): + print(color_message(pkg_size.log(), Color.RED), file=sys.stderr) else: - emoji = "✅" if diff <= 0 else "⚠️" - print(message) - return f"|{flavor}-{arch}-{os_name}|{mb(diff)}|{emoji}|{mb(package_size)}|{mb(previous_size)}|{mb(threshold)}|" + print(pkg_size.log()) + return pkg_size def mb(value): diff --git a/tasks/libs/package/utils.py b/tasks/libs/package/utils.py index 298901732c33e..ec8363fcfa134 100644 --- a/tasks/libs/package/utils.py +++ b/tasks/libs/package/utils.py @@ -1,5 +1,6 @@ import glob import json +import os from invoke import Exit, UnexpectedExit @@ -11,7 +12,67 @@ PACKAGE_SIZE_S3_CI_BUCKET_URL = "s3://dd-ci-artefacts-build-stable/datadog-agent/package_size" -def get_package_path(glob_pattern): +class PackageSize: + def __init__(self, arch, flavor, os_name, threshold): + self.arch = arch + self.flavor = flavor + self.os = os_name + self.size = 0 + self.ancestor_size = 0 + self.diff = 0 + self.threshold = threshold + self.emoji = "✅" + + @property + def name(self): + return f"{self.flavor}-{self.arch}-{self.os}" + + def arch_name(self): + if self.arch in ["x86_64", "amd64"]: + return "amd" + return "arm" + + def ko(self): + return self.diff > self.threshold + + def path(self): + if self.os == 'suse': + dir = os.environ['OMNIBUS_PACKAGE_DIR_SUSE'] + return f'{dir}/{self.flavor}-7*{self.arch}.rpm' + else: + dir = os.environ['OMNIBUS_PACKAGE_DIR'] + separator = '_' if self.os == 'deb' else '-' + return f'{dir}/{self.flavor}{separator}7*{self.arch}.{self.os}' + + def compare(self, size, ancestor_size): + self.size = size + self.ancestor_size = ancestor_size + self.diff = self.size - self.ancestor_size + if self.ko(): + self.emoji = "❌" + elif self.diff > 0: + self.emoji = "⚠️" + + @staticmethod + def mb(value): + return f"{value / 1000000:.2f}MB" + + def log(self): + return f"{self.emoji} - {self.name} size {self.mb(self.size)}: {self.mb(self.diff)} diff[{self.diff}] with previous {self.mb(self.ancestor_size)} (max: {self.mb(self.threshold)})" + + def markdown(self): + elements = ( + self.name, + self.mb(self.diff), + self.emoji, + self.mb(self.size), + self.mb(self.ancestor_size), + self.mb(self.threshold), + ) + return f'|{"|".join(map(str, elements))}|' + + +def find_package(glob_pattern): package_paths = glob.glob(glob_pattern) if len(package_paths) > 1: raise Exit(code=1, message=color_message(f"Too many files matching {glob_pattern}: {package_paths}", "red")) @@ -103,4 +164,4 @@ def display_message(ctx, ancestor, rows, decision): ## Decision {decision} """ - pr_commenter(ctx, title="Package size comparison", body=message) + pr_commenter(ctx, title="Uncompressed package size comparison", body=message) diff --git a/tasks/package.py b/tasks/package.py index 307624be2f2f2..f2d49130152dc 100644 --- a/tasks/package.py +++ b/tasks/package.py @@ -14,9 +14,10 @@ compute_package_size_metrics, ) from tasks.libs.package.utils import ( + PackageSize, display_message, + find_package, get_ancestor, - get_package_path, list_packages, retrieve_package_sizes, upload_package_sizes, @@ -33,26 +34,29 @@ def check_size(ctx, filename: str = 'package_sizes.json', dry_run: bool = False) if ancestor in package_sizes: # The test already ran on this commit return - package_sizes[ancestor] = PACKAGE_SIZE_TEMPLATE + package_sizes[ancestor] = PACKAGE_SIZE_TEMPLATE.copy() package_sizes[ancestor]['timestamp'] = int(datetime.now().timestamp()) # Check size of packages print( color_message(f"Checking package sizes from {os.environ['CI_COMMIT_REF_NAME']} against {ancestor}", Color.BLUE) ) - size_table = "" + size_table = [] for package_info in list_packages(PACKAGE_SIZE_TEMPLATE): - size_table += f"{compare(ctx, package_sizes, ancestor, *package_info)}\n" + pkg_size = PackageSize(*package_info) + size_table.append(compare(ctx, package_sizes, ancestor, pkg_size)) if on_main: upload_package_sizes(ctx, package_sizes, filename, distant=not dry_run) else: - if "❌" in size_table: + size_table.sort(key=lambda x: (-x.diff, x.flavor, x.arch_name())) + size_message = "".join(f"{pkg_size.markdown()}\n" for pkg_size in size_table) + if "❌" in size_message: decision = "❌ Failed" - elif "⚠️" in size_table: + elif "⚠️" in size_message: decision = "⚠️ Warning" else: decision = "✅ Passed" - display_message(ctx, ancestor, size_table, decision) + display_message(ctx, ancestor, size_message, decision) if "Failed" in decision: raise Exit(code=1) @@ -62,11 +66,11 @@ def compare_size(ctx, new_package, stable_package, package_type, last_stable, th mb = 1000000 if package_type.endswith('deb'): - new_package_size = _get_deb_uncompressed_size(ctx, get_package_path(new_package)) - stable_package_size = _get_deb_uncompressed_size(ctx, get_package_path(stable_package)) + new_package_size = _get_deb_uncompressed_size(ctx, find_package(new_package)) + stable_package_size = _get_deb_uncompressed_size(ctx, find_package(stable_package)) else: - new_package_size = _get_rpm_uncompressed_size(ctx, get_package_path(new_package)) - stable_package_size = _get_rpm_uncompressed_size(ctx, get_package_path(stable_package)) + new_package_size = _get_rpm_uncompressed_size(ctx, find_package(new_package)) + stable_package_size = _get_rpm_uncompressed_size(ctx, find_package(stable_package)) threshold = int(threshold) diff --git a/tasks/unit_tests/package_lib_tests.py b/tasks/unit_tests/package_lib_tests.py index 57816701fd07c..984e4cc4f871c 100644 --- a/tasks/unit_tests/package_lib_tests.py +++ b/tasks/unit_tests/package_lib_tests.py @@ -6,13 +6,12 @@ from invoke import MockContext, Result from tasks.libs.package.size import ( - PACKAGE_SIZE_TEMPLATE, SCANNED_BINARIES, _get_uncompressed_size, compare, compute_package_size_metrics, ) -from tasks.libs.package.utils import get_ancestor, list_packages +from tasks.libs.package.utils import PackageSize, get_ancestor, list_packages class TestProduceSizeStats(unittest.TestCase): @@ -167,6 +166,28 @@ def test_get_suse_uncompressed_size(self): self.assertEqual(_get_uncompressed_size(c, flavor, 'suse'), 69) +class TestPackageSizeMethods(unittest.TestCase): + def test_markdown_row(self): + size = PackageSize("amd64", "datadog-agent", "deb", 70000000) + size.compare(67000000, 68000000) + self.assertEqual("|datadog-agent-amd64-deb|-1.00MB|✅|67.00MB|68.00MB|70.00MB|", size.markdown()) + + @patch.dict('os.environ', {'OMNIBUS_PACKAGE_DIR': 'root'}) + def test_path_deb(self): + size = PackageSize("amd64", "datadog-agent", "deb", 70000000) + self.assertEqual("root/datadog-agent_7*amd64.deb", size.path()) + + @patch.dict('os.environ', {'OMNIBUS_PACKAGE_DIR': 'root'}) + def test_path_rpm(self): + size = PackageSize("x86_64", "datadog-agent", "rpm", 70000000) + self.assertEqual("root/datadog-agent-7*x86_64.rpm", size.path()) + + @patch.dict('os.environ', {'OMNIBUS_PACKAGE_DIR_SUSE': 'rout'}) + def test_path_suse(self): + size = PackageSize("x86_64", "datadog-agent", "suse", 70000000) + self.assertEqual("rout/datadog-agent-7*x86_64.rpm", size.path()) + + class TestCompare(unittest.TestCase): package_sizes = {} pkg_root = 'tasks/unit_tests/testdata/packages' @@ -181,6 +202,7 @@ def setUp(self) -> None: @patch('builtins.print') def test_on_main(self, mock_print): flavor, arch, os_name = 'datadog-heroku-agent', 'amd64', 'deb' + s = PackageSize(arch, flavor, os_name, 2001) c = MockContext( run={ 'git merge-base HEAD origin/main': Result('12345'), @@ -189,9 +211,9 @@ def test_on_main(self, mock_print): ), } ) - self.package_sizes['12345'] = PACKAGE_SIZE_TEMPLATE + self.package_sizes['12345'] = {arch: {flavor: {os_name: 70000000}}} self.assertEqual(self.package_sizes['12345'][arch][flavor][os_name], 70000000) - res = compare(c, self.package_sizes, '12345', arch, flavor, os_name, 2001) + res = compare(c, self.package_sizes, '12345', s) self.assertIsNone(res) self.assertEqual(self.package_sizes['12345'][arch][flavor][os_name], 43008) mock_print.assert_not_called() @@ -203,16 +225,17 @@ def test_on_main(self, mock_print): @patch('builtins.print') def test_on_branch_warning(self, mock_print): flavor, arch, os_name = 'datadog-agent', 'aarch64', 'suse' + s = PackageSize(arch, flavor, os_name, 70000000) c = MockContext( run={ 'git merge-base HEAD origin/main': Result('25'), f"rpm -qip {self.pkg_root}/{flavor}-7.{arch}.rpm | grep Size | cut -d : -f 2 | xargs": Result(69000000), } ) - res = compare(c, self.package_sizes, '25', arch, flavor, os_name, 70000000) - self.assertEqual(res, "|datadog-agent-aarch64-suse|1.00MB|⚠️|69.00MB|68.00MB|70.00MB|") + res = compare(c, self.package_sizes, '25', s) + self.assertEqual(res.markdown(), "|datadog-agent-aarch64-suse|1.00MB|⚠️|69.00MB|68.00MB|70.00MB|") mock_print.assert_called_with( - f"{flavor}-{arch}-{os_name} size 69.00MB is OK: 1.00MB diff with previous 68.00MB (max: 70.00MB)" + f"⚠️ - {flavor}-{arch}-{os_name} size 69.00MB: 1.00MB diff[1000000] with previous 68.00MB (max: 70.00MB)" ) @patch.dict( @@ -221,6 +244,7 @@ def test_on_branch_warning(self, mock_print): @patch('builtins.print') def test_on_branch_ok_rpm(self, mock_print): flavor, arch, os_name = 'datadog-iot-agent', 'x86_64', 'rpm' + s = PackageSize(arch, flavor, os_name, 70000000) c = MockContext( run={ 'git merge-base HEAD origin/main': Result('25'), @@ -229,10 +253,10 @@ def test_on_branch_ok_rpm(self, mock_print): ), } ) - res = compare(c, self.package_sizes, '25', arch, flavor, os_name, 70000000) - self.assertEqual(res, "|datadog-iot-agent-x86_64-rpm|-9.00MB|✅|69.00MB|78.00MB|70.00MB|") + res = compare(c, self.package_sizes, '25', s) + self.assertEqual(res.markdown(), "|datadog-iot-agent-x86_64-rpm|-9.00MB|✅|69.00MB|78.00MB|70.00MB|") mock_print.assert_called_with( - f"{flavor}-{arch}-{os_name} size 69.00MB is OK: -9.00MB diff with previous 78.00MB (max: 70.00MB)" + f"✅ - {flavor}-{arch}-{os_name} size 69.00MB: -9.00MB diff[-9000000] with previous 78.00MB (max: 70.00MB)" ) @patch.dict( @@ -242,6 +266,7 @@ def test_on_branch_ok_rpm(self, mock_print): @patch('builtins.print') def test_on_branch_ko(self, mock_print): flavor, arch, os_name = 'datadog-agent', 'aarch64', 'suse' + s = PackageSize(arch, flavor, os_name, 70000000) c = MockContext( run={ 'git merge-base HEAD origin/main': Result('25'), @@ -250,9 +275,9 @@ def test_on_branch_ko(self, mock_print): ), } ) - res = compare(c, self.package_sizes, '25', arch, flavor, os_name, 70000000) - self.assertEqual(res, "|datadog-agent-aarch64-suse|71.00MB|❌|139.00MB|68.00MB|70.00MB|") + res = compare(c, self.package_sizes, '25', s) + self.assertEqual(res.markdown(), "|datadog-agent-aarch64-suse|71.00MB|❌|139.00MB|68.00MB|70.00MB|") mock_print.assert_called_with( - "\x1b[91mdatadog-agent-aarch64-suse size 139.00MB is too large: 71.00MB diff with previous 68.00MB (max: 70.00MB)\x1b[0m", + "\x1b[91m❌ - datadog-agent-aarch64-suse size 139.00MB: 71.00MB diff[71000000] with previous 68.00MB (max: 70.00MB)\x1b[0m", file=sys.stderr, ) diff --git a/tasks/unit_tests/package_tests.py b/tasks/unit_tests/package_tests.py index b1698695b8e5e..1c7e0c47dc249 100644 --- a/tasks/unit_tests/package_tests.py +++ b/tasks/unit_tests/package_tests.py @@ -16,19 +16,25 @@ class TestCheckSize(unittest.TestCase): 'CI_COMMIT_REF_NAME': 'pikachu', }, ) - @patch('tasks.libs.package.size.get_package_path', new=MagicMock(return_value='datadog-agent')) - @patch('tasks.package.display_message', new=MagicMock()) - def test_dev_branch_ko(self): + @patch('tasks.libs.package.size.find_package', new=MagicMock(return_value='datadog-agent')) + @patch('tasks.package.display_message') + def test_dev_branch_ko(self, display_mock): flavor = 'datadog-agent' c = MockContext( run={ 'git merge-base HEAD origin/main': Result('25'), f"dpkg-deb --info {flavor} | grep Installed-Size | cut -d : -f 2 | xargs": Result(42), - f"rpm -qip {flavor} | grep Size | cut -d : -f 2 | xargs": Result(69000000), + f"rpm -qip {flavor} | grep Size | cut -d : -f 2 | xargs": Result(141000000), } ) with self.assertRaises(Exit): check_size(c, filename='tasks/unit_tests/testdata/package_sizes_real.json', dry_run=True) + display_mock.assert_called_with( + c, + '12345', + '|datadog-dogstatsd-x86_64-rpm|131.00MB|❌|141.00MB|10.00MB|10.00MB|\n|datadog-dogstatsd-x86_64-suse|131.00MB|❌|141.00MB|10.00MB|10.00MB|\n|datadog-iot-agent-x86_64-rpm|131.00MB|❌|141.00MB|10.00MB|10.00MB|\n|datadog-iot-agent-x86_64-suse|131.00MB|❌|141.00MB|10.00MB|10.00MB|\n|datadog-iot-agent-aarch64-rpm|131.00MB|❌|141.00MB|10.00MB|10.00MB|\n|datadog-agent-x86_64-rpm|1.00MB|⚠️|141.00MB|140.00MB|140.00MB|\n|datadog-agent-x86_64-suse|1.00MB|⚠️|141.00MB|140.00MB|140.00MB|\n|datadog-agent-aarch64-rpm|1.00MB|⚠️|141.00MB|140.00MB|140.00MB|\n|datadog-dogstatsd-amd64-deb|-9.96MB|✅|0.04MB|10.00MB|10.00MB|\n|datadog-dogstatsd-arm64-deb|-9.96MB|✅|0.04MB|10.00MB|10.00MB|\n|datadog-iot-agent-amd64-deb|-9.96MB|✅|0.04MB|10.00MB|10.00MB|\n|datadog-iot-agent-arm64-deb|-9.96MB|✅|0.04MB|10.00MB|10.00MB|\n|datadog-heroku-agent-amd64-deb|-69.96MB|✅|0.04MB|70.00MB|70.00MB|\n|datadog-agent-amd64-deb|-139.96MB|✅|0.04MB|140.00MB|140.00MB|\n|datadog-agent-arm64-deb|-139.96MB|✅|0.04MB|140.00MB|140.00MB|\n', + '❌ Failed', + ) @patch('builtins.print') @patch.dict( @@ -39,7 +45,7 @@ def test_dev_branch_ko(self): 'CI_COMMIT_REF_NAME': 'pikachu', }, ) - @patch('tasks.libs.package.size.get_package_path', new=MagicMock(return_value='datadog-agent')) + @patch('tasks.libs.package.size.find_package', new=MagicMock(return_value='datadog-agent')) @patch('tasks.package.display_message', new=MagicMock()) @patch('tasks.package.upload_package_sizes') def test_dev_branch_ok(self, upload_mock, print_mock): @@ -64,7 +70,7 @@ def test_dev_branch_ok(self, upload_mock, print_mock): 'CI_COMMIT_REF_NAME': 'main', }, ) - @patch('tasks.libs.package.size.get_package_path', new=MagicMock(return_value='datadog-agent')) + @patch('tasks.libs.package.size.find_package', new=MagicMock(return_value='datadog-agent')) @patch('tasks.package.display_message', new=MagicMock()) def test_main_branch_ok(self): flavor = 'datadog-agent' From f95df913d2b76227a296ed9df61d52a54c86e867 Mon Sep 17 00:00:00 2001 From: Spencer Gilbert Date: Fri, 13 Dec 2024 11:54:07 -0500 Subject: [PATCH 02/78] Revert "feat(notify): Add conductor scheduled pipelines to the notification rules" (#32163) --- .gitlab/notify/notify.yml | 19 +++- tasks/libs/notify/utils.py | 28 ------ tasks/notify.py | 12 +-- tasks/unit_tests/libs/notify/alerts_tests.py | 31 +------ tasks/unit_tests/notify_tests.py | 96 ++------------------ 5 files changed, 30 insertions(+), 156 deletions(-) diff --git a/.gitlab/notify/notify.yml b/.gitlab/notify/notify.yml index 82f996576e57c..f40187465a50f 100644 --- a/.gitlab/notify/notify.yml +++ b/.gitlab/notify/notify.yml @@ -28,8 +28,23 @@ notify: - GITLAB_TOKEN=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $GITLAB_TOKEN read_api) || exit $?; export GITLAB_TOKEN - DD_API_KEY=$($CI_PROJECT_DIR/tools/ci/fetch_secret.sh $AGENT_API_KEY_ORG2 token) || exit $?; export DD_API_KEY - python3 -m pip install -r requirements.txt -r tasks/libs/requirements-notifications.txt - - invoke -e notify.send-message - - invoke -e notify.check-consistent-failures + - | + # Do not send notifications if this is a child pipeline of another repo + # The triggering repo should already have its own notification system + if [ "$CI_PIPELINE_SOURCE" != "pipeline" ]; then + if [ "$DEPLOY_AGENT" = "true" ]; then + invoke -e notify.send-message --notification-type "deploy" + elif [ "$CI_PIPELINE_SOURCE" != "push" ]; then + invoke -e notify.send-message --notification-type "trigger" + else + invoke -e notify.send-message --notification-type "merge" + fi + if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then + invoke notify.check-consistent-failures + fi + else + echo "This pipeline was triggered by another repository, skipping notification." + fi send_pipeline_stats: stage: notify diff --git a/tasks/libs/notify/utils.py b/tasks/libs/notify/utils.py index d1d803f1b2d64..c0305184ee81d 100644 --- a/tasks/libs/notify/utils.py +++ b/tasks/libs/notify/utils.py @@ -1,6 +1,5 @@ from __future__ import annotations -import os import re from typing import Any from urllib.parse import quote @@ -44,30 +43,3 @@ def get_ci_visibility_job_url( extra_args = ''.join([f'&{key}={value}' for key, value in extra_args.items()]) return CI_VISIBILITY_JOB_URL.format(name=name, extra_flags=extra_flags, extra_args=extra_args) - - -def should_notify(): - """ - Check if the pipeline should notify the channel: only for non-downstream pipelines, unless conductor triggered it - """ - from tasks.libs.ciproviders.gitlab_api import get_pipeline - - CONDUCTOR_ID = 8278 - pipeline = get_pipeline(PROJECT_NAME, os.environ['CI_PIPELINE_ID']) - return ( - os.environ['CI_PIPELINE_SOURCE'] != 'pipeline' - or os.environ['CI_PIPELINE_SOURCE'] == 'pipeline' - and pipeline.user['id'] == CONDUCTOR_ID - ) - - -def notification_type(): - """ - Return the type of notification to send (related to the type of pipeline, amongst 'deploy', 'trigger' and 'merge') - """ - if os.environ['DEPLOY_AGENT'] == 'true': - return 'deploy' - elif os.environ['CI_PIPELINE_SOURCE'] != 'push': - return 'trigger' - else: - return 'merge' diff --git a/tasks/notify.py b/tasks/notify.py index f4f9da27c1ce4..7cdd97f12b170 100644 --- a/tasks/notify.py +++ b/tasks/notify.py @@ -20,7 +20,7 @@ from tasks.libs.common.utils import gitlab_section from tasks.libs.notify import alerts, failure_summary, pipeline_status from tasks.libs.notify.jira_failing_tests import close_issue, get_failing_tests_names, get_jira -from tasks.libs.notify.utils import PROJECT_NAME, notification_type, should_notify +from tasks.libs.notify.utils import PROJECT_NAME from tasks.libs.pipeline.notifications import ( check_for_missing_owners_slack_and_jira, ) @@ -41,16 +41,13 @@ def check_teams(_): @task -def send_message(ctx: Context, dry_run: bool = False): +def send_message(ctx: Context, notification_type: str = "merge", dry_run: bool = False): """ Send notifications for the current pipeline. CI-only task. Use the --dry-run option to test this locally, without sending real slack messages. """ - if should_notify(): - pipeline_status.send_message(ctx, notification_type(), dry_run) - else: - print("This pipeline is a non-conductor downstream pipeline, skipping notifications") + pipeline_status.send_message(ctx, notification_type, dry_run) @task @@ -90,9 +87,6 @@ def check_consistent_failures(ctx, job_failures_file="job_executions.v2.json"): # The jobs dictionary contains the consecutive and cumulative failures for each job # The consecutive failures are reset to 0 when the job is not failing, and are raising an alert when reaching the CONSECUTIVE_THRESHOLD (3) # The cumulative failures list contains 1 for failures, 0 for succes. They contain only then CUMULATIVE_LENGTH(10) last executions and raise alert when 50% failure rate is reached - if not should_notify() or os.environ['CI_COMMIT_BRANCH'] != os.environ['CI_DEFAULT_BRANCH']: - print("Consistent failures check is only run on the not-downstream default branch") - return job_executions = alerts.retrieve_job_executions(ctx, job_failures_file) diff --git a/tasks/unit_tests/libs/notify/alerts_tests.py b/tasks/unit_tests/libs/notify/alerts_tests.py index d1a532c40c417..ebade3240213c 100644 --- a/tasks/unit_tests/libs/notify/alerts_tests.py +++ b/tasks/unit_tests/libs/notify/alerts_tests.py @@ -30,17 +30,10 @@ def test_job_executions(path="tasks/unit_tests/testdata/job_executions.json"): class TestCheckConsistentFailures(unittest.TestCase): - @patch.dict( - 'os.environ', - { - 'CI_PIPELINE_ID': '456', - 'CI_PIPELINE_SOURCE': 'push', - 'CI_COMMIT_BRANCH': 'taylor-swift', - 'CI_DEFAULT_BRANCH': 'taylor-swift', - }, - ) @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') def test_nominal(self, api_mock): + os.environ["CI_PIPELINE_ID"] = "456" + repo_mock = api_mock.return_value.projects.get.return_value trace_mock = repo_mock.jobs.get.return_value.trace list_mock = repo_mock.pipelines.get.return_value.jobs.list @@ -54,29 +47,9 @@ def test_nominal(self, api_mock): path, ) - repo_mock.jobs.get.assert_called() trace_mock.assert_called() list_mock.assert_called() - @patch.dict( - 'os.environ', - { - 'CI_PIPELINE_ID': '456', - 'CI_PIPELINE_SOURCE': 'push', - 'CI_COMMIT_BRANCH': 'taylor', - 'CI_DEFAULT_BRANCH': 'swift', - }, - ) - @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - def test_dismiss(self, api_mock): - repo_mock = api_mock.return_value.projects.get.return_value - with test_job_executions() as path: - notify.check_consistent_failures( - MockContext(run=Result("test")), - path, - ) - repo_mock.jobs.get.assert_not_called() - class TestAlertsRetrieveJobExecutionsCreated(unittest.TestCase): job_executions = None diff --git a/tasks/unit_tests/notify_tests.py b/tasks/unit_tests/notify_tests.py index 87c93d579c77d..6e7ddc70abf0a 100644 --- a/tasks/unit_tests/notify_tests.py +++ b/tasks/unit_tests/notify_tests.py @@ -31,29 +31,22 @@ def get_github_slack_map(): class TestSendMessage(unittest.TestCase): - @patch.dict('os.environ', {'DEPLOY_AGENT': 'false', 'CI_PIPELINE_SOURCE': 'push', 'CI_PIPELINE_ID': '42'}) - @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - @patch('builtins.print') @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - def test_merge(self, api_mock, print_mock): + @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) + def test_merge(self, api_mock): repo_mock = api_mock.return_value.projects.get.return_value repo_mock.jobs.get.return_value.artifact.return_value = b"{}" repo_mock.jobs.get.return_value.trace.return_value = b"Log trace" repo_mock.pipelines.get.return_value.ref = "test" list_mock = repo_mock.pipelines.get.return_value.jobs.list list_mock.side_effect = [get_fake_jobs(), []] - notify.send_message(MockContext(), dry_run=True) + notify.send_message(MockContext(), notification_type="merge", dry_run=True) list_mock.assert_called() - repo_mock.pipelines.get.assert_called_with('42') - self.assertTrue("merge" in print_mock.mock_calls[0].args[0]) - repo_mock.jobs.get.assert_called() - @patch.dict('os.environ', {'DEPLOY_AGENT': 'false', 'CI_PIPELINE_SOURCE': 'push', 'CI_PIPELINE_ID': '42'}) @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') @patch('tasks.libs.notify.pipeline_status.get_failed_jobs') - @patch('builtins.print') @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - def test_merge_without_get_failed_call(self, print_mock, get_failed_jobs_mock, api_mock): + def test_merge_without_get_failed_call(self, get_failed_jobs_mock, api_mock): repo_mock = api_mock.return_value.projects.get.return_value repo_mock.jobs.get.return_value.artifact.return_value = b"{}" repo_mock.jobs.get.return_value.trace.return_value = b"Log trace" @@ -121,10 +114,9 @@ def test_merge_without_get_failed_call(self, print_mock, get_failed_jobs_mock, a ) ) get_failed_jobs_mock.return_value = failed - notify.send_message(MockContext(), dry_run=True) - self.assertTrue("merge" in print_mock.mock_calls[0].args[0]) + notify.send_message(MockContext(), notification_type="merge", dry_run=True) + get_failed_jobs_mock.assert_called() - repo_mock.jobs.get.assert_called() @patch("tasks.libs.owners.parsing.read_owners") def test_route_e2e_internal_error(self, read_owners_mock): @@ -199,51 +191,9 @@ def test_route_e2e_internal_error(self, read_owners_mock): self.assertNotIn("@DataDog/agent-devx-loops", owners) self.assertNotIn("@DataDog/agent-delivery", owners) - @patch.dict('os.environ', {'DEPLOY_AGENT': 'false', 'CI_PIPELINE_SOURCE': 'push', 'CI_PIPELINE_ID': '42'}) - @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - @patch('builtins.print') - @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - def test_merge_with_get_failed_call(self, print_mock, api_mock): - repo_mock = api_mock.return_value.projects.get.return_value - trace_mock = repo_mock.jobs.get.return_value.trace - list_mock = repo_mock.pipelines.get.return_value.jobs.list - - trace_mock.return_value = b"no basic auth credentials" - list_mock.return_value = get_fake_jobs() - repo_mock.jobs.get.return_value.artifact.return_value = b"{}" - repo_mock.pipelines.get.return_value.ref = "test" - - notify.send_message(MockContext(), dry_run=True) - self.assertTrue("merge" in print_mock.mock_calls[0].args[0]) - trace_mock.assert_called() - list_mock.assert_called() - repo_mock.jobs.get.assert_called() - - @patch.dict('os.environ', {'DEPLOY_AGENT': 'true', 'CI_PIPELINE_SOURCE': 'push', 'CI_PIPELINE_ID': '42'}) - @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - @patch('builtins.print') - @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - def test_deploy_with_get_failed_call(self, print_mock, api_mock): - repo_mock = api_mock.return_value.projects.get.return_value - trace_mock = repo_mock.jobs.get.return_value.trace - list_mock = repo_mock.pipelines.get.return_value.jobs.list - - trace_mock.return_value = b"no basic auth credentials" - list_mock.return_value = get_fake_jobs() - repo_mock.jobs.get.return_value.artifact.return_value = b"{}" - repo_mock.pipelines.get.return_value.ref = "test" - - notify.send_message(MockContext(), dry_run=True) - self.assertTrue("rocket" in print_mock.mock_calls[0].args[0]) - trace_mock.assert_called() - list_mock.assert_called() - repo_mock.jobs.get.assert_called() - - @patch.dict('os.environ', {'DEPLOY_AGENT': 'false', 'CI_PIPELINE_SOURCE': 'api', 'CI_PIPELINE_ID': '42'}) @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - @patch('builtins.print') @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - def test_trigger_with_get_failed_call(self, print_mock, api_mock): + def test_merge_with_get_failed_call(self, api_mock): repo_mock = api_mock.return_value.projects.get.return_value trace_mock = repo_mock.jobs.get.return_value.trace list_mock = repo_mock.pipelines.get.return_value.jobs.list @@ -253,40 +203,10 @@ def test_trigger_with_get_failed_call(self, print_mock, api_mock): repo_mock.jobs.get.return_value.artifact.return_value = b"{}" repo_mock.pipelines.get.return_value.ref = "test" - notify.send_message(MockContext(), dry_run=True) - self.assertTrue("arrow_forward" in print_mock.mock_calls[0].args[0]) - trace_mock.assert_called() - list_mock.assert_called() - repo_mock.jobs.get.assert_called() - - @patch.dict('os.environ', {'DEPLOY_AGENT': 'false', 'CI_PIPELINE_SOURCE': 'pipeline', 'CI_PIPELINE_ID': '42'}) - @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - @patch('builtins.print') - @patch('tasks.libs.pipeline.notifications.get_pr_from_commit', new=MagicMock(return_value="")) - def test_trigger_with_get_failed_call_conductor(self, print_mock, api_mock): - repo_mock = api_mock.return_value.projects.get.return_value - trace_mock = repo_mock.jobs.get.return_value.trace - list_mock = repo_mock.pipelines.get.return_value.jobs.list - - trace_mock.return_value = b"no basic auth credentials" - list_mock.return_value = get_fake_jobs() - repo_mock.jobs.get.return_value.artifact.return_value = b"{}" - repo_mock.pipelines.get.return_value.ref = "test" - repo_mock.pipelines.get.return_value.user.__getitem__.return_value = 8278 + notify.send_message(MockContext(), notification_type="merge", dry_run=True) - notify.send_message(MockContext(), dry_run=True) - self.assertTrue("arrow_forward" in print_mock.mock_calls[0].args[0]) trace_mock.assert_called() list_mock.assert_called() - repo_mock.jobs.get.assert_called() - - @patch.dict('os.environ', {'CI_PIPELINE_SOURCE': 'pipeline', 'CI_PIPELINE_ID': '42'}) - @patch('tasks.libs.ciproviders.gitlab_api.get_gitlab_api') - def test_dismiss_notification(self, api_mock): - repo_mock = api_mock.return_value.projects.get.return_value - - notify.send_message(MockContext(), dry_run=True) - repo_mock.jobs.get.assert_not_called() def test_post_to_channel1(self): self.assertFalse(pipeline_status.should_send_message_to_author("main", default_branch="main")) From 8bb0066b115e128f75fcb699c0b8778712e10abc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 17:39:33 +0000 Subject: [PATCH 03/78] Bump docker/setup-buildx-action from 3.6.1 to 3.7.1 (#29913) Co-authored-by: chouetz --- .github/workflows/serverless-binary-size.yml | 2 +- .github/workflows/serverless-integration.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/serverless-binary-size.yml b/.github/workflows/serverless-binary-size.yml index 5bc272591f0e6..5bf7a69b36177 100644 --- a/.github/workflows/serverless-binary-size.yml +++ b/.github/workflows/serverless-binary-size.yml @@ -43,7 +43,7 @@ jobs: persist-credentials: false - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: Previous binary size and dependencies id: previous diff --git a/.github/workflows/serverless-integration.yml b/.github/workflows/serverless-integration.yml index de78371e895e1..f94ed5ca56b52 100644 --- a/.github/workflows/serverless-integration.yml +++ b/.github/workflows/serverless-integration.yml @@ -54,7 +54,7 @@ jobs: platforms: amd64,arm64 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@988b5a0280414f521da01fcc63a27aeeb4b104db # v3.6.1 + uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3.7.1 - name: Create raw logs directory id: rawlogs From c50e6cfb5894f25cc6c18f0fdd1b982fbc93d77d Mon Sep 17 00:00:00 2001 From: sabrina lu Date: Fri, 13 Dec 2024 13:24:16 -0500 Subject: [PATCH 04/78] Revert "[snmp] Add agent_group tag for snmp integration" (#32164) --- comp/haagent/helpers/helpers.go | 26 ---------- comp/haagent/helpers/helpers_test.go | 33 ------------ comp/haagent/impl/config.go | 5 +- comp/metadata/host/hostimpl/hosttags/tags.go | 5 +- .../snmp/internal/devicecheck/devicecheck.go | 17 ++----- .../internal/devicecheck/devicecheck_test.go | 51 +++---------------- .../snmp/internal/discovery/discovery.go | 7 +-- .../snmp/internal/discovery/discovery_test.go | 13 +++-- pkg/collector/corechecks/snmp/snmp.go | 15 ++---- pkg/collector/corechecks/snmp/snmp_test.go | 13 +++-- pkg/commonchecks/corechecks.go | 2 +- 11 files changed, 35 insertions(+), 152 deletions(-) delete mode 100644 comp/haagent/helpers/helpers.go delete mode 100644 comp/haagent/helpers/helpers_test.go diff --git a/comp/haagent/helpers/helpers.go b/comp/haagent/helpers/helpers.go deleted file mode 100644 index 4b5e755e04936..0000000000000 --- a/comp/haagent/helpers/helpers.go +++ /dev/null @@ -1,26 +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 haagenthelpers provides helpers for haagent component -package haagenthelpers - -import ( - "github.com/DataDog/datadog-agent/pkg/config/model" -) - -// IsEnabled returns true if HA Agent is enabled -func IsEnabled(agentConfig model.Reader) bool { - return agentConfig.GetBool("ha_agent.enabled") -} - -// GetGroup returns HA Agent group -func GetGroup(agentConfig model.Reader) string { - return agentConfig.GetString("ha_agent.group") -} - -// GetHaAgentTags returns HA Agent related tags -func GetHaAgentTags(agentConfig model.Reader) []string { - return []string{"agent_group:" + GetGroup(agentConfig)} -} diff --git a/comp/haagent/helpers/helpers_test.go b/comp/haagent/helpers/helpers_test.go deleted file mode 100644 index 987ab6a5cccf4..0000000000000 --- a/comp/haagent/helpers/helpers_test.go +++ /dev/null @@ -1,33 +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 haagenthelpers - -import ( - "testing" - - "github.com/DataDog/datadog-agent/comp/core/config" - "github.com/stretchr/testify/assert" -) - -func TestIsEnabled(t *testing.T) { - cfg := config.NewMock(t) - assert.False(t, IsEnabled(cfg)) - - cfg.SetWithoutSource("ha_agent.enabled", true) - assert.True(t, IsEnabled(cfg)) -} - -func TestGetGroup(t *testing.T) { - cfg := config.NewMock(t) - cfg.SetWithoutSource("ha_agent.group", "my-group") - assert.Equal(t, "my-group", GetGroup(cfg)) -} - -func TestGetHaAgentTags(t *testing.T) { - cfg := config.NewMock(t) - cfg.SetWithoutSource("ha_agent.group", "my-group") - assert.Equal(t, []string{"agent_group:my-group"}, GetHaAgentTags(cfg)) -} diff --git a/comp/haagent/impl/config.go b/comp/haagent/impl/config.go index ea9f54d9f16ea..2a6c4e20a8d12 100644 --- a/comp/haagent/impl/config.go +++ b/comp/haagent/impl/config.go @@ -7,7 +7,6 @@ package haagentimpl import ( "github.com/DataDog/datadog-agent/comp/core/config" - helpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" ) // validHaIntegrations represent the list of integrations that will be considered as @@ -31,7 +30,7 @@ type haAgentConfigs struct { func newHaAgentConfigs(agentConfig config.Component) *haAgentConfigs { return &haAgentConfigs{ - enabled: helpers.IsEnabled(agentConfig), - group: helpers.GetGroup(agentConfig), + enabled: agentConfig.GetBool("ha_agent.enabled"), + group: agentConfig.GetString("ha_agent.group"), } } diff --git a/comp/metadata/host/hostimpl/hosttags/tags.go b/comp/metadata/host/hostimpl/hosttags/tags.go index 92d610d49e079..01cfff7c0810f 100644 --- a/comp/metadata/host/hostimpl/hosttags/tags.go +++ b/comp/metadata/host/hostimpl/hosttags/tags.go @@ -12,7 +12,6 @@ import ( "strings" "time" - haagenthelpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" "github.com/DataDog/datadog-agent/pkg/config/env" "github.com/DataDog/datadog-agent/pkg/config/model" configUtils "github.com/DataDog/datadog-agent/pkg/config/utils" @@ -134,8 +133,8 @@ func Get(ctx context.Context, cached bool, conf model.Reader) *Tags { hostTags = appendToHostTags(hostTags, clusterNameTags) } - if haagenthelpers.IsEnabled(conf) { - hostTags = appendToHostTags(hostTags, haagenthelpers.GetHaAgentTags(conf)) + if conf.GetBool("ha_agent.enabled") { + hostTags = appendToHostTags(hostTags, []string{"agent_group:" + conf.GetString("ha_agent.group")}) } gceTags := []string{} diff --git a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go index d23d5642a2470..969fd45d2da52 100644 --- a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go +++ b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go @@ -17,9 +17,8 @@ import ( "go.uber.org/atomic" - "github.com/DataDog/datadog-agent/comp/core/config" - haagenthelpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" "github.com/DataDog/datadog-agent/pkg/collector/externalhost" + pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" configUtils "github.com/DataDog/datadog-agent/pkg/config/utils" "github.com/DataDog/datadog-agent/pkg/metrics/servicecheck" "github.com/DataDog/datadog-agent/pkg/util/hostname/validate" @@ -67,13 +66,12 @@ type DeviceCheck struct { diagnoses *diagnoses.Diagnoses interfaceBandwidthState report.InterfaceBandwidthState cacheKey string - agentConfig config.Component } const cacheKeyPrefix = "snmp-tags" // NewDeviceCheck returns a new DeviceCheck -func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFactory session.Factory, agentConfig config.Component) (*DeviceCheck, error) { +func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFactory session.Factory) (*DeviceCheck, error) { newConfig := config.CopyWithNewIP(ipAddress) var devicePinger pinger.Pinger @@ -96,7 +94,6 @@ func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFa diagnoses: diagnoses.NewDeviceDiagnoses(newConfig.DeviceID), interfaceBandwidthState: report.MakeInterfaceBandwidthState(), cacheKey: cacheKey, - agentConfig: agentConfig, } d.readTagsFromCache() @@ -247,19 +244,11 @@ func (d *DeviceCheck) setDeviceHostExternalTags() { if deviceHostname == "" || err != nil { return } - agentTags := d.buildExternalTags() + agentTags := configUtils.GetConfiguredTags(pkgconfigsetup.Datadog(), false) log.Debugf("Set external tags for device host, host=`%s`, agentTags=`%v`", deviceHostname, agentTags) externalhost.SetExternalTags(deviceHostname, common.SnmpExternalTagsSourceType, agentTags) } -func (d *DeviceCheck) buildExternalTags() []string { - agentTags := configUtils.GetConfiguredTags(d.agentConfig, false) - if haagenthelpers.IsEnabled(d.agentConfig) { - agentTags = append(agentTags, haagenthelpers.GetHaAgentTags(d.agentConfig)...) - } - return agentTags -} - func (d *DeviceCheck) getValuesAndTags() (bool, []string, *valuestore.ResultValueStore, error) { var deviceReachable bool var checkErrors []string diff --git a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go index 1e7ad7fc7a001..c56235171d3a2 100644 --- a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go +++ b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go @@ -17,7 +17,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" - agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/metrics/servicecheck" "github.com/DataDog/datadog-agent/pkg/version" @@ -58,7 +57,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -198,7 +197,7 @@ global_metrics: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -247,7 +246,7 @@ community_string: public config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -278,7 +277,7 @@ community_string: public config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession) assert.Nil(t, err) hostname, err := deviceCk.GetDeviceHostname() @@ -344,7 +343,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) snmpTags := []string{"snmp_device:1.2.3.4", "device_ip:1.2.3.4", "device_id:default:1.2.3.4", "snmp_profile:f5-big-ip", "device_vendor:f5", "snmp_host:foo_sys_name", @@ -649,7 +648,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -696,7 +695,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) // override pinger with mock pinger @@ -847,7 +846,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) assert.Nil(t, err) // override pinger with mock pinger @@ -968,37 +967,3 @@ profiles: sender.AssertNotCalled(t, "Gauge", pingAvgRttMetric, mock.Anything, mock.Anything, mock.Anything) sender.AssertNotCalled(t, "Gauge", pingPacketLoss, mock.Anything, mock.Anything, mock.Anything) } - -func TestDeviceCheck_buildExternalTags(t *testing.T) { - // GIVEN - profile.SetConfdPathAndCleanProfiles() - sess := session.CreateFakeSession() - sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { - return sess, nil - } - - // language=yaml - rawInstanceConfig := []byte(` -ip_address: 1.2.3.4 -community_string: public -collect_topology: false -`) - // language=yaml - rawInitConfig := []byte(``) - - config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) - assert.Nil(t, err) - - cfg := agentconfig.NewMock(t) - cfg.SetWithoutSource("ha_agent.enabled", true) - cfg.SetWithoutSource("ha_agent.group", "my-group") - - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, cfg) - assert.Nil(t, err) - - // WHEN - externalTags := deviceCk.buildExternalTags() - - // THEN - assert.Equal(t, []string{"agent_group:my-group"}, externalTags) -} diff --git a/pkg/collector/corechecks/snmp/internal/discovery/discovery.go b/pkg/collector/corechecks/snmp/internal/discovery/discovery.go index 860f3cb781991..b7fb91915209a 100644 --- a/pkg/collector/corechecks/snmp/internal/discovery/discovery.go +++ b/pkg/collector/corechecks/snmp/internal/discovery/discovery.go @@ -14,7 +14,6 @@ import ( "sync" "time" - "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/persistentcache" "github.com/DataDog/datadog-agent/pkg/util/log" "go.uber.org/atomic" @@ -44,7 +43,6 @@ type Discovery struct { discoveredDevices map[checkconfig.DeviceDigest]Device sessionFactory session.Factory - agentConfig config.Component } // Device implements and store results from the Service interface for the SNMP listener @@ -239,7 +237,7 @@ func (d *Discovery) getDevicesFound() []string { } func (d *Discovery) createDevice(deviceDigest checkconfig.DeviceDigest, subnet *snmpSubnet, deviceIP string, writeCache bool) { - deviceCk, err := devicecheck.NewDeviceCheck(subnet.config, deviceIP, d.sessionFactory, d.agentConfig) + deviceCk, err := devicecheck.NewDeviceCheck(subnet.config, deviceIP, d.sessionFactory) if err != nil { // should not happen since the deviceCheck is expected to be valid at this point // and are only changing the device ip @@ -337,12 +335,11 @@ func (d *Discovery) writeCache(subnet *snmpSubnet) { } // NewDiscovery return a new Discovery instance -func NewDiscovery(config *checkconfig.CheckConfig, sessionFactory session.Factory, agentConfig config.Component) *Discovery { +func NewDiscovery(config *checkconfig.CheckConfig, sessionFactory session.Factory) *Discovery { return &Discovery{ discoveredDevices: make(map[checkconfig.DeviceDigest]Device), stop: make(chan struct{}), config: config, sessionFactory: sessionFactory, - agentConfig: agentConfig, } } diff --git a/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go b/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go index d2ee7611c0597..9d59def5890d0 100644 --- a/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go +++ b/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go @@ -15,7 +15,6 @@ import ( "github.com/gosnmp/gosnmp" "github.com/stretchr/testify/assert" - agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp/internal/checkconfig" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp/internal/session" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" @@ -60,7 +59,7 @@ func TestDiscovery(t *testing.T) { DiscoveryWorkers: 1, IgnoredIPAddresses: map[string]bool{"192.168.0.5": true}, } - discovery := NewDiscovery(checkConfig, sessionFactory, agentconfig.NewMock(t)) + discovery := NewDiscovery(checkConfig, sessionFactory) discovery.Start() assert.NoError(t, waitForDiscoveredDevices(discovery, 7, 2*time.Second)) discovery.Stop() @@ -110,7 +109,7 @@ func TestDiscoveryCache(t *testing.T) { DiscoveryInterval: 3600, DiscoveryWorkers: 1, } - discovery := NewDiscovery(checkConfig, sessionFactory, agentconfig.NewMock(t)) + discovery := NewDiscovery(checkConfig, sessionFactory) discovery.Start() assert.NoError(t, waitForDiscoveredDevices(discovery, 4, 2*time.Second)) discovery.Stop() @@ -142,7 +141,7 @@ func TestDiscoveryCache(t *testing.T) { DiscoveryInterval: 3600, DiscoveryWorkers: 0, // no workers, the devices will be loaded from cache } - discovery2 := NewDiscovery(checkConfig, sessionFactory, agentconfig.NewMock(t)) + discovery2 := NewDiscovery(checkConfig, sessionFactory) discovery2.Start() assert.NoError(t, waitForDiscoveredDevices(discovery2, 4, 2*time.Second)) discovery2.Stop() @@ -181,7 +180,7 @@ func TestDiscoveryTicker(t *testing.T) { DiscoveryInterval: 1, DiscoveryWorkers: 1, } - discovery := NewDiscovery(checkConfig, sessionFactory, agentconfig.NewMock(t)) + discovery := NewDiscovery(checkConfig, sessionFactory) discovery.Start() time.Sleep(1500 * time.Millisecond) discovery.Stop() @@ -228,7 +227,7 @@ func TestDiscovery_checkDevice(t *testing.T) { } var sess *session.MockSession - discovery := NewDiscovery(checkConfig, session.NewMockSession, agentconfig.NewMock(t)) + discovery := NewDiscovery(checkConfig, session.NewMockSession) checkDeviceOnce := func() { sess = session.CreateMockSession() @@ -316,7 +315,7 @@ func TestDiscovery_createDevice(t *testing.T) { DiscoveryAllowedFailures: 3, Namespace: "default", } - discovery := NewDiscovery(checkConfig, session.NewMockSession, agentconfig.NewMock(t)) + discovery := NewDiscovery(checkConfig, session.NewMockSession) ipAddr, ipNet, err := net.ParseCIDR(checkConfig.Network) assert.Nil(t, err) startingIP := ipAddr.Mask(ipNet.Mask) diff --git a/pkg/collector/corechecks/snmp/snmp.go b/pkg/collector/corechecks/snmp/snmp.go index 29e2ae2327c5b..6b59932b057c6 100644 --- a/pkg/collector/corechecks/snmp/snmp.go +++ b/pkg/collector/corechecks/snmp/snmp.go @@ -11,7 +11,6 @@ import ( "sync" "time" - "github.com/DataDog/datadog-agent/comp/core/config" "go.uber.org/atomic" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" @@ -45,7 +44,6 @@ type Check struct { discovery *discovery.Discovery sessionFactory session.Factory workerRunDeviceCheckErrors *atomic.Uint64 - agentConfig config.Component } // Run executes the check @@ -157,10 +155,10 @@ func (c *Check) Configure(senderManager sender.SenderManager, integrationConfigD } if c.config.IsDiscovery() { - c.discovery = discovery.NewDiscovery(c.config, c.sessionFactory, c.agentConfig) + c.discovery = discovery.NewDiscovery(c.config, c.sessionFactory) c.discovery.Start() } else { - c.singleDeviceCk, err = devicecheck.NewDeviceCheck(c.config, c.config.IPAddress, c.sessionFactory, c.agentConfig) + c.singleDeviceCk, err = devicecheck.NewDeviceCheck(c.config, c.config.IPAddress, c.sessionFactory) if err != nil { return fmt.Errorf("failed to create device check: %s", err) } @@ -198,17 +196,14 @@ func (c *Check) GetDiagnoses() ([]diagnosis.Diagnosis, error) { } // Factory creates a new check factory -func Factory(agentConfig config.Component) optional.Option[func() check.Check] { - return optional.NewOption(func() check.Check { - return newCheck(agentConfig) - }) +func Factory() optional.Option[func() check.Check] { + return optional.NewOption(newCheck) } -func newCheck(agentConfig config.Component) check.Check { +func newCheck() check.Check { return &Check{ CheckBase: core.NewCheckBase(common.SnmpIntegrationName), sessionFactory: session.NewGosnmpSession, workerRunDeviceCheckErrors: atomic.NewUint64(0), - agentConfig: agentConfig, } } diff --git a/pkg/collector/corechecks/snmp/snmp_test.go b/pkg/collector/corechecks/snmp/snmp_test.go index 2f1095dc52027..88513ca8ce0d3 100644 --- a/pkg/collector/corechecks/snmp/snmp_test.go +++ b/pkg/collector/corechecks/snmp/snmp_test.go @@ -25,7 +25,6 @@ import ( "github.com/DataDog/datadog-agent/comp/aggregator/demultiplexer/demultiplexerimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" - agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/collector/externalhost" @@ -997,10 +996,10 @@ community_string: public func TestCheckID(t *testing.T) { profile.SetConfdPathAndCleanProfiles() - check1 := newCheck(agentconfig.NewMock(t)) - check2 := newCheck(agentconfig.NewMock(t)) - check3 := newCheck(agentconfig.NewMock(t)) - checkSubnet := newCheck(agentconfig.NewMock(t)) + check1 := newCheck() + check2 := newCheck() + check3 := newCheck() + checkSubnet := newCheck() // language=yaml rawInstanceConfig1 := []byte(` ip_address: 1.1.1.1 @@ -2166,7 +2165,7 @@ func TestDeviceIDAsHostname(t *testing.T) { sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { return sess, nil } - chk := Check{sessionFactory: sessionFactory, agentConfig: agentconfig.NewMock(t)} + chk := Check{sessionFactory: sessionFactory} pkgconfigsetup.Datadog().SetWithoutSource("hostname", "test-hostname") pkgconfigsetup.Datadog().SetWithoutSource("tags", []string{"agent_tag1:val1", "agent_tag2:val2"}) senderManager := deps.Demultiplexer @@ -2359,7 +2358,7 @@ func TestDiscoveryDeviceIDAsHostname(t *testing.T) { sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { return sess, nil } - chk := Check{sessionFactory: sessionFactory, agentConfig: agentconfig.NewMock(t)} + chk := Check{sessionFactory: sessionFactory} pkgconfigsetup.Datadog().SetWithoutSource("hostname", "my-hostname") senderManager := deps.Demultiplexer diff --git a/pkg/commonchecks/corechecks.go b/pkg/commonchecks/corechecks.go index cb20f9412ab2a..9af1e7f9833c8 100644 --- a/pkg/commonchecks/corechecks.go +++ b/pkg/commonchecks/corechecks.go @@ -62,7 +62,7 @@ func RegisterChecks(store workloadmeta.Component, tagger tagger.Component, cfg c corecheckLoader.RegisterCheck(uptime.CheckName, uptime.Factory()) corecheckLoader.RegisterCheck(telemetryCheck.CheckName, telemetryCheck.Factory(telemetry)) corecheckLoader.RegisterCheck(ntp.CheckName, ntp.Factory()) - corecheckLoader.RegisterCheck(snmp.CheckName, snmp.Factory(cfg)) + corecheckLoader.RegisterCheck(snmp.CheckName, snmp.Factory()) corecheckLoader.RegisterCheck(networkpath.CheckName, networkpath.Factory(telemetry)) corecheckLoader.RegisterCheck(io.CheckName, io.Factory()) corecheckLoader.RegisterCheck(filehandles.CheckName, filehandles.Factory()) From 0f53c9a45030daca82ab159c340c36ec51890a88 Mon Sep 17 00:00:00 2001 From: Arthur Bellal Date: Fri, 13 Dec 2024 20:38:34 +0100 Subject: [PATCH 05/78] (fleet) basic logging in setup scripts (#32133) --- pkg/fleet/installer/setup/common/output.go | 18 +++++++++ pkg/fleet/installer/setup/common/setup.go | 43 ++++++++++++++++++---- pkg/fleet/installer/setup/setup.go | 26 +++++++------ 3 files changed, 69 insertions(+), 18 deletions(-) create mode 100644 pkg/fleet/installer/setup/common/output.go diff --git a/pkg/fleet/installer/setup/common/output.go b/pkg/fleet/installer/setup/common/output.go new file mode 100644 index 0000000000000..cc42f5e4c8d02 --- /dev/null +++ b/pkg/fleet/installer/setup/common/output.go @@ -0,0 +1,18 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +package common + +import "io" + +// Output is a writer for the output. It will support some ANSI escape sequences to format the output. +type Output struct { + tty io.Writer +} + +// WriteString writes a string to the output. +func (o *Output) WriteString(s string) { + _, _ = o.tty.Write([]byte(s)) +} diff --git a/pkg/fleet/installer/setup/common/setup.go b/pkg/fleet/installer/setup/common/setup.go index 159b1b69b0d36..7abab785021f6 100644 --- a/pkg/fleet/installer/setup/common/setup.go +++ b/pkg/fleet/installer/setup/common/setup.go @@ -10,11 +10,14 @@ import ( "context" "errors" "fmt" + "io" "os" + "time" "github.com/DataDog/datadog-agent/pkg/fleet/installer" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/oci" + "github.com/DataDog/datadog-agent/pkg/version" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) @@ -32,7 +35,10 @@ var ( type Setup struct { configDir string installer installer.Installer + start time.Time + flavor string + Out *Output Env *env.Env Ctx context.Context Span ddtrace.Span @@ -41,7 +47,13 @@ type Setup struct { } // NewSetup creates a new Setup structure with some default values. -func NewSetup(ctx context.Context, env *env.Env, name string) (*Setup, error) { +func NewSetup(ctx context.Context, env *env.Env, flavor string, flavorPath string, logOutput io.Writer) (*Setup, error) { + header := `Datadog Installer %s - https://www.datadoghq.com +Running the %s installation script (https://github.com/DataDog/datadog-agent/tree/%s/pkg/fleet/installer/setup/%s) - %s +` + start := time.Now() + output := &Output{tty: logOutput} + output.WriteString(fmt.Sprintf(header, version.AgentVersion, flavor, version.Commit, flavorPath, start.Format(time.RFC3339))) if env.APIKey == "" { return nil, ErrNoAPIKey } @@ -49,10 +61,13 @@ func NewSetup(ctx context.Context, env *env.Env, name string) (*Setup, error) { if err != nil { return nil, fmt.Errorf("failed to create installer: %w", err) } - span, ctx := tracer.StartSpanFromContext(ctx, fmt.Sprintf("setup.%s", name)) + span, ctx := tracer.StartSpanFromContext(ctx, fmt.Sprintf("setup.%s", flavor)) s := &Setup{ configDir: configDir, installer: installer, + start: start, + flavor: flavor, + Out: output, Env: env, Ctx: ctx, Span: span, @@ -75,30 +90,44 @@ func NewSetup(ctx context.Context, env *env.Env, name string) (*Setup, error) { // Run installs the packages and writes the configurations func (s *Setup) Run() (err error) { defer func() { s.Span.Finish(tracer.WithError(err)) }() + s.Out.WriteString("Applying configurations...\n") err = writeConfigs(s.Config, s.configDir) if err != nil { return fmt.Errorf("failed to write configuration: %w", err) } - err = s.installPackage(installerOCILayoutURL) + packages := resolvePackages(s.Packages) + s.Out.WriteString("The following packages will be installed:\n") + s.Out.WriteString(fmt.Sprintf(" - %s / %s\n", "datadog-installer", version.AgentVersion)) + for _, p := range packages { + s.Out.WriteString(fmt.Sprintf(" - %s / %s\n", p.name, p.version)) + } + err = s.installPackage("datadog-installer", installerOCILayoutURL) if err != nil { return fmt.Errorf("failed to install installer: %w", err) } - packages := resolvePackages(s.Packages) for _, p := range packages { url := oci.PackageURL(s.Env, p.name, p.version) - err = s.installPackage(url) + err = s.installPackage(p.name, url) if err != nil { return fmt.Errorf("failed to install package %s: %w", url, err) } } + s.Out.WriteString(fmt.Sprintf("Successfully ran the %s install script in %s!\n", s.flavor, time.Since(s.start).Round(time.Second))) return nil } // installPackage mimicks the telemetry of calling the install package command -func (s *Setup) installPackage(url string) (err error) { +func (s *Setup) installPackage(name string, url string) (err error) { span, ctx := tracer.StartSpanFromContext(s.Ctx, "install") defer func() { span.Finish(tracer.WithError(err)) }() span.SetTag("url", url) span.SetTag("_top_level", 1) - return s.installer.Install(ctx, url, nil) + + s.Out.WriteString(fmt.Sprintf("Installing %s...\n", name)) + err = s.installer.Install(ctx, url, nil) + if err != nil { + return err + } + s.Out.WriteString(fmt.Sprintf("Successfully installed %s\n", name)) + return nil } diff --git a/pkg/fleet/installer/setup/setup.go b/pkg/fleet/installer/setup/setup.go index 3b01d320c9975..93a57fc575a5b 100644 --- a/pkg/fleet/installer/setup/setup.go +++ b/pkg/fleet/installer/setup/setup.go @@ -9,6 +9,7 @@ package setup import ( "context" "fmt" + "os" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/setup/common" @@ -17,23 +18,26 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" ) -const ( - // FlavorDatabricks is the flavor for the Data Jobs Monitoring databricks setup. - FlavorDatabricks = "databricks" -) +type flavor struct { + path string // path is used to print the path to the setup script for users. + run func(*common.Setup) error +} + +var flavors = map[string]flavor{ + "databricks": {path: "djm/databricks.go", run: djm.SetupDatabricks}, +} // Setup installs Datadog. func Setup(ctx context.Context, env *env.Env, flavor string) error { - s, err := common.NewSetup(ctx, env, flavor) + f, ok := flavors[flavor] + if !ok { + return fmt.Errorf("unknown flavor %s", flavor) + } + s, err := common.NewSetup(ctx, env, flavor, f.path, os.Stdout) if err != nil { return err } - switch flavor { - case FlavorDatabricks: - err = djm.SetupDatabricks(s) - default: - return fmt.Errorf("unknown setup flavor %s", flavor) - } + err = f.run(s) if err != nil { return err } From e87eaecb8358a40207a043f80e85cc173500a992 Mon Sep 17 00:00:00 2001 From: Marco Costa Date: Fri, 13 Dec 2024 12:39:23 -0800 Subject: [PATCH 06/78] [AIDM-459] Remap OTel GraphQL resource to use query data (#31731) --- pkg/trace/api/otlp.go | 7 +++++++ pkg/trace/api/otlp_test.go | 12 +++++++++++ pkg/trace/traceutil/otel_util.go | 17 +++++++++++++++ pkg/trace/traceutil/otel_util_test.go | 21 +++++++++++++++++++ ...tel-graphql-resource-e31ae2262b52a873.yaml | 11 ++++++++++ 5 files changed, 68 insertions(+) create mode 100644 releasenotes/notes/otel-graphql-resource-e31ae2262b52a873.yaml diff --git a/pkg/trace/api/otlp.go b/pkg/trace/api/otlp.go index de53f14d369ec..88491ea9052ec 100644 --- a/pkg/trace/api/otlp.go +++ b/pkg/trace/api/otlp.go @@ -684,6 +684,13 @@ func resourceFromTags(meta map[string]string) string { return m + " " + svc } return m + } else if typ := meta[semconv117.AttributeGraphqlOperationType]; typ != "" { + // Enrich GraphQL query resource names. + // See https://github.com/open-telemetry/semantic-conventions/blob/v1.29.0/docs/graphql/graphql-spans.md + if name := meta[semconv117.AttributeGraphqlOperationName]; name != "" { + return typ + " " + name + } + return typ } return "" } diff --git a/pkg/trace/api/otlp_test.go b/pkg/trace/api/otlp_test.go index bd6377ff4612d..0966ee75ff1e9 100644 --- a/pkg/trace/api/otlp_test.go +++ b/pkg/trace/api/otlp_test.go @@ -1573,6 +1573,18 @@ func TestOTLPHelpers(t *testing.T) { meta: map[string]string{semconv.AttributeRPCMethod: "M"}, out: "M", }, + { + meta: map[string]string{"graphql.operation.name": "myQuery"}, + out: "", + }, + { + meta: map[string]string{"graphql.operation.type": "query"}, + out: "query", + }, + { + meta: map[string]string{"graphql.operation.type": "query", "graphql.operation.name": "myQuery"}, + out: "query myQuery", + }, } { assert.Equal(t, tt.out, resourceFromTags(tt.meta)) } diff --git a/pkg/trace/traceutil/otel_util.go b/pkg/trace/traceutil/otel_util.go index bb3e03783c4dc..5e645f7b6673b 100644 --- a/pkg/trace/traceutil/otel_util.go +++ b/pkg/trace/traceutil/otel_util.go @@ -195,6 +195,13 @@ func GetOTelResourceV1(span ptrace.Span, res pcommon.Resource) (resName string) // ...and service if available resName = resName + " " + svc } + } else if m := GetOTelAttrValInResAndSpanAttrs(span, res, false, semconv117.AttributeGraphqlOperationType); m != "" { + // Enrich GraphQL query resource names. + // See https://github.com/open-telemetry/semantic-conventions/blob/v1.29.0/docs/graphql/graphql-spans.md + resName = m + if name := GetOTelAttrValInResAndSpanAttrs(span, res, false, semconv117.AttributeGraphqlOperationName); name != "" { + resName = resName + " " + name + } } else { resName = span.Name() } @@ -249,6 +256,16 @@ func GetOTelResourceV2(span ptrace.Span, res pcommon.Resource) (resName string) } return } + + if m := GetOTelAttrValInResAndSpanAttrs(span, res, false, semconv117.AttributeGraphqlOperationType); m != "" { + // Enrich GraphQL query resource names. + // See https://github.com/open-telemetry/semantic-conventions/blob/v1.29.0/docs/graphql/graphql-spans.md + resName = m + if name := GetOTelAttrValInResAndSpanAttrs(span, res, false, semconv117.AttributeGraphqlOperationName); name != "" { + resName = resName + " " + name + } + return + } resName = span.Name() return diff --git a/pkg/trace/traceutil/otel_util_test.go b/pkg/trace/traceutil/otel_util_test.go index 9b6934caf3a7a..201c64a0745b8 100644 --- a/pkg/trace/traceutil/otel_util_test.go +++ b/pkg/trace/traceutil/otel_util_test.go @@ -293,6 +293,27 @@ func TestGetOTelResource(t *testing.T) { expectedV1: strings.Repeat("a", MaxResourceLen), expectedV2: strings.Repeat("a", MaxResourceLen), }, + { + name: "GraphQL with no type", + sattrs: map[string]string{"graphql.operation.name": "myQuery"}, + normalize: false, + expectedV1: "span_name", + expectedV2: "span_name", + }, + { + name: "GraphQL with only type", + sattrs: map[string]string{"graphql.operation.type": "query"}, + normalize: false, + expectedV1: "query", + expectedV2: "query", + }, + { + name: "GraphQL with only type", + sattrs: map[string]string{"graphql.operation.type": "query", "graphql.operation.name": "myQuery"}, + normalize: false, + expectedV1: "query myQuery", + expectedV2: "query myQuery", + }, } { t.Run(tt.name, func(t *testing.T) { span := ptrace.NewSpan() diff --git a/releasenotes/notes/otel-graphql-resource-e31ae2262b52a873.yaml b/releasenotes/notes/otel-graphql-resource-e31ae2262b52a873.yaml new file mode 100644 index 0000000000000..8962c355b585a --- /dev/null +++ b/releasenotes/notes/otel-graphql-resource-e31ae2262b52a873.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + For OpenTelemetry GraphQL request spans, the span resource name is now the GraphQL operation type and name. From 37fbbc826dfb608e5b4f6fb7d9058bae8d27ce22 Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Fri, 13 Dec 2024 16:59:46 -0500 Subject: [PATCH 07/78] [NPM-3662] Add sestatus to agent flare (#32068) --- cmd/system-probe/api/debug/handlers_linux.go | 41 +++++++++++++++++++ .../api/debug/handlers_nolinux.go | 20 +++++++++ cmd/system-probe/api/server.go | 2 + pkg/ebpf/debug_handlers.go | 5 +-- pkg/flare/archive_linux.go | 7 ++++ ...agent-flare-sestatus-5820cfc79ec91d1f.yaml | 11 +++++ 6 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 cmd/system-probe/api/debug/handlers_linux.go create mode 100644 cmd/system-probe/api/debug/handlers_nolinux.go create mode 100644 releasenotes/notes/agent-flare-sestatus-5820cfc79ec91d1f.yaml diff --git a/cmd/system-probe/api/debug/handlers_linux.go b/cmd/system-probe/api/debug/handlers_linux.go new file mode 100644 index 0000000000000..d2bd7dfbd5f48 --- /dev/null +++ b/cmd/system-probe/api/debug/handlers_linux.go @@ -0,0 +1,41 @@ +// 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. + +//go:build linux + +// Package debug contains handlers for debug information global to all of system-probe +package debug + +import ( + "context" + "errors" + "fmt" + "net/http" + "os/exec" + "time" +) + +// HandleSelinuxSestatus reports the output of sestatus as an http result +func HandleSelinuxSestatus(w http.ResponseWriter, r *http.Request) { + ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second) + defer cancel() + + cmd := exec.CommandContext(ctx, "sestatus") + output, err := cmd.CombinedOutput() + + var execError *exec.Error + var exitErr *exec.ExitError + + if err != nil { + // don't 500 for ExitErrors etc, to report "normal" failures to the selinux_sestatus.log file + if !errors.As(err, &execError) && !errors.As(err, &exitErr) { + w.WriteHeader(500) + } + fmt.Fprintf(w, "command failed: %s\n%s", err, output) + return + } + + w.Write(output) +} diff --git a/cmd/system-probe/api/debug/handlers_nolinux.go b/cmd/system-probe/api/debug/handlers_nolinux.go new file mode 100644 index 0000000000000..1475d821c1e6e --- /dev/null +++ b/cmd/system-probe/api/debug/handlers_nolinux.go @@ -0,0 +1,20 @@ +// 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. + +//go:build !linux + +// Package debug contains handlers for debug information global to all of system-probe +package debug + +import ( + "io" + "net/http" +) + +// HandleSelinuxSestatus is not supported +func HandleSelinuxSestatus(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(500) + io.WriteString(w, "HandleSelinuxSestatus is not supported on this platform") +} diff --git a/cmd/system-probe/api/server.go b/cmd/system-probe/api/server.go index 3e4a71056f143..d81007a0c8f0d 100644 --- a/cmd/system-probe/api/server.go +++ b/cmd/system-probe/api/server.go @@ -15,6 +15,7 @@ import ( gorilla "github.com/gorilla/mux" + "github.com/DataDog/datadog-agent/cmd/system-probe/api/debug" "github.com/DataDog/datadog-agent/cmd/system-probe/api/module" "github.com/DataDog/datadog-agent/cmd/system-probe/api/server" sysconfigtypes "github.com/DataDog/datadog-agent/cmd/system-probe/config/types" @@ -58,6 +59,7 @@ func StartServer(cfg *sysconfigtypes.Config, telemetry telemetry.Component, wmet if runtime.GOOS == "linux" { mux.HandleFunc("/debug/ebpf_btf_loader_info", ebpf.HandleBTFLoaderInfo) + mux.HandleFunc("/debug/selinux_sestatus", debug.HandleSelinuxSestatus) } go func() { diff --git a/pkg/ebpf/debug_handlers.go b/pkg/ebpf/debug_handlers.go index ea10d22a844c2..04cba9faed556 100644 --- a/pkg/ebpf/debug_handlers.go +++ b/pkg/ebpf/debug_handlers.go @@ -6,10 +6,9 @@ package ebpf import ( + "fmt" "io" "net/http" - - "github.com/DataDog/datadog-agent/pkg/util/log" ) // HandleBTFLoaderInfo responds with where the system-probe found BTF data (and @@ -17,7 +16,7 @@ import ( func HandleBTFLoaderInfo(w http.ResponseWriter, _ *http.Request) { info, err := GetBTFLoaderInfo() if err != nil { - log.Errorf("unable to get ebpf_btf_loader info: %s", err) + fmt.Fprintf(w, "unable to get ebpf_btf_loader info: %s", err) w.WriteHeader(500) return } diff --git a/pkg/flare/archive_linux.go b/pkg/flare/archive_linux.go index 1c9b5d7a4ad48..dafe8bd41d1bc 100644 --- a/pkg/flare/archive_linux.go +++ b/pkg/flare/archive_linux.go @@ -38,6 +38,7 @@ func addSystemProbePlatformSpecificEntries(fb flaretypes.FlareBuilder) { _ = fb.AddFileFromFunc(filepath.Join("system-probe", "conntrack_cached.log"), getSystemProbeConntrackCached) _ = fb.AddFileFromFunc(filepath.Join("system-probe", "conntrack_host.log"), getSystemProbeConntrackHost) _ = fb.AddFileFromFunc(filepath.Join("system-probe", "ebpf_btf_loader.log"), getSystemProbeBTFLoaderInfo) + _ = fb.AddFileFromFunc(filepath.Join("system-probe", "selinux_sestatus.log"), getSystemProbeSelinuxSestatus) } } @@ -148,3 +149,9 @@ func getSystemProbeBTFLoaderInfo() ([]byte, error) { url := sysprobeclient.DebugURL("/ebpf_btf_loader_info") return getHTTPData(sysProbeClient, url) } + +func getSystemProbeSelinuxSestatus() ([]byte, error) { + sysProbeClient := sysprobeclient.Get(getSystemProbeSocketPath()) + url := sysprobeclient.DebugURL("/selinux_sestatus") + return getHTTPData(sysProbeClient, url) +} diff --git a/releasenotes/notes/agent-flare-sestatus-5820cfc79ec91d1f.yaml b/releasenotes/notes/agent-flare-sestatus-5820cfc79ec91d1f.yaml new file mode 100644 index 0000000000000..e7bac3330c728 --- /dev/null +++ b/releasenotes/notes/agent-flare-sestatus-5820cfc79ec91d1f.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + Added the output of ``sestatus`` into the Agent flare. This information will appear in ``system-probe/selinux_sestatus.log``. From 786bce8532868cc9f21e4f45233c4c517d36bdd9 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Fri, 13 Dec 2024 17:37:35 -0500 Subject: [PATCH 08/78] Enter repo root (#32169) --- tasks/winbuildscripts/common.ps1 | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/tasks/winbuildscripts/common.ps1 b/tasks/winbuildscripts/common.ps1 index 35ef863d57535..d1e51fc5b2b1d 100644 --- a/tasks/winbuildscripts/common.ps1 +++ b/tasks/winbuildscripts/common.ps1 @@ -42,6 +42,23 @@ function Exit-BuildRoot() { Pop-Location -StackName AgentBuildRoot } +<# +.SYNOPSIS +Sets the current directory to the root of the repository. +#> +function Enter-RepoRoot() { + # Expected PSScriptRoot: datadog-agent\tasks\winbuildscripts\ + Push-Location "$PSScriptRoot\..\.." -ErrorAction Stop -StackName AgentRepoRoot | Out-Null +} + +<# +.SYNOPSIS +Leaves the repository root directory and returns to the original working directory. +#> +function Exit-RepoRoot() { + Pop-Location -StackName AgentRepoRoot +} + <# .SYNOPSIS Expands the Go module cache from an archive file. @@ -209,6 +226,8 @@ function Invoke-BuildScript { if ($BuildOutOfSource) { Enter-BuildRoot + } else { + Enter-RepoRoot } Expand-ModCache -modcache modcache @@ -234,9 +253,11 @@ function Invoke-BuildScript { # This finally block is executed regardless of whether the try block completes successfully, throws an exception, # or uses `exit` to terminate the script. + # Restore the original working directory if ($BuildOutOfSource) { - # Restore the original working directory Exit-BuildRoot + } else { + Exit-RepoRoot } } } From 179220f0b172ccdfcff02346de3102ee84476939 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Sat, 14 Dec 2024 23:05:31 +0100 Subject: [PATCH 09/78] [Fleet] Migrate ddtrace references to telem package (#32175) --- cmd/installer-downloader/main.go | 3 +- .../subcommands/installer/command.go | 6 +- .../subcommands/installer/umask_nix.go | 4 +- .../subcommands/installer/umask_windows.go | 6 +- pkg/fleet/daemon/daemon.go | 54 +++++++---------- pkg/fleet/installer/installer.go | 15 +++-- pkg/fleet/installer/oci/download.go | 4 +- pkg/fleet/installer/packages/apm_inject.go | 34 +++++------ pkg/fleet/installer/packages/apm_sockets.go | 12 ++-- pkg/fleet/installer/packages/app_armor.go | 10 ++-- pkg/fleet/installer/packages/datadog_agent.go | 27 +++++---- .../packages/datadog_agent_windows.go | 19 +++--- .../packages/datadog_installer_windows.go | 18 +++--- pkg/fleet/installer/packages/docker.go | 22 +++---- pkg/fleet/installer/packages/file.go | 6 +- pkg/fleet/installer/packages/pkg_manager.go | 6 +- pkg/fleet/installer/packages/systemd.go | 30 +++++----- pkg/fleet/installer/setup/common/setup.go | 13 ++--- .../installer/setup/djm/databricks_test.go | 4 +- pkg/fleet/internal/cdn/cdn.go | 6 +- pkg/fleet/internal/exec/installer_exec.go | 33 +++++------ pkg/fleet/telemetry/span.go | 26 +++++++++ pkg/fleet/telemetry/telemetry.go | 58 +++++++++++++------ 23 files changed, 223 insertions(+), 193 deletions(-) create mode 100644 pkg/fleet/telemetry/span.go diff --git a/cmd/installer-downloader/main.go b/cmd/installer-downloader/main.go index 5ddadc54c0d60..f7b67910ae94e 100644 --- a/cmd/installer-downloader/main.go +++ b/cmd/installer-downloader/main.go @@ -17,7 +17,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/oci" "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -49,7 +48,7 @@ func main() { defer func() { _ = t.Stop(ctx) }() var err error span, ctx := telemetry.StartSpanFromEnv(ctx, fmt.Sprintf("downloader-%s", Flavor)) - defer func() { span.Finish(tracer.WithError(err)) }() + defer func() { span.Finish(err) }() err = runDownloader(ctx, env, Version, Flavor) if err != nil { fmt.Fprintf(os.Stderr, "Installation failed: %v\n", err) diff --git a/cmd/installer/subcommands/installer/command.go b/cmd/installer/subcommands/installer/command.go index 186367fc20b42..66ec6b1c74c57 100644 --- a/cmd/installer/subcommands/installer/command.go +++ b/cmd/installer/subcommands/installer/command.go @@ -22,8 +22,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/version" "github.com/spf13/cobra" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "gopkg.in/yaml.v2" ) @@ -89,7 +87,7 @@ func UnprivilegedCommands(_ *command.GlobalParams) []*cobra.Command { type cmd struct { t *telemetry.Telemetry ctx context.Context - span ddtrace.Span + span telemetry.Span env *env.Env } @@ -107,7 +105,7 @@ func newCmd(operation string) *cmd { } func (c *cmd) Stop(err error) { - c.span.Finish(tracer.WithError(err)) + c.span.Finish(err) if c.t != nil { err := c.t.Stop(context.Background()) if err != nil { diff --git a/cmd/installer/subcommands/installer/umask_nix.go b/cmd/installer/subcommands/installer/umask_nix.go index b4062c09a4f77..1fd44c01ac405 100644 --- a/cmd/installer/subcommands/installer/umask_nix.go +++ b/cmd/installer/subcommands/installer/umask_nix.go @@ -10,11 +10,11 @@ package installer import ( "syscall" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" ) // setInstallerUmask sets umask 0 to override any inherited umask -func setInstallerUmask(span ddtrace.Span) { +func setInstallerUmask(span telemetry.Span) { oldmask := syscall.Umask(0) span.SetTag("inherited_umask", oldmask) } diff --git a/cmd/installer/subcommands/installer/umask_windows.go b/cmd/installer/subcommands/installer/umask_windows.go index 3d308c67cc318..1b076f92bc389 100644 --- a/cmd/installer/subcommands/installer/umask_windows.go +++ b/cmd/installer/subcommands/installer/umask_windows.go @@ -7,9 +7,7 @@ package installer -import ( - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" -) +import "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" // setInstallerUmask no-op on Windows -func setInstallerUmask(_ ddtrace.Span) {} +func setInstallerUmask(_ telemetry.Span) {} diff --git a/pkg/fleet/daemon/daemon.go b/pkg/fleet/daemon/daemon.go index 7c5d7401a1388..63ed3e2a3a626 100644 --- a/pkg/fleet/daemon/daemon.go +++ b/pkg/fleet/daemon/daemon.go @@ -20,9 +20,6 @@ import ( "sync" "time" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" - "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/config/remote/client" "github.com/DataDog/datadog-agent/pkg/config/utils" @@ -34,6 +31,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/internal/cdn" "github.com/DataDog/datadog-agent/pkg/fleet/internal/exec" "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/version" @@ -271,8 +269,8 @@ func (d *daemonImpl) Install(ctx context.Context, url string, args []string) err } func (d *daemonImpl) install(ctx context.Context, url string, args []string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "install") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "install") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -293,8 +291,8 @@ func (d *daemonImpl) StartExperiment(ctx context.Context, url string) error { } func (d *daemonImpl) startExperiment(ctx context.Context, url string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "start_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "start_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -308,8 +306,8 @@ func (d *daemonImpl) startExperiment(ctx context.Context, url string) (err error } func (d *daemonImpl) startInstallerExperiment(ctx context.Context, url string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "start_installer_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "start_installer_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -334,8 +332,8 @@ func (d *daemonImpl) PromoteExperiment(ctx context.Context, pkg string) error { } func (d *daemonImpl) promoteExperiment(ctx context.Context, pkg string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "promote_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "promote_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -356,8 +354,8 @@ func (d *daemonImpl) StopExperiment(ctx context.Context, pkg string) error { } func (d *daemonImpl) stopExperiment(ctx context.Context, pkg string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "stop_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "stop_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -378,8 +376,8 @@ func (d *daemonImpl) StartConfigExperiment(ctx context.Context, url string, vers } func (d *daemonImpl) startConfigExperiment(ctx context.Context, url string, version string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "start_config_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "start_config_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -400,8 +398,8 @@ func (d *daemonImpl) PromoteConfigExperiment(ctx context.Context, pkg string) er } func (d *daemonImpl) promoteConfigExperiment(ctx context.Context, pkg string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "promote_config_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "promote_config_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -422,8 +420,8 @@ func (d *daemonImpl) StopConfigExperiment(ctx context.Context, pkg string) error } func (d *daemonImpl) stopConfigExperiment(ctx context.Context, pkg string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "stop_config_experiment") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "stop_config_experiment") + defer func() { span.Finish(err) }() d.refreshState(ctx) defer d.refreshState(ctx) @@ -455,7 +453,7 @@ func (d *daemonImpl) handleRemoteAPIRequest(request remoteAPIRequest) (err error defer d.m.Unlock() defer d.requestsWG.Done() parentSpan, ctx := newRequestContext(request) - defer parentSpan.Finish(tracer.WithError(err)) + defer parentSpan.Finish(err) d.refreshState(ctx) defer d.refreshState(ctx) @@ -545,25 +543,13 @@ type requestState struct { ErrorCode installerErrors.InstallerErrorCode } -func newRequestContext(request remoteAPIRequest) (ddtrace.Span, context.Context) { +func newRequestContext(request remoteAPIRequest) (telemetry.Span, context.Context) { ctx := context.WithValue(context.Background(), requestStateKey, &requestState{ Package: request.Package, ID: request.ID, State: pbgo.TaskState_RUNNING, }) - - ctxCarrier := tracer.TextMapCarrier{ - tracer.DefaultTraceIDHeader: request.TraceID, - tracer.DefaultParentIDHeader: request.ParentSpanID, - tracer.DefaultPriorityHeader: "2", - } - spanCtx, err := tracer.Extract(ctxCarrier) - if err != nil { - log.Debugf("failed to extract span context from install script params: %v", err) - return tracer.StartSpan("remote_request"), ctx - } - - return tracer.StartSpanFromContext(ctx, "remote_request", tracer.ChildOf(spanCtx)) + return telemetry.StartSpanFromIDs(ctx, "remote_request", request.TraceID, request.ParentSpanID) } func setRequestInvalid(ctx context.Context) { diff --git a/pkg/fleet/installer/installer.go b/pkg/fleet/installer/installer.go index e43e0cfaae4d2..3dd39b8fddb81 100644 --- a/pkg/fleet/installer/installer.go +++ b/pkg/fleet/installer/installer.go @@ -19,6 +19,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/internal/cdn" "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" installerErrors "github.com/DataDog/datadog-agent/pkg/fleet/installer/errors" @@ -28,8 +29,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/internal/db" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/version" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -164,9 +163,9 @@ func (i *installerImpl) Install(ctx context.Context, url string, args []string) if err != nil { return fmt.Errorf("could not download package: %w", err) } - span, ok := tracer.SpanFromContext(ctx) + span, ok := telemetry.SpanFromContext(ctx) if ok { - span.SetTag(ext.ResourceName, pkg.Name) + span.SetResourceName(pkg.Name) span.SetTag("package_version", pkg.Version) } err = i.preparePackage(ctx, pkg.Name, args) // Preinst @@ -471,9 +470,9 @@ func (i *installerImpl) Purge(ctx context.Context) { } // remove all from disk - span, _ := tracer.StartSpanFromContext(ctx, "remove_all") + span, _ := telemetry.StartSpanFromContext(ctx, "remove_all") err = os.RemoveAll(i.packagesDir) - defer span.Finish(tracer.WithError(err)) + defer span.Finish(err) if err != nil { log.Warnf("could not delete packages dir: %v", err) } @@ -659,8 +658,8 @@ func (i *installerImpl) configurePackage(ctx context.Context, pkg string) (err e return nil } - span, _ := tracer.StartSpanFromContext(ctx, "configure_package") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "configure_package") + defer func() { span.Finish(err) }() switch pkg { case packageDatadogAgent, packageAPMInjector: diff --git a/pkg/fleet/installer/oci/download.go b/pkg/fleet/installer/oci/download.go index 1ddff40e37238..9260d19bc0e97 100644 --- a/pkg/fleet/installer/oci/download.go +++ b/pkg/fleet/installer/oci/download.go @@ -31,11 +31,11 @@ import ( "github.com/google/go-containerregistry/pkg/v1/types" "go.uber.org/multierr" "golang.org/x/net/http2" - httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" installerErrors "github.com/DataDog/datadog-agent/pkg/fleet/installer/errors" "github.com/DataDog/datadog-agent/pkg/fleet/installer/tar" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" ) @@ -241,7 +241,7 @@ func getRefAndKeychain(env *env.Env, url string) urlWithKeychain { // If they are specified, the registry and authentication overrides are applied first. // Then we try each registry in the list of default registries in order and return the first successful download. func (d *Downloader) downloadRegistry(ctx context.Context, url string) (oci.Image, error) { - transport := httptrace.WrapRoundTripper(d.client.Transport) + transport := telemetry.WrapRoundTripper(d.client.Transport) var err error if d.env.Mirror != "" { transport, err = newMirrorTransport(transport, d.env.Mirror) diff --git a/pkg/fleet/installer/packages/apm_inject.go b/pkg/fleet/installer/packages/apm_inject.go index 3f51793cf3486..0066671494204 100644 --- a/pkg/fleet/installer/packages/apm_inject.go +++ b/pkg/fleet/installer/packages/apm_inject.go @@ -19,9 +19,9 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/packages/embedded" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" "go.uber.org/multierr" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -32,8 +32,8 @@ const ( // SetupAPMInjector sets up the injector at bootstrap func SetupAPMInjector(ctx context.Context) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "setup_injector") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "setup_injector") + defer func() { span.Finish(err) }() installer := newAPMInjectorInstaller(injectorPath) defer func() { installer.Finish(err) }() return installer.Setup(ctx) @@ -41,8 +41,8 @@ func SetupAPMInjector(ctx context.Context) (err error) { // RemoveAPMInjector removes the APM injector func RemoveAPMInjector(ctx context.Context) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "remove_injector") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "remove_injector") + defer func() { span.Finish(err) }() installer := newAPMInjectorInstaller(injectorPath) defer func() { installer.Finish(err) }() return installer.Remove(ctx) @@ -50,8 +50,8 @@ func RemoveAPMInjector(ctx context.Context) (err error) { // InstrumentAPMInjector instruments the APM injector func InstrumentAPMInjector(ctx context.Context, method string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "instrument_injector") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "instrument_injector") + defer func() { span.Finish(err) }() installer := newAPMInjectorInstaller(injectorPath) installer.envs.InstallScript.APMInstrumentationEnabled = method defer func() { installer.Finish(err) }() @@ -60,8 +60,8 @@ func InstrumentAPMInjector(ctx context.Context, method string) (err error) { // UninstrumentAPMInjector uninstruments the APM injector func UninstrumentAPMInjector(ctx context.Context, method string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "uninstrument_injector") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "uninstrument_injector") + defer func() { span.Finish(err) }() installer := newAPMInjectorInstaller(injectorPath) installer.envs.InstallScript.APMInstrumentationEnabled = method defer func() { installer.Finish(err) }() @@ -148,8 +148,8 @@ func (a *apmInjectorInstaller) Setup(ctx context.Context) error { } func (a *apmInjectorInstaller) Remove(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "remove_injector") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "remove_injector") + defer func() { span.Finish(err) }() err = a.removeInstrumentScripts(ctx) if err != nil { @@ -277,8 +277,8 @@ func (a *apmInjectorInstaller) deleteLDPreloadConfigContent(_ context.Context, l } func (a *apmInjectorInstaller) verifySharedLib(ctx context.Context, libPath string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "verify_shared_lib") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "verify_shared_lib") + defer func() { span.Finish(err) }() echoPath, err := exec.LookPath("echo") if err != nil { // If echo is not found, to not block install, @@ -302,8 +302,8 @@ func (a *apmInjectorInstaller) verifySharedLib(ctx context.Context, libPath stri // - Referenced in our public documentation, so we override them to use installer commands for consistency // - Used on deb/rpm removal and may break the OCI in the process func (a *apmInjectorInstaller) addInstrumentScripts(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "add_instrument_scripts") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "add_instrument_scripts") + defer func() { span.Finish(err) }() hostMutator := newFileMutator( "/usr/bin/dd-host-install", @@ -370,8 +370,8 @@ func (a *apmInjectorInstaller) addInstrumentScripts(ctx context.Context) (err er // removeInstrumentScripts removes the install scripts that come with the APM injector // if and only if they've been installed by the installer and not modified func (a *apmInjectorInstaller) removeInstrumentScripts(ctx context.Context) (retErr error) { - span, _ := tracer.StartSpanFromContext(ctx, "remove_instrument_scripts") - defer func() { span.Finish(tracer.WithError(retErr)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "remove_instrument_scripts") + defer func() { span.Finish(retErr) }() for _, script := range []string{"dd-host-install", "dd-container-install", "dd-cleanup"} { path := filepath.Join("/usr/bin", script) diff --git a/pkg/fleet/installer/packages/apm_sockets.go b/pkg/fleet/installer/packages/apm_sockets.go index db2def19fe43f..4c2e866116557 100644 --- a/pkg/fleet/installer/packages/apm_sockets.go +++ b/pkg/fleet/installer/packages/apm_sockets.go @@ -15,8 +15,8 @@ import ( "path/filepath" "strings" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "gopkg.in/yaml.v2" ) @@ -119,9 +119,9 @@ func (a *apmInjectorInstaller) configureSocketsEnv(ctx context.Context) (retErr } // setSocketEnvs sets the socket environment variables -func setSocketEnvs(ctx context.Context, envFile []byte) ([]byte, error) { - span, _ := tracer.StartSpanFromContext(ctx, "set_socket_envs") - defer span.Finish() +func setSocketEnvs(ctx context.Context, envFile []byte) (res []byte, err error) { + span, _ := telemetry.StartSpanFromContext(ctx, "set_socket_envs") + defer span.Finish(err) apmSocket, statsdSocket, err := getSocketsPath() if err != nil { @@ -169,8 +169,8 @@ func addEnvsIfNotSet(envs map[string]string, envFile []byte) ([]byte, error) { // // Reloading systemd & restarting the unit has to be done separately by the caller func addSystemDEnvOverrides(ctx context.Context, unit string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "add_systemd_env_overrides") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "add_systemd_env_overrides") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) // The - is important as it lets the unit start even if the file is missing. diff --git a/pkg/fleet/installer/packages/app_armor.go b/pkg/fleet/installer/packages/app_armor.go index bd0ea2889802e..054a319fe4139 100644 --- a/pkg/fleet/installer/packages/app_armor.go +++ b/pkg/fleet/installer/packages/app_armor.go @@ -15,8 +15,8 @@ import ( "path/filepath" "strings" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -33,8 +33,8 @@ func setupAppArmor(ctx context.Context) (err error) { // no-op if apparmor is not installed return nil } - span, _ := tracer.StartSpanFromContext(ctx, "setup_app_armor") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "setup_app_armor") + defer func() { span.Finish(err) }() if err = os.MkdirAll(appArmorConfigPath, 0755); err != nil { return fmt.Errorf("failed to create %s: %w", appArmorConfigPath, err) } @@ -61,8 +61,8 @@ func removeAppArmor(ctx context.Context) (err error) { } return err } - span, _ := tracer.StartSpanFromContext(ctx, "remove_app_armor") - defer span.Finish(tracer.WithError(err)) + span, _ := telemetry.StartSpanFromContext(ctx, "remove_app_armor") + defer span.Finish(err) if err = os.Remove(datadogProfilePath); err != nil { return err } diff --git a/pkg/fleet/installer/packages/datadog_agent.go b/pkg/fleet/installer/packages/datadog_agent.go index b364045328f57..34dbac394050f 100644 --- a/pkg/fleet/installer/packages/datadog_agent.go +++ b/pkg/fleet/installer/packages/datadog_agent.go @@ -16,9 +16,9 @@ import ( "path/filepath" "strings" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/installinfo" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -72,8 +72,8 @@ var ( // PrepareAgent prepares the machine to install the agent func PrepareAgent(ctx context.Context) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "prepare_agent") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "prepare_agent") + defer func() { span.Finish(err) }() // Check if the agent has been installed by a package manager, if yes remove it if !oldAgentInstalled() { @@ -88,13 +88,13 @@ func PrepareAgent(ctx context.Context) (err error) { // SetupAgent installs and starts the agent func SetupAgent(ctx context.Context, _ []string) (err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "setup_agent") + span, ctx := telemetry.StartSpanFromContext(ctx, "setup_agent") defer func() { if err != nil { log.Errorf("Failed to setup agent, reverting: %s", err) err = errors.Join(err, RemoveAgent(ctx)) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() for _, unit := range stableUnits { @@ -159,38 +159,45 @@ func SetupAgent(ctx context.Context, _ []string) (err error) { // RemoveAgent stops and removes the agent func RemoveAgent(ctx context.Context) error { - span, ctx := tracer.StartSpanFromContext(ctx, "remove_agent_units") - defer span.Finish() + span, ctx := telemetry.StartSpanFromContext(ctx, "remove_agent_units") + var spanErr error + defer func() { span.Finish(spanErr) }() // stop experiments, they can restart stable agent for _, unit := range experimentalUnits { if err := stopUnit(ctx, unit); err != nil { log.Warnf("Failed to stop %s: %s", unit, err) + spanErr = err } } // stop stable agents for _, unit := range stableUnits { if err := stopUnit(ctx, unit); err != nil { log.Warnf("Failed to stop %s: %s", unit, err) + spanErr = err } } if err := disableUnit(ctx, agentUnit); err != nil { log.Warnf("Failed to disable %s: %s", agentUnit, err) + spanErr = err } // remove units from disk for _, unit := range experimentalUnits { if err := removeUnit(ctx, unit); err != nil { log.Warnf("Failed to remove %s: %s", unit, err) + spanErr = err } } for _, unit := range stableUnits { if err := removeUnit(ctx, unit); err != nil { log.Warnf("Failed to remove %s: %s", unit, err) + spanErr = err } } if err := os.Remove(agentSymlink); err != nil { log.Warnf("Failed to remove agent symlink: %s", err) + spanErr = err } installinfo.RmInstallInfo() // TODO: Return error to caller? @@ -202,12 +209,12 @@ func oldAgentInstalled() bool { return err == nil } -func stopOldAgentUnits(ctx context.Context) error { +func stopOldAgentUnits(ctx context.Context) (err error) { if !oldAgentInstalled() { return nil } - span, ctx := tracer.StartSpanFromContext(ctx, "remove_old_agent_units") - defer span.Finish() + span, ctx := telemetry.StartSpanFromContext(ctx, "remove_old_agent_units") + defer span.Finish(err) for _, unit := range stableUnits { if err := stopUnit(ctx, unit); err != nil { return fmt.Errorf("failed to stop %s: %v", unit, err) diff --git a/pkg/fleet/installer/packages/datadog_agent_windows.go b/pkg/fleet/installer/packages/datadog_agent_windows.go index d330ab434d0e0..69935fdee41d8 100644 --- a/pkg/fleet/installer/packages/datadog_agent_windows.go +++ b/pkg/fleet/installer/packages/datadog_agent_windows.go @@ -12,9 +12,8 @@ import ( "fmt" "github.com/DataDog/datadog-agent/pkg/fleet/internal/winregistry" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -28,12 +27,12 @@ func PrepareAgent(_ context.Context) error { // SetupAgent installs and starts the agent func SetupAgent(ctx context.Context, args []string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "setup_agent") + span, _ := telemetry.StartSpanFromContext(ctx, "setup_agent") defer func() { if err != nil { log.Errorf("Failed to setup agent: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() // Make sure there are no Agent already installed _ = removeAgentIfInstalled(ctx) @@ -43,12 +42,12 @@ func SetupAgent(ctx context.Context, args []string) (err error) { // StartAgentExperiment starts the agent experiment func StartAgentExperiment(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "start_experiment") + span, _ := telemetry.StartSpanFromContext(ctx, "start_experiment") defer func() { if err != nil { log.Errorf("Failed to start agent experiment: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() err = removeAgentIfInstalled(ctx) @@ -66,12 +65,12 @@ func StartAgentExperiment(ctx context.Context) (err error) { // StopAgentExperiment stops the agent experiment, i.e. removes/uninstalls it. func StopAgentExperiment(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "stop_experiment") + span, _ := telemetry.StartSpanFromContext(ctx, "stop_experiment") defer func() { if err != nil { log.Errorf("Failed to stop agent experiment: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() err = removeAgentIfInstalled(ctx) @@ -123,14 +122,14 @@ func installAgentPackage(target string, args []string) error { func removeAgentIfInstalled(ctx context.Context) (err error) { if isProductInstalled("Datadog Agent") { - span, _ := tracer.StartSpanFromContext(ctx, "remove_agent") + span, _ := telemetry.StartSpanFromContext(ctx, "remove_agent") defer func() { if err != nil { // removal failed, this should rarely happen. // Rollback might have restored the Agent, but we can't be sure. log.Errorf("Failed to remove agent: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() err := removeProduct("Datadog Agent") if err != nil { diff --git a/pkg/fleet/installer/packages/datadog_installer_windows.go b/pkg/fleet/installer/packages/datadog_installer_windows.go index 2d8dc8241c541..d7714ddb257cc 100644 --- a/pkg/fleet/installer/packages/datadog_installer_windows.go +++ b/pkg/fleet/installer/packages/datadog_installer_windows.go @@ -11,8 +11,8 @@ package packages import ( "context" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -21,12 +21,12 @@ const ( // SetupInstaller installs and starts the installer func SetupInstaller(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "setup_installer") + span, _ := telemetry.StartSpanFromContext(ctx, "setup_installer") defer func() { if err != nil { log.Errorf("Failed to setup installer: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() cmd, err := msiexec("stable", datadogInstaller, "/i", nil) if err == nil { @@ -39,12 +39,12 @@ func SetupInstaller(ctx context.Context) (err error) { // RemoveInstaller removes the installer func RemoveInstaller(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "remove_installer") + span, _ := telemetry.StartSpanFromContext(ctx, "remove_installer") defer func() { if err != nil { log.Errorf("Failed to remove installer: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() err = removeProduct("Datadog Installer") return err @@ -52,12 +52,12 @@ func RemoveInstaller(ctx context.Context) (err error) { // StartInstallerExperiment starts the installer experiment func StartInstallerExperiment(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "start_installer_experiment") + span, _ := telemetry.StartSpanFromContext(ctx, "start_installer_experiment") defer func() { if err != nil { log.Errorf("Failed to start installer experiment: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() cmd, err := msiexec("experiment", datadogInstaller, "/i", nil) if err == nil { @@ -69,12 +69,12 @@ func StartInstallerExperiment(ctx context.Context) (err error) { // StopInstallerExperiment stops the installer experiment func StopInstallerExperiment(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "stop_installer_experiment") + span, _ := telemetry.StartSpanFromContext(ctx, "stop_installer_experiment") defer func() { if err != nil { log.Errorf("Failed to stop installer experiment: %s", err) } - span.Finish(tracer.WithError(err)) + span.Finish(err) }() cmd, err := msiexec("stable", datadogInstaller, "/i", nil) if err == nil { diff --git a/pkg/fleet/installer/packages/docker.go b/pkg/fleet/installer/packages/docker.go index 45984ca96a652..2b264fb7f9e02 100644 --- a/pkg/fleet/installer/packages/docker.go +++ b/pkg/fleet/installer/packages/docker.go @@ -20,9 +20,9 @@ import ( "syscall" "time" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/shirou/gopsutil/v4/process" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) type dockerDaemonConfig map[string]interface{} @@ -73,14 +73,14 @@ func (a *apmInjectorInstaller) uninstrumentDocker(ctx context.Context) error { } // setDockerConfigContent sets the content of the docker daemon configuration -func (a *apmInjectorInstaller) setDockerConfigContent(ctx context.Context, previousContent []byte) ([]byte, error) { - span, _ := tracer.StartSpanFromContext(ctx, "set_docker_config_content") - defer span.Finish() +func (a *apmInjectorInstaller) setDockerConfigContent(ctx context.Context, previousContent []byte) (res []byte, err error) { + span, _ := telemetry.StartSpanFromContext(ctx, "set_docker_config_content") + defer span.Finish(err) dockerConfig := dockerDaemonConfig{} if len(previousContent) > 0 { - err := json.Unmarshal(previousContent, &dockerConfig) + err = json.Unmarshal(previousContent, &dockerConfig) if err != nil { return nil, err } @@ -140,8 +140,8 @@ func (a *apmInjectorInstaller) deleteDockerConfigContent(_ context.Context, prev // // This method is valid since at least Docker 17.03 (last update 2018-08-30) func (a *apmInjectorInstaller) verifyDockerRuntime(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "verify_docker_runtime") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "verify_docker_runtime") + defer func() { span.Finish(err) }() if !isDockerActive(ctx) { log.Warn("docker is inactive, skipping docker runtime verification") @@ -172,8 +172,8 @@ func (a *apmInjectorInstaller) verifyDockerRuntime(ctx context.Context) (err err } func reloadDockerConfig(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "reload_docker") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "reload_docker") + defer func() { span.Finish(err) }() if !isDockerActive(ctx) { log.Warn("docker is inactive, skipping docker reload") return nil @@ -205,8 +205,8 @@ func reloadDockerConfig(ctx context.Context) (err error) { // isDockerInstalled checks if docker is installed on the system func isDockerInstalled(ctx context.Context) bool { - span, _ := tracer.StartSpanFromContext(ctx, "is_docker_installed") - defer span.Finish() + span, _ := telemetry.StartSpanFromContext(ctx, "is_docker_installed") + defer span.Finish(nil) // Docker is installed if the docker binary is in the PATH _, err := exec.LookPath("docker") diff --git a/pkg/fleet/installer/packages/file.go b/pkg/fleet/installer/packages/file.go index b8670b26ee34d..f6e6b7eeadb70 100644 --- a/pkg/fleet/installer/packages/file.go +++ b/pkg/fleet/installer/packages/file.go @@ -15,8 +15,8 @@ import ( "os" "syscall" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) var rollbackNoop = func() error { return nil } @@ -46,8 +46,8 @@ func newFileMutator(path string, transform func(ctx context.Context, existing [] } func (ft *fileMutator) mutate(ctx context.Context) (rollback func() error, err error) { - span, ctx := tracer.StartSpanFromContext(ctx, "mutate_file") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(ctx, "mutate_file") + defer func() { span.Finish(err) }() span.SetTag("file", ft.path) defer os.Remove(ft.pathTmp) diff --git a/pkg/fleet/installer/packages/pkg_manager.go b/pkg/fleet/installer/packages/pkg_manager.go index e74dba210a015..98b9d76b29d59 100644 --- a/pkg/fleet/installer/packages/pkg_manager.go +++ b/pkg/fleet/installer/packages/pkg_manager.go @@ -13,7 +13,7 @@ import ( "fmt" "os/exec" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" ) // removeDebRPMPackage removes a package installed via deb/rpm package manager @@ -21,8 +21,8 @@ import ( // and reinstall the package using the installer. // Note: we don't run the pre/post remove scripts as we want to avoid surprises for older agent versions (like removing config) func removeDebRPMPackage(ctx context.Context, pkg string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "remove_deb_rpm_package") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "remove_deb_rpm_package") + defer func() { span.Finish(err) }() // Compute the right command depending on the package manager var cmd *exec.Cmd if _, pathErr := exec.LookPath("dpkg"); pathErr == nil { diff --git a/pkg/fleet/installer/packages/systemd.go b/pkg/fleet/installer/packages/systemd.go index 9b95672af1533..0daf5623b8332 100644 --- a/pkg/fleet/installer/packages/systemd.go +++ b/pkg/fleet/installer/packages/systemd.go @@ -17,15 +17,15 @@ import ( "path/filepath" "github.com/DataDog/datadog-agent/pkg/fleet/installer/packages/embedded" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const systemdPath = "/etc/systemd/system" func stopUnit(ctx context.Context, unit string, args ...string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "stop_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "stop_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) args = append([]string{"stop", unit}, args...) err = exec.CommandContext(ctx, "systemctl", args...).Run() @@ -42,8 +42,8 @@ func stopUnit(ctx context.Context, unit string, args ...string) (err error) { } func startUnit(ctx context.Context, unit string, args ...string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "start_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "start_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) args = append([]string{"start", unit}, args...) err = exec.CommandContext(ctx, "systemctl", args...).Run() @@ -56,8 +56,8 @@ func startUnit(ctx context.Context, unit string, args ...string) (err error) { } func enableUnit(ctx context.Context, unit string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "enable_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "enable_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) err = exec.CommandContext(ctx, "systemctl", "enable", unit).Run() exitErr := &exec.ExitError{} @@ -69,8 +69,8 @@ func enableUnit(ctx context.Context, unit string) (err error) { } func disableUnit(ctx context.Context, unit string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "disable_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "disable_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) enabledErr := exec.CommandContext(ctx, "systemctl", "is-enabled", "--quiet", unit).Run() @@ -93,8 +93,8 @@ func disableUnit(ctx context.Context, unit string) (err error) { } func loadUnit(ctx context.Context, unit string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "load_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "load_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) content, err := embedded.FS.ReadFile(unit) if err != nil { @@ -105,8 +105,8 @@ func loadUnit(ctx context.Context, unit string) (err error) { } func removeUnit(ctx context.Context, unit string) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "remove_unit") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "remove_unit") + defer func() { span.Finish(err) }() span.SetTag("unit", unit) err = os.Remove(path.Join(systemdPath, unit)) if err != nil && !os.IsNotExist(err) { @@ -116,8 +116,8 @@ func removeUnit(ctx context.Context, unit string) (err error) { } func systemdReload(ctx context.Context) (err error) { - span, _ := tracer.StartSpanFromContext(ctx, "systemd_reload") - defer func() { span.Finish(tracer.WithError(err)) }() + span, _ := telemetry.StartSpanFromContext(ctx, "systemd_reload") + defer func() { span.Finish(err) }() err = exec.CommandContext(ctx, "systemctl", "daemon-reload").Run() exitErr := &exec.ExitError{} if !errors.As(err, &exitErr) { diff --git a/pkg/fleet/installer/setup/common/setup.go b/pkg/fleet/installer/setup/common/setup.go index 7abab785021f6..60931ef01da69 100644 --- a/pkg/fleet/installer/setup/common/setup.go +++ b/pkg/fleet/installer/setup/common/setup.go @@ -17,9 +17,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/installer" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/installer/oci" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/version" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const ( @@ -41,7 +40,7 @@ type Setup struct { Out *Output Env *env.Env Ctx context.Context - Span ddtrace.Span + Span telemetry.Span Packages Packages Config Config } @@ -61,7 +60,7 @@ Running the %s installation script (https://github.com/DataDog/datadog-agent/tre if err != nil { return nil, fmt.Errorf("failed to create installer: %w", err) } - span, ctx := tracer.StartSpanFromContext(ctx, fmt.Sprintf("setup.%s", flavor)) + span, ctx := telemetry.StartSpanFromContext(ctx, fmt.Sprintf("setup.%s", flavor)) s := &Setup{ configDir: configDir, installer: installer, @@ -89,7 +88,7 @@ Running the %s installation script (https://github.com/DataDog/datadog-agent/tre // Run installs the packages and writes the configurations func (s *Setup) Run() (err error) { - defer func() { s.Span.Finish(tracer.WithError(err)) }() + defer func() { s.Span.Finish(err) }() s.Out.WriteString("Applying configurations...\n") err = writeConfigs(s.Config, s.configDir) if err != nil { @@ -118,8 +117,8 @@ func (s *Setup) Run() (err error) { // installPackage mimicks the telemetry of calling the install package command func (s *Setup) installPackage(name string, url string) (err error) { - span, ctx := tracer.StartSpanFromContext(s.Ctx, "install") - defer func() { span.Finish(tracer.WithError(err)) }() + span, ctx := telemetry.StartSpanFromContext(s.Ctx, "install") + defer func() { span.Finish(err) }() span.SetTag("url", url) span.SetTag("_top_level", 1) diff --git a/pkg/fleet/installer/setup/djm/databricks_test.go b/pkg/fleet/installer/setup/djm/databricks_test.go index 6f2457b7d0e29..0bd7d183122cf 100644 --- a/pkg/fleet/installer/setup/djm/databricks_test.go +++ b/pkg/fleet/installer/setup/djm/databricks_test.go @@ -13,9 +13,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" "github.com/DataDog/datadog-agent/pkg/fleet/installer/setup/common" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" ) func TestSetupCommonHostTags(t *testing.T) { @@ -70,7 +70,7 @@ func TestSetupCommonHostTags(t *testing.T) { for k, v := range tt.env { require.NoError(t, os.Setenv(k, v)) } - span, _ := tracer.StartSpanFromContext(context.Background(), "test") + span, _ := telemetry.StartSpanFromContext(context.Background(), "test") s := &common.Setup{Span: span} setupCommonHostTags(s) diff --git a/pkg/fleet/internal/cdn/cdn.go b/pkg/fleet/internal/cdn/cdn.go index ab74ce9bfe0a7..4f65108dfa90f 100644 --- a/pkg/fleet/internal/cdn/cdn.go +++ b/pkg/fleet/internal/cdn/cdn.go @@ -16,8 +16,8 @@ import ( "runtime" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" + "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) const policyMetadataFilename = "policy.metadata" @@ -110,13 +110,13 @@ func New(env *env.Env, configDBPath string) (*CDN, error) { // Get fetches the configuration for the given package. func (c *CDN) Get(ctx context.Context, pkg string) (cfg Config, err error) { - span, _ := tracer.StartSpanFromContext(ctx, "cdn.Get") + span, _ := telemetry.StartSpanFromContext(ctx, "cdn.Get") defer func() { spanErr := err if spanErr == ErrProductNotSupported { spanErr = nil } - span.Finish(tracer.WithError(spanErr)) + span.Finish(spanErr) }() switch pkg { diff --git a/pkg/fleet/internal/exec/installer_exec.go b/pkg/fleet/internal/exec/installer_exec.go index 10da440869079..834f54b3d8a53 100644 --- a/pkg/fleet/internal/exec/installer_exec.go +++ b/pkg/fleet/internal/exec/installer_exec.go @@ -22,7 +22,6 @@ import ( installerErrors "github.com/DataDog/datadog-agent/pkg/fleet/installer/errors" "github.com/DataDog/datadog-agent/pkg/fleet/installer/repository" "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" - "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" ) // InstallerExec is an implementation of the Installer interface that uses the installer binary. @@ -41,13 +40,13 @@ func NewInstallerExec(env *env.Env, installerBinPath string) *InstallerExec { type installerCmd struct { *exec.Cmd - span tracer.Span + span telemetry.Span ctx context.Context } func (i *InstallerExec) newInstallerCmd(ctx context.Context, command string, args ...string) *installerCmd { env := i.env.ToEnv() - span, ctx := tracer.StartSpanFromContext(ctx, fmt.Sprintf("installer.%s", command)) + span, ctx := telemetry.StartSpanFromContext(ctx, fmt.Sprintf("installer.%s", command)) span.SetTag("args", args) cmd := exec.CommandContext(ctx, i.installerBinPath, append([]string{command}, args...)...) env = append(os.Environ(), env...) @@ -72,14 +71,14 @@ func (i *InstallerExec) newInstallerCmd(ctx context.Context, command string, arg // Install installs a package. func (i *InstallerExec) Install(ctx context.Context, url string, _ []string) (err error) { cmd := i.newInstallerCmd(ctx, "install", url) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // Remove removes a package. func (i *InstallerExec) Remove(ctx context.Context, pkg string) (err error) { cmd := i.newInstallerCmd(ctx, "remove", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } @@ -91,70 +90,70 @@ func (i *InstallerExec) Purge(_ context.Context) { // InstallExperiment installs an experiment. func (i *InstallerExec) InstallExperiment(ctx context.Context, url string) (err error) { cmd := i.newInstallerCmd(ctx, "install-experiment", url) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // RemoveExperiment removes an experiment. func (i *InstallerExec) RemoveExperiment(ctx context.Context, pkg string) (err error) { cmd := i.newInstallerCmd(ctx, "remove-experiment", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // PromoteExperiment promotes an experiment to stable. func (i *InstallerExec) PromoteExperiment(ctx context.Context, pkg string) (err error) { cmd := i.newInstallerCmd(ctx, "promote-experiment", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // InstallConfigExperiment installs an experiment. func (i *InstallerExec) InstallConfigExperiment(ctx context.Context, url string, version string) (err error) { cmd := i.newInstallerCmd(ctx, "install-config-experiment", url, version) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // RemoveConfigExperiment removes an experiment. func (i *InstallerExec) RemoveConfigExperiment(ctx context.Context, pkg string) (err error) { cmd := i.newInstallerCmd(ctx, "remove-config-experiment", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // PromoteConfigExperiment promotes an experiment to stable. func (i *InstallerExec) PromoteConfigExperiment(ctx context.Context, pkg string) (err error) { cmd := i.newInstallerCmd(ctx, "promote-config-experiment", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // GarbageCollect runs the garbage collector. func (i *InstallerExec) GarbageCollect(ctx context.Context) (err error) { cmd := i.newInstallerCmd(ctx, "garbage-collect") - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // InstrumentAPMInjector instruments the APM auto-injector. func (i *InstallerExec) InstrumentAPMInjector(ctx context.Context, method string) (err error) { cmd := i.newInstallerCmd(ctx, "apm instrument", method) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // UninstrumentAPMInjector uninstruments the APM auto-injector. func (i *InstallerExec) UninstrumentAPMInjector(ctx context.Context, method string) (err error) { cmd := i.newInstallerCmd(ctx, "apm uninstrument", method) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() return cmd.Run() } // IsInstalled checks if a package is installed. func (i *InstallerExec) IsInstalled(ctx context.Context, pkg string) (_ bool, err error) { cmd := i.newInstallerCmd(ctx, "is-installed", pkg) - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() err = cmd.Run() if err != nil && cmd.ProcessState.ExitCode() == 10 { return false, nil @@ -168,7 +167,7 @@ func (i *InstallerExec) IsInstalled(ctx context.Context, pkg string) (_ bool, er // DefaultPackages returns the default packages to install. func (i *InstallerExec) DefaultPackages(ctx context.Context) (_ []string, err error) { cmd := i.newInstallerCmd(ctx, "default-packages") - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() var stdout bytes.Buffer var stderr bytes.Buffer cmd.Stdout = &stdout @@ -190,7 +189,7 @@ func (i *InstallerExec) DefaultPackages(ctx context.Context) (_ []string, err er // Setup runs the setup command. func (i *InstallerExec) Setup(ctx context.Context) (err error) { cmd := i.newInstallerCmd(ctx, "setup") - defer func() { cmd.span.Finish(tracer.WithError(err)) }() + defer func() { cmd.span.Finish(err) }() var stderr bytes.Buffer cmd.Stderr = &stderr err = cmd.Run() diff --git a/pkg/fleet/telemetry/span.go b/pkg/fleet/telemetry/span.go new file mode 100644 index 0000000000000..ac8f79516c736 --- /dev/null +++ b/pkg/fleet/telemetry/span.go @@ -0,0 +1,26 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +// Package telemetry provides the telemetry for fleet components. +package telemetry + +import ( + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" +) + +// Span is an alias for ddtrace.Span until we phase ddtrace out. +type Span struct{ ddtrace.Span } + +// Finish finishes the span with an error. +func (s *Span) Finish(err error) { + s.Span.Finish(tracer.WithError(err)) +} + +// SetResourceName sets the resource name of the span. +func (s *Span) SetResourceName(name string) { + s.Span.SetTag(ext.ResourceName, name) +} diff --git a/pkg/fleet/telemetry/telemetry.go b/pkg/fleet/telemetry/telemetry.go index f484e06822cd4..df641722cb18a 100644 --- a/pkg/fleet/telemetry/telemetry.go +++ b/pkg/fleet/telemetry/telemetry.go @@ -19,6 +19,7 @@ import ( "strings" "sync" + httptrace "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" @@ -207,17 +208,41 @@ func (addr) String() string { return "local" } -// StartSpanFromEnv starts a span using the environment variables to find the parent span. -func StartSpanFromEnv(ctx context.Context, operationName string, spanOptions ...ddtrace.StartSpanOption) (ddtrace.Span, context.Context) { - spanContext, ok := spanContextFromEnv() - if ok { - spanOptions = append([]ddtrace.StartSpanOption{tracer.ChildOf(spanContext)}, spanOptions...) +// StartSpanFromIDs starts a span using the trace and parent +// IDs provided. +func StartSpanFromIDs(ctx context.Context, operationName, traceID, parentID string, spanOptions ...ddtrace.StartSpanOption) (Span, context.Context) { + ctxCarrier := tracer.TextMapCarrier{ + tracer.DefaultTraceIDHeader: traceID, + tracer.DefaultParentIDHeader: parentID, + tracer.DefaultPriorityHeader: "2", + } + spanCtx, err := tracer.Extract(ctxCarrier) + if err != nil { + log.Debugf("failed to extract span context from install script params: %v", err) + return Span{tracer.StartSpan("remote_request")}, ctx + } + spanOptions = append([]ddtrace.StartSpanOption{tracer.ChildOf(spanCtx)}, spanOptions...) + + return StartSpanFromContext(ctx, operationName, spanOptions...) +} + +// SpanFromContext returns the span from the context if available. +func SpanFromContext(ctx context.Context) (Span, bool) { + span, ok := tracer.SpanFromContext(ctx) + if !ok { + return Span{}, false } - return tracer.StartSpanFromContext(ctx, operationName, spanOptions...) + return Span{span}, true } -// spanContextFromEnv injects the traceID and parentID from the environment into the context if available. -func spanContextFromEnv() (ddtrace.SpanContext, bool) { +// StartSpanFromContext starts a span using the context to find the parent span. +func StartSpanFromContext(ctx context.Context, operationName string, spanOptions ...ddtrace.StartSpanOption) (Span, context.Context) { + span, ctx := tracer.StartSpanFromContext(ctx, operationName, spanOptions...) + return Span{span}, ctx +} + +// StartSpanFromEnv starts a span using the environment variables to find the parent span. +func StartSpanFromEnv(ctx context.Context, operationName string, spanOptions ...ddtrace.StartSpanOption) (Span, context.Context) { traceID, ok := os.LookupEnv(EnvTraceID) if !ok { traceID = strconv.FormatUint(rand.Uint64(), 10) @@ -226,17 +251,7 @@ func spanContextFromEnv() (ddtrace.SpanContext, bool) { if !ok { parentID = "0" } - ctxCarrier := tracer.TextMapCarrier{ - tracer.DefaultTraceIDHeader: traceID, - tracer.DefaultParentIDHeader: parentID, - tracer.DefaultPriorityHeader: "2", - } - spanCtx, err := tracer.Extract(ctxCarrier) - if err != nil { - log.Debugf("failed to extract span context from install script params: %v", err) - return nil, false - } - return spanCtx, true + return StartSpanFromIDs(ctx, operationName, traceID, parentID, spanOptions...) } // EnvFromContext returns the environment variables for the context. @@ -266,3 +281,8 @@ func WithSamplingRules(rules ...tracer.SamplingRule) Option { t.samplingRules = rules } } + +// WrapRoundTripper wraps the round tripper with the telemetry round tripper. +func WrapRoundTripper(rt http.RoundTripper) http.RoundTripper { + return httptrace.WrapRoundTripper(rt) +} From b9ab51afbc1ec3d1c2269328fe7eed437947f2cb Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Sun, 15 Dec 2024 11:06:23 +0200 Subject: [PATCH 10/78] usm: go-tls: Change BPF_LRU_MAP to BPF_HASH_MAP (#32178) --- pkg/network/ebpf/c/protocols/tls/go-tls-maps.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/network/ebpf/c/protocols/tls/go-tls-maps.h b/pkg/network/ebpf/c/protocols/tls/go-tls-maps.h index 1c66dee1d0abc..896b77cc8190f 100644 --- a/pkg/network/ebpf/c/protocols/tls/go-tls-maps.h +++ b/pkg/network/ebpf/c/protocols/tls/go-tls-maps.h @@ -11,11 +11,11 @@ BPF_HASH_MAP(offsets_data, go_tls_offsets_data_key_t, tls_offsets_data_t, 1024) /* go_tls_read_args is used to get the read function info when running in the read-return uprobe. The key contains the go routine id and the pid. */ -BPF_LRU_MAP(go_tls_read_args, go_tls_function_args_key_t, go_tls_read_args_data_t, 2048) +BPF_HASH_MAP(go_tls_read_args, go_tls_function_args_key_t, go_tls_read_args_data_t, 2048) /* go_tls_write_args is used to get the read function info when running in the write-return uprobe. The key contains the go routine id and the pid. */ -BPF_LRU_MAP(go_tls_write_args, go_tls_function_args_key_t, go_tls_write_args_data_t, 2048) +BPF_HASH_MAP(go_tls_write_args, go_tls_function_args_key_t, go_tls_write_args_data_t, 2048) /* This map associates crypto/tls.(*Conn) values to the corresponding conn_tuple_t* value. It is used to implement a simplified version of tup_from_ssl_ctx from usm.c From cfea906319be73b0af7877153829d6360b810766 Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Sun, 15 Dec 2024 11:06:29 +0200 Subject: [PATCH 11/78] usm: sharedlibraries: Change BPF_LRU_MAP to BPF_HASH_MAP (#32179) --- .../ebpf/c/protocols/tls/native-tls-maps.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h b/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h index 4ee9366cd58cc..282c593308b16 100644 --- a/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h +++ b/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h @@ -3,20 +3,20 @@ #include "map-defs.h" -BPF_LRU_MAP(ssl_sock_by_ctx, void *, ssl_sock_t, 1) +BPF_HASH_MAP(ssl_sock_by_ctx, void *, ssl_sock_t, 1) -BPF_LRU_MAP(ssl_read_args, u64, ssl_read_args_t, 1024) +BPF_HASH_MAP(ssl_read_args, u64, ssl_read_args_t, 1024) -BPF_LRU_MAP(ssl_read_ex_args, u64, ssl_read_ex_args_t, 1024) +BPF_HASH_MAP(ssl_read_ex_args, u64, ssl_read_ex_args_t, 1024) -BPF_LRU_MAP(ssl_write_args, u64, ssl_write_args_t, 1024) +BPF_HASH_MAP(ssl_write_args, u64, ssl_write_args_t, 1024) -BPF_LRU_MAP(ssl_write_ex_args, u64, ssl_write_ex_args_t, 1024) +BPF_HASH_MAP(ssl_write_ex_args, u64, ssl_write_ex_args_t, 1024) -BPF_LRU_MAP(bio_new_socket_args, __u64, __u32, 1024) +BPF_HASH_MAP(bio_new_socket_args, __u64, __u32, 1024) -BPF_LRU_MAP(fd_by_ssl_bio, __u32, void *, 1024) +BPF_HASH_MAP(fd_by_ssl_bio, __u32, void *, 1024) -BPF_LRU_MAP(ssl_ctx_by_pid_tgid, __u64, void *, 1024) +BPF_HASH_MAP(ssl_ctx_by_pid_tgid, __u64, void *, 1024) #endif From 6f5f2b0e65208a732d6ccb60d2558ebd00e9eda8 Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Sun, 15 Dec 2024 14:26:21 +0200 Subject: [PATCH 12/78] usm: sharedlibraries: Change BPF_LRU_MAP to BPF_HASH_MAP (#32180) --- pkg/network/ebpf/c/shared-libraries/maps.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/network/ebpf/c/shared-libraries/maps.h b/pkg/network/ebpf/c/shared-libraries/maps.h index ec0112536462a..f1d8f34d854f4 100644 --- a/pkg/network/ebpf/c/shared-libraries/maps.h +++ b/pkg/network/ebpf/c/shared-libraries/maps.h @@ -4,7 +4,9 @@ #include "shared-libraries/types.h" #include "map-defs.h" -BPF_LRU_MAP(open_at_args, __u64, lib_path_t, 1024) +// This map is used with 3 different probes, each can be called up to 1024 times each. +// Thus we need to have a map that can store 1024*3 entries. I'm using a larger map to be safe. +BPF_HASH_MAP(open_at_args, __u64, lib_path_t, 10240) /* * These maps are used for notifying userspace of a shared library being loaded From a0a4d167cf3e2b7452cf160b7c2fc4687fc3299c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9lian=20Raimbault?= <161456554+CelianR@users.noreply.github.com> Date: Mon, 16 Dec 2024 03:53:35 -0500 Subject: [PATCH 13/78] Worktree: Retry checkout with fetch when branch not found (#32124) --- tasks/libs/common/worktree.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tasks/libs/common/worktree.py b/tasks/libs/common/worktree.py index e8c9d740f07bc..17e5db4abfc97 100644 --- a/tasks/libs/common/worktree.py +++ b/tasks/libs/common/worktree.py @@ -9,7 +9,7 @@ from contextlib import contextmanager from pathlib import Path -from invoke.exceptions import Exit +from invoke.exceptions import Exit, UnexpectedExit from tasks.libs.common.color import Color, color_message from tasks.libs.common.git import get_current_branch @@ -49,7 +49,18 @@ def init_env(ctx, branch: str | None = None): f"git -C '{WORKTREE_DIRECTORY}' rev-parse --abbrev-ref HEAD", hide=True ).stdout.strip() if worktree_branch != branch: - ctx.run(f"git -C '{WORKTREE_DIRECTORY}' checkout '{branch}'", hide=True) + for retry in range(2): + try: + ctx.run(f"git -C '{WORKTREE_DIRECTORY}' checkout '{branch}'", hide=True) + except UnexpectedExit as e: + if retry == 1: + raise e + else: + print( + f'{color_message("Warning", Color.ORANGE)}: Git branch not found in the local worktree folder, fetching repository', + file=sys.stderr, + ) + ctx.run(f"git -C '{WORKTREE_DIRECTORY}' fetch", hide=True) if not os.environ.get("AGENT_WORKTREE_NO_PULL"): ctx.run(f"git -C '{WORKTREE_DIRECTORY}' pull", hide=True) From fde3c5b85c546793f1899f942f9afa7e8583269b Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 16 Dec 2024 10:22:43 +0100 Subject: [PATCH 14/78] pkg/util/trivy: move containerd trivy code to separate file (#32193) --- pkg/util/trivy/trivy.go | 96 ------------------------ pkg/util/trivy/trivy_containerd.go | 116 +++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+), 96 deletions(-) create mode 100644 pkg/util/trivy/trivy_containerd.go diff --git a/pkg/util/trivy/trivy.go b/pkg/util/trivy/trivy.go index 07a15d58d93ac..5709f4d384657 100644 --- a/pkg/util/trivy/trivy.go +++ b/pkg/util/trivy/trivy.go @@ -22,13 +22,11 @@ import ( "time" "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/namespaces" "github.com/DataDog/datadog-agent/comp/core/config" workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def" "github.com/DataDog/datadog-agent/pkg/config/env" "github.com/DataDog/datadog-agent/pkg/sbom" - cutil "github.com/DataDog/datadog-agent/pkg/util/containerd" containersimage "github.com/DataDog/datadog-agent/pkg/util/containers/image" "github.com/DataDog/datadog-agent/pkg/util/crio" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -48,9 +46,6 @@ import ( "github.com/aquasecurity/trivy/pkg/scanner/ospkg" "github.com/aquasecurity/trivy/pkg/types" "github.com/aquasecurity/trivy/pkg/vulnerability" - "github.com/containerd/containerd" - "github.com/containerd/containerd/leases" - "github.com/containerd/errdefs" "github.com/docker/docker/client" // This is required to load sqlite based RPM databases @@ -70,9 +65,6 @@ const ( TypeImageConfigSecret = "image-config-secret" // TypeImageConfigSecret defines a history-dockerfile analyzer ) -// ContainerdAccessor is a function that should return a containerd client -type ContainerdAccessor func() (cutil.ContainerdItf, error) - // collectorConfig allows to pass configuration type collectorConfig struct { clearCacheOnClose bool @@ -317,94 +309,6 @@ func (c *Collector) scanOverlayFS(ctx context.Context, layers []string, imgMeta return report, nil } -// ScanContainerdImageFromSnapshotter scans containerd image directly from the snapshotter -func (c *Collector) ScanContainerdImageFromSnapshotter(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { - // Computing duration of containerd lease - deadline, _ := ctx.Deadline() - expiration := deadline.Sub(time.Now().Add(cleanupTimeout)) - clClient := client.RawClient() - imageID := imgMeta.ID - - mounts, err := client.Mounts(ctx, expiration, imgMeta.Namespace, img) - if err != nil { - return nil, fmt.Errorf("unable to get mounts for image %s, err: %w", imgMeta.ID, err) - } - - layers := extractLayersFromOverlayFSMounts(mounts) - if len(layers) == 0 { - return nil, fmt.Errorf("unable to extract layers from overlayfs mounts %+v for image %s", mounts, imgMeta.ID) - } - - ctx = namespaces.WithNamespace(ctx, imgMeta.Namespace) - // Adding a lease to cleanup dandling snaphots at expiration - ctx, done, err := clClient.WithLease(ctx, - leases.WithID(imageID), - leases.WithExpiration(expiration), - leases.WithLabels(map[string]string{ - "containerd.io/gc.ref.snapshot." + containerd.DefaultSnapshotter: imageID, - }), - ) - if err != nil && !errdefs.IsAlreadyExists(err) { - return nil, fmt.Errorf("unable to get a lease, err: %w", err) - } - - report, err := c.scanOverlayFS(ctx, layers, imgMeta, scanOptions) - - if err := done(ctx); err != nil { - log.Warnf("Unable to cancel containerd lease with id: %s, err: %v", imageID, err) - } - - return report, err -} - -// ScanContainerdImage scans containerd image by exporting it and scanning the tarball -func (c *Collector) ScanContainerdImage(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { - fanalImage, cleanup, err := convertContainerdImage(ctx, client.RawClient(), imgMeta, img) - if cleanup != nil { - defer cleanup() - } - if err != nil { - return nil, fmt.Errorf("unable to convert containerd image, err: %w", err) - } - - return c.scanImage(ctx, fanalImage, imgMeta, scanOptions) -} - -// ScanContainerdImageFromFilesystem scans containerd image from file-system -func (c *Collector) ScanContainerdImageFromFilesystem(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { - //nolint:gosimple // TODO(CINT) Fix go simple linte - imagePath, err := os.MkdirTemp(os.TempDir(), fmt.Sprintf("containerd-image-*")) - if err != nil { - return nil, fmt.Errorf("unable to create temp dir, err: %w", err) - } - defer func() { - err := os.RemoveAll(imagePath) - if err != nil { - log.Errorf("Unable to remove temp dir: %s, err: %v", imagePath, err) - } - }() - - // Computing duration of containerd lease - deadline, _ := ctx.Deadline() - expiration := deadline.Sub(time.Now().Add(cleanupTimeout)) - - cleanUp, err := client.MountImage(ctx, expiration, imgMeta.Namespace, img, imagePath) - if err != nil { - return nil, fmt.Errorf("unable to mount containerd image, err: %w", err) - } - - defer func() { - cleanUpContext, cleanUpContextCancel := context.WithTimeout(context.Background(), cleanupTimeout) - err := cleanUp(cleanUpContext) - cleanUpContextCancel() - if err != nil { - log.Errorf("Unable to clean up mounted image, err: %v", err) - } - }() - - return c.scanFilesystem(ctx, os.DirFS("/"), imagePath, imgMeta, scanOptions) -} - // ScanCRIOImageFromOverlayFS scans the CRI-O image layers using OverlayFS. func (c *Collector) ScanCRIOImageFromOverlayFS(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, client crio.Client, scanOptions sbom.ScanOptions) (sbom.Report, error) { lowerDirs, err := client.GetCRIOImageLayers(imgMeta) diff --git a/pkg/util/trivy/trivy_containerd.go b/pkg/util/trivy/trivy_containerd.go new file mode 100644 index 0000000000000..6611a56c217dc --- /dev/null +++ b/pkg/util/trivy/trivy_containerd.go @@ -0,0 +1,116 @@ +// 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 trivy && containerd + +// Package trivy holds the scan components +package trivy + +import ( + "context" + "fmt" + "os" + "time" + + workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def" + "github.com/DataDog/datadog-agent/pkg/sbom" + cutil "github.com/DataDog/datadog-agent/pkg/util/containerd" + "github.com/DataDog/datadog-agent/pkg/util/log" + "github.com/containerd/containerd" + "github.com/containerd/containerd/leases" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/errdefs" +) + +// ContainerdAccessor is a function that should return a containerd client +type ContainerdAccessor func() (cutil.ContainerdItf, error) + +// ScanContainerdImageFromSnapshotter scans containerd image directly from the snapshotter +func (c *Collector) ScanContainerdImageFromSnapshotter(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { + // Computing duration of containerd lease + deadline, _ := ctx.Deadline() + expiration := deadline.Sub(time.Now().Add(cleanupTimeout)) + clClient := client.RawClient() + imageID := imgMeta.ID + + mounts, err := client.Mounts(ctx, expiration, imgMeta.Namespace, img) + if err != nil { + return nil, fmt.Errorf("unable to get mounts for image %s, err: %w", imgMeta.ID, err) + } + + layers := extractLayersFromOverlayFSMounts(mounts) + if len(layers) == 0 { + return nil, fmt.Errorf("unable to extract layers from overlayfs mounts %+v for image %s", mounts, imgMeta.ID) + } + + ctx = namespaces.WithNamespace(ctx, imgMeta.Namespace) + // Adding a lease to cleanup dandling snaphots at expiration + ctx, done, err := clClient.WithLease(ctx, + leases.WithID(imageID), + leases.WithExpiration(expiration), + leases.WithLabels(map[string]string{ + "containerd.io/gc.ref.snapshot." + containerd.DefaultSnapshotter: imageID, + }), + ) + if err != nil && !errdefs.IsAlreadyExists(err) { + return nil, fmt.Errorf("unable to get a lease, err: %w", err) + } + + report, err := c.scanOverlayFS(ctx, layers, imgMeta, scanOptions) + + if err := done(ctx); err != nil { + log.Warnf("Unable to cancel containerd lease with id: %s, err: %v", imageID, err) + } + + return report, err +} + +// ScanContainerdImage scans containerd image by exporting it and scanning the tarball +func (c *Collector) ScanContainerdImage(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { + fanalImage, cleanup, err := convertContainerdImage(ctx, client.RawClient(), imgMeta, img) + if cleanup != nil { + defer cleanup() + } + if err != nil { + return nil, fmt.Errorf("unable to convert containerd image, err: %w", err) + } + + return c.scanImage(ctx, fanalImage, imgMeta, scanOptions) +} + +// ScanContainerdImageFromFilesystem scans containerd image from file-system +func (c *Collector) ScanContainerdImageFromFilesystem(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { + //nolint:gosimple // TODO(CINT) Fix go simple linte + imagePath, err := os.MkdirTemp(os.TempDir(), fmt.Sprintf("containerd-image-*")) + if err != nil { + return nil, fmt.Errorf("unable to create temp dir, err: %w", err) + } + defer func() { + err := os.RemoveAll(imagePath) + if err != nil { + log.Errorf("Unable to remove temp dir: %s, err: %v", imagePath, err) + } + }() + + // Computing duration of containerd lease + deadline, _ := ctx.Deadline() + expiration := deadline.Sub(time.Now().Add(cleanupTimeout)) + + cleanUp, err := client.MountImage(ctx, expiration, imgMeta.Namespace, img, imagePath) + if err != nil { + return nil, fmt.Errorf("unable to mount containerd image, err: %w", err) + } + + defer func() { + cleanUpContext, cleanUpContextCancel := context.WithTimeout(context.Background(), cleanupTimeout) + err := cleanUp(cleanUpContext) + cleanUpContextCancel() + if err != nil { + log.Errorf("Unable to clean up mounted image, err: %v", err) + } + }() + + return c.scanFilesystem(ctx, os.DirFS("/"), imagePath, imgMeta, scanOptions) +} From 62c501b3647c2828426d8b620c2b4f15045dabc9 Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Mon, 16 Dec 2024 12:00:36 +0100 Subject: [PATCH 15/78] [CWS] Allow triggering an activity dump from CLI (#31968) --- .../subcommands/runtime/activity_dump.go | 18 +- .../subcommands/runtime/command_linux.go | 3 + .../subcommands/runtime/activity_dump.go | 17 +- .../subcommands/runtime/command_linux.go | 3 + pkg/security/agent/client.go | 7 +- .../mocks/security_module_client_wrapper.go | 18 +- .../ebpf/c/include/helpers/activity_dump.h | 41 +- pkg/security/ebpf/c/include/maps.h | 4 +- pkg/security/probe/field_handlers_ebpf.go | 7 +- pkg/security/probe/probe_ebpf.go | 26 +- pkg/security/process_list/process_list.go | 3 +- .../process_resolver/process_resolver_test.go | 3 +- pkg/security/proto/api/api.pb.go | 639 +++++++++--------- pkg/security/proto/api/api.proto | 3 + pkg/security/proto/api/api_vtproto.pb.go | 129 ++++ pkg/security/resolvers/resolvers_ebpf.go | 38 +- .../secl/model/unmarshallers_linux.go | 2 +- .../activity_tree/metadata/metadata.go | 25 +- .../metadata/metadata_proto_dec_v1.go | 3 +- .../metadata/metadata_proto_enc_v1.go | 2 +- .../security_profile/dump/activity_dump.go | 93 ++- .../security_profile/dump/load_controller.go | 1 + pkg/security/security_profile/dump/manager.go | 91 ++- .../security_profile/dump/manager_test.go | 5 +- .../tests/activity_tree_test.go | 2 +- pkg/security/tests/activity_dumps_test.go | 14 +- pkg/security/tests/cmdwrapper.go | 22 +- pkg/security/tests/main_linux.go | 14 + pkg/security/tests/main_windows.go | 7 + pkg/security/tests/module_tester_linux.go | 19 +- pkg/security/tests/security_profile_test.go | 61 +- pkg/security/tests/threat_score_test.go | 8 +- pkg/security/utils/cgroup.go | 5 +- 33 files changed, 828 insertions(+), 505 deletions(-) diff --git a/cmd/security-agent/subcommands/runtime/activity_dump.go b/cmd/security-agent/subcommands/runtime/activity_dump.go index 514cdda1bc612..35907553045fb 100644 --- a/cmd/security-agent/subcommands/runtime/activity_dump.go +++ b/cmd/security-agent/subcommands/runtime/activity_dump.go @@ -37,6 +37,7 @@ type activityDumpCliParams struct { name string containerID string + cgroupID string file string file2 string timeout string @@ -113,7 +114,13 @@ func stopCommands(globalParams *command.GlobalParams) []*cobra.Command { &cliParams.containerID, "container-id", "", - "an containerID can be used to filter the activity dump.", + "a containerID can be used to filter the activity dump.", + ) + activityDumpStopCmd.Flags().StringVar( + &cliParams.cgroupID, + "cgroup-id", + "", + "a cgroup ID can be used to filter the activity dump.", ) return []*cobra.Command{activityDumpStopCmd} @@ -157,6 +164,12 @@ func generateDumpCommands(globalParams *command.GlobalParams) []*cobra.Command { "", "a container identifier can be used to filter the activity dump from a specific container.", ) + activityDumpGenerateDumpCmd.Flags().StringVar( + &cliParams.cgroupID, + "cgroup-id", + "", + "a cgroup identifier can be used to filter the activity dump from a specific cgroup.", + ) activityDumpGenerateDumpCmd.Flags().StringVar( &cliParams.timeout, "timeout", @@ -461,6 +474,7 @@ func generateActivityDump(_ log.Component, _ config.Component, _ secrets.Compone output, err := client.GenerateActivityDump(&api.ActivityDumpParams{ ContainerID: activityDumpArgs.containerID, + CGroupID: activityDumpArgs.cgroupID, Timeout: activityDumpArgs.timeout, DifferentiateArgs: activityDumpArgs.differentiateArgs, Storage: storage, @@ -609,7 +623,7 @@ func stopActivityDump(_ log.Component, _ config.Component, _ secrets.Component, } defer client.Close() - output, err := client.StopActivityDump(activityDumpArgs.name, activityDumpArgs.containerID) + output, err := client.StopActivityDump(activityDumpArgs.name, activityDumpArgs.containerID, activityDumpArgs.cgroupID) if err != nil { return fmt.Errorf("unable to send request to system-probe: %w", err) } diff --git a/cmd/security-agent/subcommands/runtime/command_linux.go b/cmd/security-agent/subcommands/runtime/command_linux.go index 4c5a90d3167cd..a813dc82b0385 100644 --- a/cmd/security-agent/subcommands/runtime/command_linux.go +++ b/cmd/security-agent/subcommands/runtime/command_linux.go @@ -46,6 +46,9 @@ func printSecurityActivityDumpMessage(prefix string, msg *api.ActivityDumpMessag if len(msg.GetMetadata().GetContainerID()) > 0 { fmt.Printf("%s container ID: %s\n", prefix, msg.GetMetadata().GetContainerID()) } + if len(msg.GetMetadata().GetCGroupID()) > 0 { + fmt.Printf("%s cgroup ID: %s\n", prefix, msg.GetMetadata().GetCGroupID()) + } if len(msg.GetTags()) > 0 { fmt.Printf("%s tags: %s\n", prefix, strings.Join(msg.GetTags(), ", ")) } diff --git a/cmd/system-probe/subcommands/runtime/activity_dump.go b/cmd/system-probe/subcommands/runtime/activity_dump.go index 72096d1160bbd..e00d027c94d7b 100644 --- a/cmd/system-probe/subcommands/runtime/activity_dump.go +++ b/cmd/system-probe/subcommands/runtime/activity_dump.go @@ -34,6 +34,7 @@ type activityDumpCliParams struct { name string containerID string + cgroupID string file string file2 string timeout string @@ -109,7 +110,12 @@ func stopCommands(globalParams *command.GlobalParams) []*cobra.Command { "", "an containerID can be used to filter the activity dump.", ) - + activityDumpStopCmd.Flags().StringVar( + &cliParams.cgroupID, + "cgroup-id", + "", + "a cgroup ID can be used to filter the activity dump.", + ) return []*cobra.Command{activityDumpStopCmd} } @@ -151,6 +157,12 @@ func generateDumpCommands(globalParams *command.GlobalParams) []*cobra.Command { "", "a container identifier can be used to filter the activity dump from a specific container.", ) + activityDumpGenerateDumpCmd.Flags().StringVar( + &cliParams.cgroupID, + "cgroup-id", + "", + "a cgroup identifier can be used to filter the activity dump from a specific cgroup.", + ) activityDumpGenerateDumpCmd.Flags().StringVar( &cliParams.timeout, "timeout", @@ -449,6 +461,7 @@ func generateActivityDump(_ log.Component, _ config.Component, _ secrets.Compone output, err := client.GenerateActivityDump(&api.ActivityDumpParams{ ContainerID: activityDumpArgs.containerID, + CGroupID: activityDumpArgs.cgroupID, Timeout: activityDumpArgs.timeout, DifferentiateArgs: activityDumpArgs.differentiateArgs, Storage: storage, @@ -573,7 +586,7 @@ func stopActivityDump(_ log.Component, _ config.Component, _ secrets.Component, } defer client.Close() - output, err := client.StopActivityDump(activityDumpArgs.name, activityDumpArgs.containerID) + output, err := client.StopActivityDump(activityDumpArgs.name, activityDumpArgs.containerID, activityDumpArgs.cgroupID) if err != nil { return fmt.Errorf("unable to send request to system-probe: %w", err) } diff --git a/cmd/system-probe/subcommands/runtime/command_linux.go b/cmd/system-probe/subcommands/runtime/command_linux.go index 51d2a8c92dec7..1980ac0fbfb23 100644 --- a/cmd/system-probe/subcommands/runtime/command_linux.go +++ b/cmd/system-probe/subcommands/runtime/command_linux.go @@ -42,6 +42,9 @@ func printSecurityActivityDumpMessage(prefix string, msg *api.ActivityDumpMessag if len(msg.GetMetadata().GetContainerID()) > 0 { fmt.Printf("%s container ID: %s\n", prefix, msg.GetMetadata().GetContainerID()) } + if len(msg.GetMetadata().GetCGroupID()) > 0 { + fmt.Printf("%s cgroup ID: %s\n", prefix, msg.GetMetadata().GetCGroupID()) + } if len(msg.GetTags()) > 0 { fmt.Printf("%s tags: %s\n", prefix, strings.Join(msg.GetTags(), ", ")) } diff --git a/pkg/security/agent/client.go b/pkg/security/agent/client.go index 81e2cf630b549..bb0c1714bd039 100644 --- a/pkg/security/agent/client.go +++ b/pkg/security/agent/client.go @@ -35,7 +35,7 @@ type SecurityModuleClientWrapper interface { DumpProcessCache(withArgs bool, format string) (string, error) GenerateActivityDump(request *api.ActivityDumpParams) (*api.ActivityDumpMessage, error) ListActivityDumps() (*api.ActivityDumpListMessage, error) - StopActivityDump(name, containerid string) (*api.ActivityDumpStopMessage, error) + StopActivityDump(name, container, cgroup string) (*api.ActivityDumpStopMessage, error) GenerateEncoding(request *api.TranscodingRequestParams) (*api.TranscodingRequestMessage, error) DumpNetworkNamespace(snapshotInterfaces bool) (*api.DumpNetworkNamespaceMessage, error) GetConfig() (*api.SecurityConfigMessage, error) @@ -81,10 +81,11 @@ func (c *RuntimeSecurityClient) GenerateActivityDump(request *api.ActivityDumpPa } // StopActivityDump stops an active dump if it exists -func (c *RuntimeSecurityClient) StopActivityDump(name, containerid string) (*api.ActivityDumpStopMessage, error) { +func (c *RuntimeSecurityClient) StopActivityDump(name, container, cgroup string) (*api.ActivityDumpStopMessage, error) { return c.apiClient.StopActivityDump(context.Background(), &api.ActivityDumpStopParams{ Name: name, - ContainerID: containerid, + ContainerID: container, + CGroupID: cgroup, }) } diff --git a/pkg/security/agent/mocks/security_module_client_wrapper.go b/pkg/security/agent/mocks/security_module_client_wrapper.go index 227b6c2072121..c31eae478f235 100644 --- a/pkg/security/agent/mocks/security_module_client_wrapper.go +++ b/pkg/security/agent/mocks/security_module_client_wrapper.go @@ -463,9 +463,9 @@ func (_m *SecurityModuleClientWrapper) SaveSecurityProfile(name string, tag stri return r0, r1 } -// StopActivityDump provides a mock function with given fields: name, containerid -func (_m *SecurityModuleClientWrapper) StopActivityDump(name string, containerid string) (*api.ActivityDumpStopMessage, error) { - ret := _m.Called(name, containerid) +// StopActivityDump provides a mock function with given fields: name, container, cgroup +func (_m *SecurityModuleClientWrapper) StopActivityDump(name string, container string, cgroup string) (*api.ActivityDumpStopMessage, error) { + ret := _m.Called(name, container, cgroup) if len(ret) == 0 { panic("no return value specified for StopActivityDump") @@ -473,19 +473,19 @@ func (_m *SecurityModuleClientWrapper) StopActivityDump(name string, containerid var r0 *api.ActivityDumpStopMessage var r1 error - if rf, ok := ret.Get(0).(func(string, string) (*api.ActivityDumpStopMessage, error)); ok { - return rf(name, containerid) + if rf, ok := ret.Get(0).(func(string, string, string) (*api.ActivityDumpStopMessage, error)); ok { + return rf(name, container, cgroup) } - if rf, ok := ret.Get(0).(func(string, string) *api.ActivityDumpStopMessage); ok { - r0 = rf(name, containerid) + if rf, ok := ret.Get(0).(func(string, string, string) *api.ActivityDumpStopMessage); ok { + r0 = rf(name, container, cgroup) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*api.ActivityDumpStopMessage) } } - if rf, ok := ret.Get(1).(func(string, string) error); ok { - r1 = rf(name, containerid) + if rf, ok := ret.Get(1).(func(string, string, string) error); ok { + r1 = rf(name, container, cgroup) } else { r1 = ret.Error(1) } diff --git a/pkg/security/ebpf/c/include/helpers/activity_dump.h b/pkg/security/ebpf/c/include/helpers/activity_dump.h index 82ed6834e10fb..c0f8d246006bf 100644 --- a/pkg/security/ebpf/c/include/helpers/activity_dump.h +++ b/pkg/security/ebpf/c/include/helpers/activity_dump.h @@ -53,7 +53,7 @@ __attribute__((always_inline)) struct cgroup_tracing_event_t *get_cgroup_tracing return evt; } -__attribute__((always_inline)) bool reserve_traced_cgroup_spot(container_id_t cgroup, u64 now, u64 cookie, struct activity_dump_config *config) { +__attribute__((always_inline)) bool reserve_traced_cgroup_spot(struct cgroup_context_t *cgroup, u64 now, u64 cookie, struct activity_dump_config *config) { // insert dump config defaults u32 defaults_key = 0; struct activity_dump_config *defaults = bpf_map_lookup_elem(&activity_dump_config_defaults, &defaults_key); @@ -72,7 +72,9 @@ __attribute__((always_inline)) bool reserve_traced_cgroup_spot(container_id_t cg return false; } - ret = bpf_map_update_elem(&traced_cgroups, &cgroup[0], &cookie, BPF_NOEXIST); + struct path_key_t path_key; + path_key = cgroup->cgroup_file; + ret = bpf_map_update_elem(&traced_cgroups, &path_key, &cookie, BPF_NOEXIST); if (ret < 0) { // we didn't get a lock, skip this cgroup for now and go back to it later bpf_map_delete_elem(&activity_dumps_config, &cookie); @@ -80,15 +82,15 @@ __attribute__((always_inline)) bool reserve_traced_cgroup_spot(container_id_t cg } // we're tracing a new cgroup, update its wait list timeout - bpf_map_update_elem(&cgroup_wait_list, &cgroup[0], &config->wait_list_timestamp, BPF_ANY); + bpf_map_update_elem(&cgroup_wait_list, &path_key, &config->wait_list_timestamp, BPF_ANY); return true; } -__attribute__((always_inline)) u64 trace_new_cgroup(void *ctx, u64 now, container_id_t container_id, struct cgroup_context_t *cgroup) { +__attribute__((always_inline)) u64 trace_new_cgroup(void *ctx, u64 now, struct container_context_t *container) { u64 cookie = rand64(); struct activity_dump_config config = {}; - if (!reserve_traced_cgroup_spot(container_id, now, cookie, &config)) { + if (!reserve_traced_cgroup_spot(&container->cgroup_context, now, cookie, &config)) { // we're already tracing too many cgroups concurrently, ignore this one for now return 0; } @@ -100,35 +102,38 @@ __attribute__((always_inline)) u64 trace_new_cgroup(void *ctx, u64 now, containe return 0; } - if ((cgroup->cgroup_flags & 0b111) == CGROUP_MANAGER_SYSTEMD) { + if ((container->cgroup_context.cgroup_flags & 0b111) == CGROUP_MANAGER_SYSTEMD) { return 0; } - copy_container_id(container_id, evt->container.container_id); - evt->container.cgroup_context = *cgroup; + copy_container_id(container->container_id, evt->container.container_id); + evt->container.cgroup_context = container->cgroup_context; evt->cookie = cookie; evt->config = config; send_event_ptr(ctx, EVENT_CGROUP_TRACING, evt); - // return cookie return cookie; } +__attribute__((always_inline)) u64 is_cgroup_activity_dumps_supported(struct cgroup_context_t *cgroup) { + return (cgroup->cgroup_flags != 0) && ((cgroup->cgroup_flags&0b111) != CGROUP_MANAGER_SYSTEMD); +} + __attribute__((always_inline)) u64 should_trace_new_process_cgroup(void *ctx, u64 now, u32 pid, struct container_context_t *container) { // should we start tracing this cgroup ? - container_id_t container_id; - bpf_probe_read(&container_id, sizeof(container_id), &container->container_id[0]); + struct cgroup_context_t cgroup_context; + bpf_probe_read(&cgroup_context, sizeof(cgroup_context), &container->cgroup_context); - if (is_cgroup_activity_dumps_enabled() && container_id[0] != 0) { + if (is_cgroup_activity_dumps_enabled() && is_cgroup_activity_dumps_supported(&cgroup_context)) { // is this cgroup traced ? - u64 *cookie = bpf_map_lookup_elem(&traced_cgroups, &container_id[0]); + u64 *cookie = bpf_map_lookup_elem(&traced_cgroups, &cgroup_context.cgroup_file); if (cookie) { u64 cookie_val = *cookie; struct activity_dump_config *config = bpf_map_lookup_elem(&activity_dumps_config, &cookie_val); if (config == NULL) { // delete expired cgroup entry - bpf_map_delete_elem(&traced_cgroups, &container_id[0]); + bpf_map_delete_elem(&traced_cgroups, &cgroup_context.cgroup_file); return 0; } @@ -144,7 +149,7 @@ __attribute__((always_inline)) u64 should_trace_new_process_cgroup(void *ctx, u6 if (now > config->end_timestamp) { // delete expired cgroup entry - bpf_map_delete_elem(&traced_cgroups, &container_id[0]); + bpf_map_delete_elem(&traced_cgroups, &cgroup_context.cgroup_file); // delete config bpf_map_delete_elem(&activity_dumps_config, &cookie_val); return 0; @@ -156,11 +161,11 @@ __attribute__((always_inline)) u64 should_trace_new_process_cgroup(void *ctx, u6 } else { // have we seen this cgroup before ? - u64 *wait_timeout = bpf_map_lookup_elem(&cgroup_wait_list, &container_id[0]); + u64 *wait_timeout = bpf_map_lookup_elem(&cgroup_wait_list, &cgroup_context.cgroup_file); if (wait_timeout) { if (now > *wait_timeout) { // delete expired wait_list entry - bpf_map_delete_elem(&cgroup_wait_list, &container_id[0]); + bpf_map_delete_elem(&cgroup_wait_list, &cgroup_context.cgroup_file); } // this cgroup is on the wait list, do not start tracing it @@ -168,7 +173,7 @@ __attribute__((always_inline)) u64 should_trace_new_process_cgroup(void *ctx, u6 } // can we start tracing this cgroup ? - u64 cookie_val = trace_new_cgroup(ctx, now, container_id, &container->cgroup_context); + u64 cookie_val = trace_new_cgroup(ctx, now, container); if (cookie_val == 0) { return 0; } diff --git a/pkg/security/ebpf/c/include/maps.h b/pkg/security/ebpf/c/include/maps.h index 2019c630c6f4c..d6ab3ceb74dfb 100644 --- a/pkg/security/ebpf/c/include/maps.h +++ b/pkg/security/ebpf/c/include/maps.h @@ -29,8 +29,8 @@ BPF_ARRAY_MAP(syscall_ctx, char[MAX_SYSCALL_CTX_SIZE], MAX_SYSCALL_CTX_ENTRIES) BPF_HASH_MAP(activity_dumps_config, u64, struct activity_dump_config, 1) // max entries will be overridden at runtime BPF_HASH_MAP(activity_dump_config_defaults, u32, struct activity_dump_config, 1) -BPF_HASH_MAP(traced_cgroups, container_id_t, u64, 1) // max entries will be overridden at runtime -BPF_HASH_MAP(cgroup_wait_list, container_id_t, u64, 1) // max entries will be overridden at runtime +BPF_HASH_MAP(traced_cgroups, struct path_key_t, u64, 1) // max entries will be overridden at runtime +BPF_HASH_MAP(cgroup_wait_list, struct path_key_t, u64, 1) // max entries will be overridden at runtime BPF_HASH_MAP(traced_pids, u32, u64, 8192) // max entries will be overridden at runtime BPF_HASH_MAP(basename_approvers, struct basename_t, struct event_mask_filter_t, 255) BPF_HASH_MAP(register_netdevice_cache, u64, struct register_netdevice_cache_t, 1024) diff --git a/pkg/security/probe/field_handlers_ebpf.go b/pkg/security/probe/field_handlers_ebpf.go index 36acd3a33d5a2..b76b70a76b833 100644 --- a/pkg/security/probe/field_handlers_ebpf.go +++ b/pkg/security/probe/field_handlers_ebpf.go @@ -20,7 +20,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/resolvers" sprocess "github.com/DataDog/datadog-agent/pkg/security/resolvers/process" "github.com/DataDog/datadog-agent/pkg/security/secl/containerutils" - "github.com/DataDog/datadog-agent/pkg/security/seclog" "github.com/DataDog/datadog-agent/pkg/security/secl/args" "github.com/DataDog/datadog-agent/pkg/security/secl/model" @@ -516,11 +515,9 @@ func (fh *EBPFFieldHandlers) ResolveCGroupID(ev *model.Event, e *model.CGroupCon return string(entry.CGroup.CGroupID) } - if err := fh.resolvers.ResolveCGroup(entry, e.CGroupFile, e.CGroupFlags); err != nil { - seclog.Debugf("Failed to resolve cgroup: %s", err) + if cgroupContext, err := fh.resolvers.ResolveCGroupContext(e.CGroupFile, e.CGroupFlags); err == nil { + *e = *cgroupContext } - - e.CGroupID = entry.CGroup.CGroupID } } diff --git a/pkg/security/probe/probe_ebpf.go b/pkg/security/probe/probe_ebpf.go index ca55fb768bd5f..f06cbe8fa45ae 100644 --- a/pkg/security/probe/probe_ebpf.go +++ b/pkg/security/probe/probe_ebpf.go @@ -818,7 +818,18 @@ func (p *EBPFProbe) handleEvent(CPU int, data []byte) { return } - p.profileManagers.activityDumpManager.HandleCGroupTracingEvent(&event.CgroupTracing) + if cgroupContext, err := p.Resolvers.ResolveCGroupContext(event.CgroupTracing.CGroupContext.CGroupFile, containerutils.CGroupFlags(event.CgroupTracing.CGroupContext.CGroupFlags)); err != nil { + seclog.Debugf("Failed to resolve cgroup: %s", err) + } else { + event.CgroupTracing.CGroupContext = *cgroupContext + if cgroupContext.CGroupFlags.IsContainer() { + containerID, _ := containerutils.FindContainerID(cgroupContext.CGroupID) + event.CgroupTracing.ContainerContext.ContainerID = containerID + } + + p.profileManagers.activityDumpManager.HandleCGroupTracingEvent(&event.CgroupTracing) + } + return case model.CgroupWriteEventType: if _, err = event.CgroupWrite.UnmarshalBinary(data[offset:]); err != nil { @@ -828,10 +839,21 @@ func (p *EBPFProbe) handleEvent(CPU int, data []byte) { pce := p.Resolvers.ProcessResolver.Resolve(event.CgroupWrite.Pid, event.CgroupWrite.Pid, 0, false, newEntryCb) if pce != nil { - if err := p.Resolvers.ResolveCGroup(pce, event.CgroupWrite.File.PathKey, containerutils.CGroupFlags(event.CgroupWrite.CGroupFlags)); err != nil { + cgroupContext, err := p.Resolvers.ResolveCGroupContext(event.CgroupWrite.File.PathKey, containerutils.CGroupFlags(event.CgroupWrite.CGroupFlags)) + if err != nil { seclog.Debugf("Failed to resolve cgroup: %s", err) + } else { + pce.Process.CGroup = *cgroupContext + pce.CGroup = *cgroupContext + + if cgroupContext.CGroupFlags.IsContainer() { + containerID, _ := containerutils.FindContainerID(cgroupContext.CGroupID) + pce.ContainerID = containerID + pce.Process.ContainerID = containerID + } } } + return case model.UnshareMountNsEventType: if _, err = event.UnshareMountNS.UnmarshalBinary(data[offset:]); err != nil { diff --git a/pkg/security/process_list/process_list.go b/pkg/security/process_list/process_list.go index 1fdb4a80bd70b..64c76705a553f 100644 --- a/pkg/security/process_list/process_list.go +++ b/pkg/security/process_list/process_list.go @@ -14,9 +14,8 @@ import ( "io" "sync" - cgroupModel "github.com/DataDog/datadog-agent/pkg/security/resolvers/cgroup/model" - "github.com/DataDog/datadog-agent/pkg/process/procutil" + cgroupModel "github.com/DataDog/datadog-agent/pkg/security/resolvers/cgroup/model" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-go/v5/statsd" "golang.org/x/exp/slices" diff --git a/pkg/security/process_list/process_resolver/process_resolver_test.go b/pkg/security/process_list/process_resolver/process_resolver_test.go index 600e0c03bc8a9..1c062c45c0f99 100644 --- a/pkg/security/process_list/process_resolver/process_resolver_test.go +++ b/pkg/security/process_list/process_resolver/process_resolver_test.go @@ -141,7 +141,8 @@ func isProcessOrExecPresent(pl *processlist.ProcessList, pc *ProcessResolver, ev func TestFork1st(t *testing.T) { pc := NewProcessResolver() - processList := processlist.NewProcessList(cgroupModel.WorkloadSelector{Image: "*", Tag: "*"}, + selector, _ := cgroupModel.NewWorkloadSelector("*", "*") + processList := processlist.NewProcessList(selector, []model.EventType{model.ExecEventType, model.ForkEventType, model.ExitEventType}, pc /* ,nil */, nil, nil) stats := testStats{} diff --git a/pkg/security/proto/api/api.pb.go b/pkg/security/proto/api/api.pb.go index 60cc167d246f9..e8fd80f24c5c8 100644 --- a/pkg/security/proto/api/api.pb.go +++ b/pkg/security/proto/api/api.pb.go @@ -1594,6 +1594,7 @@ type ActivityDumpParams struct { DifferentiateArgs bool `protobuf:"varint,2,opt,name=DifferentiateArgs,proto3" json:"DifferentiateArgs,omitempty"` Storage *StorageRequestParams `protobuf:"bytes,3,opt,name=Storage,proto3" json:"Storage,omitempty"` ContainerID string `protobuf:"bytes,4,opt,name=ContainerID,proto3" json:"ContainerID,omitempty"` + CGroupID string `protobuf:"bytes,5,opt,name=CGroupID,proto3" json:"CGroupID,omitempty"` } func (x *ActivityDumpParams) Reset() { @@ -1656,6 +1657,13 @@ func (x *ActivityDumpParams) GetContainerID() string { return "" } +func (x *ActivityDumpParams) GetCGroupID() string { + if x != nil { + return x.CGroupID + } + return "" +} + type MetadataMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1676,6 +1684,7 @@ type MetadataMessage struct { Timeout string `protobuf:"bytes,12,opt,name=Timeout,proto3" json:"Timeout,omitempty"` Size uint64 `protobuf:"varint,13,opt,name=Size,proto3" json:"Size,omitempty"` Serialization string `protobuf:"bytes,14,opt,name=Serialization,proto3" json:"Serialization,omitempty"` + CGroupID string `protobuf:"bytes,15,opt,name=CGroupID,proto3" json:"CGroupID,omitempty"` } func (x *MetadataMessage) Reset() { @@ -1809,6 +1818,13 @@ func (x *MetadataMessage) GetSerialization() string { return "" } +func (x *MetadataMessage) GetCGroupID() string { + if x != nil { + return x.CGroupID + } + return "" +} + type StorageRequestMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -2091,6 +2107,7 @@ type ActivityDumpStopParams struct { Name string `protobuf:"bytes,1,opt,name=Name,proto3" json:"Name,omitempty"` ContainerID string `protobuf:"bytes,2,opt,name=ContainerID,proto3" json:"ContainerID,omitempty"` + CGroupID string `protobuf:"bytes,3,opt,name=CGroupID,proto3" json:"CGroupID,omitempty"` } func (x *ActivityDumpStopParams) Reset() { @@ -2139,6 +2156,13 @@ func (x *ActivityDumpStopParams) GetContainerID() string { return "" } +func (x *ActivityDumpStopParams) GetCGroupID() string { + if x != nil { + return x.CGroupID + } + return "" +} + type ActivityDumpStopMessage struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -3298,7 +3322,7 @@ var file_pkg_security_proto_api_api_proto_rawDesc = []byte{ 0x0a, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x18, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xb3, 0x01, 0x0a, 0x12, 0x41, + 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xcf, 0x01, 0x0a, 0x12, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x2c, 0x0a, 0x11, 0x44, @@ -3310,312 +3334,317 @@ var file_pkg_security_proto_api_api_proto_rawDesc = []byte{ 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, - 0x22, 0xcf, 0x03, 0x0a, 0x0f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, - 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x67, 0x65, 0x6e, - 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, - 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, - 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2c, 0x0a, 0x11, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, - 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4c, 0x69, 0x6e, - 0x75, 0x78, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, - 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, - 0x63, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x12, 0x2c, 0x0a, 0x11, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x74, - 0x65, 0x41, 0x72, 0x67, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x44, 0x69, 0x66, - 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x16, - 0x0a, 0x04, 0x43, 0x6f, 0x6d, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, - 0x52, 0x04, 0x43, 0x6f, 0x6d, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, - 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, - 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, - 0x0a, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0d, - 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x79, 0x0a, 0x15, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, - 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x16, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, - 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x43, 0x6f, - 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x69, 0x6c, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xbe, 0x02, - 0x0a, 0x13, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, - 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, - 0x34, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, - 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x4d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, - 0x08, 0x44, 0x4e, 0x53, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x08, 0x44, 0x4e, 0x53, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x18, - 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, - 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x5f, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, - 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, - 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x44, 0x75, - 0x6d, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x4e, 0x0a, 0x16, 0x41, 0x63, 0x74, - 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, - 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, - 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x22, 0x2f, 0x0a, 0x17, 0x41, 0x63, 0x74, - 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7b, 0x0a, 0x18, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, - 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x10, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x46, 0x69, - 0x6c, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, - 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x52, 0x07, - 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x67, 0x0a, 0x19, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x34, 0x0a, 0x07, 0x53, 0x74, - 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, - 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, - 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x22, 0x5d, 0x0a, 0x19, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x04, 0x44, 0x75, 0x6d, - 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, - 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x52, 0x04, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, 0x3f, 0x0a, 0x17, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x54, 0x61, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54, 0x61, 0x67, 0x22, 0x87, 0x01, 0x0a, - 0x1b, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, 0x54, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x11, 0x49, 0x73, 0x53, 0x74, - 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x11, 0x49, 0x73, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x0f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, - 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, - 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x54, - 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x22, - 0xec, 0x01, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x72, 0x65, 0x65, - 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, 0x11, - 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, - 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x46, 0x69, - 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, - 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x44, 0x4e, 0x53, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, - 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x44, 0x4e, 0x53, 0x4e, 0x6f, - 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, 0x53, 0x6f, 0x63, 0x6b, - 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x03, 0x52, 0x10, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, - 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x6d, - 0x61, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x41, - 0x70, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x22, 0x6e, - 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, 0x6e, 0x6f, 0x6d, 0x61, - 0x6c, 0x79, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6c, - 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, 0x4e, 0x61, 0x6e, 0x6f, 0x12, 0x2e, - 0x0a, 0x13, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x9b, - 0x02, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x69, 0x72, 0x73, - 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x69, - 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x61, 0x73, 0x74, 0x5f, - 0x73, 0x65, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x61, 0x73, 0x74, - 0x53, 0x65, 0x65, 0x6e, 0x12, 0x58, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, - 0x65, 0x78, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x54, 0x79, 0x70, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x12, - 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x1a, 0x58, 0x0a, 0x13, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2b, 0x0a, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xa0, 0x06, 0x0a, - 0x16, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x4c, 0x6f, 0x61, 0x64, 0x65, - 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x12, - 0x38, 0x0a, 0x17, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, 0x6e, 0x65, - 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x17, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, 0x08, 0x53, 0x65, 0x6c, - 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, - 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, - 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x4a, 0x0a, 0x0d, 0x4c, 0x61, 0x73, - 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, - 0x6c, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, - 0x61, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x49, - 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x09, - 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x06, 0x53, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x53, - 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x07, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, 0x56, 0x65, 0x72, 0x73, - 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x4d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x0b, 0x20, - 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x33, 0x0a, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, 0x72, 0x65, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x53, 0x74, 0x61, - 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x47, 0x6c, 0x6f, - 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x12, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x63, 0x6f, - 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0f, - 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x1a, - 0x5e, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, - 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, - 0x3f, 0x0a, 0x19, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x22, 0x0a, 0x0c, - 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x0c, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, - 0x22, 0x6b, 0x0a, 0x1a, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, - 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, - 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x55, 0x0a, - 0x19, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, - 0x53, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x38, 0x0a, 0x08, 0x53, 0x65, - 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x53, 0x65, 0x6c, 0x65, - 0x63, 0x74, 0x6f, 0x72, 0x22, 0x46, 0x0a, 0x1a, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x32, 0x8a, 0x0a, 0x0a, - 0x0e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, - 0x3f, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x13, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, - 0x12, 0x57, 0x0a, 0x10, 0x44, 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, - 0x61, 0x63, 0x68, 0x65, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x50, - 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, - 0x44, 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, 0x09, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x30, 0x0a, 0x09, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x0b, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, 0x12, 0x4b, 0x0a, 0x0b, - 0x52, 0x75, 0x6e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x12, 0x16, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, - 0x61, 0x6d, 0x73, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, - 0x74, 0x79, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x10, 0x47, 0x65, 0x74, - 0x52, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1b, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, - 0x70, 0x6f, 0x72, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, - 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, - 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x50, - 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x20, 0x2e, - 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, - 0x65, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x00, 0x12, 0x5b, 0x0a, 0x14, 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x73, - 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, - 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, - 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x49, - 0x0a, 0x0e, 0x44, 0x75, 0x6d, 0x70, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x44, 0x69, 0x73, 0x63, 0x61, - 0x72, 0x64, 0x65, 0x72, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1a, 0x2e, 0x61, 0x70, - 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x0c, 0x44, 0x75, 0x6d, - 0x70, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x17, 0x2e, 0x61, 0x70, 0x69, 0x2e, - 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x50, 0x61, 0x72, 0x61, - 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, - 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x50, - 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, - 0x6d, 0x70, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, 0xeb, 0x03, 0x0a, + 0x0f, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x22, 0x0a, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x0b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x43, 0x6f, 0x6d, + 0x6d, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x41, 0x67, 0x65, 0x6e, 0x74, + 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x4b, + 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x11, + 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x44, 0x69, + 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x41, 0x72, + 0x63, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x12, + 0x0a, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x56, 0x65, + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x50, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x11, + 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x41, 0x72, 0x67, + 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x74, 0x65, 0x41, 0x72, 0x67, 0x73, 0x12, 0x16, 0x0a, 0x04, 0x43, 0x6f, + 0x6d, 0x6d, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x43, 0x6f, + 0x6d, 0x6d, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x12, 0x14, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x05, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x54, 0x69, + 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x04, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x24, 0x0a, 0x0d, 0x53, 0x65, 0x72, 0x69, + 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0d, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, + 0x0a, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, 0x79, 0x0a, 0x15, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, + 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, + 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x46, 0x69, 0x6c, 0x65, 0x22, 0xbe, 0x02, 0x0a, 0x13, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x48, 0x6f, 0x73, + 0x74, 0x12, 0x16, 0x0a, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x53, 0x65, 0x72, + 0x76, 0x69, 0x63, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x04, 0x54, 0x61, 0x67, 0x73, 0x12, 0x34, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, + 0x67, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, + 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x12, 0x30, 0x0a, + 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, + 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x44, 0x4e, 0x53, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x09, 0x52, 0x08, 0x44, 0x4e, 0x53, 0x4e, 0x61, 0x6d, 0x65, + 0x73, 0x12, 0x33, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, + 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x22, 0x18, 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, - 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, - 0x12, 0x4f, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, - 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, - 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, - 0x73, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, - 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, - 0x00, 0x12, 0x55, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, - 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, - 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, + 0x22, 0x5f, 0x0a, 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, + 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x44, + 0x75, 0x6d, 0x70, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0x6a, 0x0a, 0x16, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, + 0x70, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x4e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x44, 0x22, 0x2f, 0x0a, + 0x17, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, + 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7b, + 0x0a, 0x18, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x2a, 0x0a, 0x10, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, + 0x6d, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x33, 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, + 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, + 0x6d, 0x73, 0x52, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x22, 0x67, 0x0a, 0x19, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x34, + 0x0a, 0x07, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x53, 0x74, 0x6f, + 0x72, 0x61, 0x67, 0x65, 0x22, 0x1a, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, - 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, - 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x65, 0x63, 0x75, - 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x1e, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1f, 0x2e, 0x61, - 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, - 0x58, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, - 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x61, 0x76, 0x65, - 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, - 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x61, 0x76, 0x65, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x18, 0x5a, 0x16, 0x70, 0x6b, 0x67, - 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, - 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x5d, 0x0a, 0x19, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2c, 0x0a, + 0x04, 0x44, 0x75, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x04, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x12, 0x0a, 0x04, 0x44, + 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x44, 0x61, 0x74, 0x61, 0x22, + 0x3f, 0x0a, 0x17, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, 0x6c, 0x65, 0x63, + 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x4e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x10, + 0x0a, 0x03, 0x54, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x54, 0x61, 0x67, + 0x22, 0x87, 0x01, 0x0a, 0x1b, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x1c, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1c, + 0x0a, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x2c, 0x0a, 0x11, + 0x49, 0x73, 0x53, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x11, 0x49, 0x73, 0x53, 0x74, 0x61, 0x62, 0x6c, + 0x65, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x22, 0x47, 0x0a, 0x0f, 0x49, 0x6e, + 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x12, + 0x12, 0x0a, 0x04, 0x54, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x54, + 0x61, 0x67, 0x73, 0x22, 0xec, 0x01, 0x0a, 0x18, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, + 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x12, 0x2c, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, + 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x11, 0x50, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, + 0x0a, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0e, 0x46, 0x69, 0x6c, 0x65, 0x4e, 0x6f, 0x64, 0x65, + 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x44, 0x4e, 0x53, 0x4e, 0x6f, 0x64, + 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x44, + 0x4e, 0x53, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x2a, 0x0a, 0x10, + 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4e, 0x6f, 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x10, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4e, 0x6f, + 0x64, 0x65, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x0f, 0x41, 0x70, 0x70, 0x72, + 0x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x0f, 0x41, 0x70, 0x70, 0x72, 0x6f, 0x78, 0x69, 0x6d, 0x61, 0x74, 0x65, 0x53, 0x69, + 0x7a, 0x65, 0x22, 0x6e, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2a, 0x0a, 0x11, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x61, + 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, 0x5f, 0x6e, 0x61, 0x6e, 0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x0f, 0x6c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, 0x4e, 0x61, + 0x6e, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x66, + 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x11, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x22, 0x9b, 0x02, 0x0a, 0x15, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x66, 0x69, 0x72, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x09, 0x66, 0x69, 0x72, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6c, + 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, + 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x65, 0x6e, 0x12, 0x58, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x1a, 0x58, 0x0a, 0x13, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, + 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, + 0x2b, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0xa0, 0x06, 0x0a, 0x16, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x26, 0x0a, 0x0e, 0x4c, + 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, 0x72, + 0x6e, 0x65, 0x6c, 0x12, 0x38, 0x0a, 0x17, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, + 0x65, 0x72, 0x6e, 0x65, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x4c, 0x6f, 0x61, 0x64, 0x65, 0x64, 0x49, 0x6e, 0x4b, 0x65, + 0x72, 0x6e, 0x65, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x38, 0x0a, + 0x08, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x65, + 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x24, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x12, 0x1e, 0x0a, + 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0a, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x4a, 0x0a, + 0x0d, 0x4c, 0x61, 0x73, 0x74, 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x69, 0x65, 0x73, 0x18, 0x06, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x41, + 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x79, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x4c, 0x61, 0x73, 0x74, + 0x41, 0x6e, 0x6f, 0x6d, 0x61, 0x6c, 0x69, 0x65, 0x73, 0x12, 0x32, 0x0a, 0x09, 0x49, 0x6e, 0x73, + 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x52, 0x09, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x73, 0x12, 0x1a, 0x0a, + 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, + 0x01, 0x52, 0x06, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1c, 0x0a, 0x07, 0x56, 0x65, 0x72, + 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x07, + 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x30, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x69, 0x2e, + 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x0a, 0x04, 0x54, 0x61, 0x67, + 0x73, 0x18, 0x0b, 0x20, 0x03, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x04, 0x54, 0x61, 0x67, + 0x73, 0x12, 0x33, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x54, + 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x73, 0x12, 0x2e, 0x0a, 0x12, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, 0x0d, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x12, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x47, 0x6c, 0x6f, 0x62, 0x61, + 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x5b, 0x0a, 0x10, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, + 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x18, 0x0e, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x30, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x50, 0x72, + 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x0f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, + 0x78, 0x74, 0x73, 0x1a, 0x5e, 0x0a, 0x14, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, + 0x6e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x30, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, + 0x70, 0x69, 0x2e, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, + 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x3f, 0x0a, 0x19, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x12, 0x22, 0x0a, 0x0c, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, 0x61, 0x63, 0x68, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x49, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x22, 0x6b, 0x0a, 0x1a, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, + 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x52, 0x08, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x45, + 0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x22, 0x55, 0x0a, 0x19, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, + 0x66, 0x69, 0x6c, 0x65, 0x53, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x12, 0x38, + 0x0a, 0x08, 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x53, + 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x08, + 0x53, 0x65, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x22, 0x46, 0x0a, 0x1a, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x61, 0x76, 0x65, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, + 0x46, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46, 0x69, 0x6c, 0x65, + 0x32, 0x8a, 0x0a, 0x0a, 0x0e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x12, 0x13, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x57, 0x0a, 0x10, 0x44, 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x63, + 0x65, 0x73, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, + 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, 0x61, 0x63, 0x68, 0x65, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x24, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, + 0x72, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x43, + 0x61, 0x63, 0x68, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x3f, 0x0a, + 0x09, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x14, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x1a, 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x30, + 0x0a, 0x09, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x14, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x1a, 0x0b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22, 0x00, + 0x12, 0x4b, 0x0a, 0x0b, 0x52, 0x75, 0x6e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x12, + 0x16, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x75, 0x6e, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, + 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x22, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, + 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x53, 0x65, 0x6c, 0x66, 0x54, 0x65, 0x73, 0x74, 0x52, 0x65, + 0x73, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, + 0x10, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, + 0x74, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x53, + 0x65, 0x74, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x22, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x75, 0x6c, 0x65, 0x53, 0x65, 0x74, 0x52, + 0x65, 0x70, 0x6f, 0x72, 0x74, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x0e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6c, + 0x6f, 0x61, 0x64, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, + 0x73, 0x1a, 0x20, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x52, 0x65, 0x6c, 0x6f, 0x61, 0x64, 0x50, 0x6f, + 0x6c, 0x69, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x14, 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, + 0x77, 0x6f, 0x72, 0x6b, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1f, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x4e, + 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x20, + 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x12, 0x49, 0x0a, 0x0e, 0x44, 0x75, 0x6d, 0x70, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x12, 0x19, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x44, + 0x69, 0x73, 0x63, 0x61, 0x72, 0x64, 0x65, 0x72, 0x73, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, + 0x1a, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x44, 0x75, 0x6d, 0x70, 0x44, 0x69, 0x73, 0x63, 0x61, 0x72, + 0x64, 0x65, 0x72, 0x73, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, + 0x0c, 0x44, 0x75, 0x6d, 0x70, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x12, 0x17, 0x2e, + 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, + 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, + 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x22, 0x00, 0x12, 0x50, 0x0a, 0x11, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, + 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x73, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, + 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x22, 0x00, 0x12, 0x4f, 0x0a, 0x10, 0x53, 0x74, 0x6f, 0x70, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x12, 0x1b, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, + 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x50, + 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1c, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x6f, 0x70, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x55, 0x0a, 0x12, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, + 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x2e, 0x61, 0x70, + 0x69, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x12, 0x5a, 0x0a, 0x15, + 0x47, 0x65, 0x74, 0x41, 0x63, 0x74, 0x69, 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, + 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1d, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, + 0x76, 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, + 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x41, 0x63, 0x74, 0x69, 0x76, + 0x69, 0x74, 0x79, 0x44, 0x75, 0x6d, 0x70, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x30, 0x01, 0x12, 0x59, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, + 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, + 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, + 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x22, 0x00, 0x12, 0x58, 0x0a, 0x13, 0x53, 0x61, 0x76, 0x65, 0x53, 0x65, 0x63, 0x75, 0x72, + 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x1e, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x53, 0x61, 0x76, 0x65, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x73, 0x1a, 0x1f, 0x2e, 0x61, 0x70, 0x69, + 0x2e, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x53, 0x61, 0x76, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x00, 0x42, 0x18, 0x5a, + 0x16, 0x70, 0x6b, 0x67, 0x2f, 0x73, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2f, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/security/proto/api/api.proto b/pkg/security/proto/api/api.proto index dc3e16fecb4b2..7032834f238c3 100644 --- a/pkg/security/proto/api/api.proto +++ b/pkg/security/proto/api/api.proto @@ -147,6 +147,7 @@ message ActivityDumpParams { bool DifferentiateArgs = 2; StorageRequestParams Storage = 3; string ContainerID = 4; + string CGroupID = 5; } message MetadataMessage { @@ -165,6 +166,7 @@ message MetadataMessage { string Timeout = 12; uint64 Size = 13; string Serialization = 14; + string CGroupID = 15; } message StorageRequestMessage { @@ -196,6 +198,7 @@ message ActivityDumpListMessage { message ActivityDumpStopParams { string Name = 1; string ContainerID = 2; + string CGroupID = 3; } message ActivityDumpStopMessage { diff --git a/pkg/security/proto/api/api_vtproto.pb.go b/pkg/security/proto/api/api_vtproto.pb.go index 462ad4782938c..8ebccda5994a0 100644 --- a/pkg/security/proto/api/api_vtproto.pb.go +++ b/pkg/security/proto/api/api_vtproto.pb.go @@ -1468,6 +1468,13 @@ func (m *ActivityDumpParams) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CGroupID) > 0 { + i -= len(m.CGroupID) + copy(dAtA[i:], m.CGroupID) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CGroupID))) + i-- + dAtA[i] = 0x2a + } if len(m.ContainerID) > 0 { i -= len(m.ContainerID) copy(dAtA[i:], m.ContainerID) @@ -1535,6 +1542,13 @@ func (m *MetadataMessage) MarshalToSizedBufferVT(dAtA []byte) (int, error) { i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CGroupID) > 0 { + i -= len(m.CGroupID) + copy(dAtA[i:], m.CGroupID) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CGroupID))) + i-- + dAtA[i] = 0x7a + } if len(m.Serialization) > 0 { i -= len(m.Serialization) copy(dAtA[i:], m.Serialization) @@ -1927,6 +1941,13 @@ func (m *ActivityDumpStopParams) MarshalToSizedBufferVT(dAtA []byte) (int, error i -= len(m.unknownFields) copy(dAtA[i:], m.unknownFields) } + if len(m.CGroupID) > 0 { + i -= len(m.CGroupID) + copy(dAtA[i:], m.CGroupID) + i = protohelpers.EncodeVarint(dAtA, i, uint64(len(m.CGroupID))) + i-- + dAtA[i] = 0x1a + } if len(m.ContainerID) > 0 { i -= len(m.ContainerID) copy(dAtA[i:], m.ContainerID) @@ -3396,6 +3417,10 @@ func (m *ActivityDumpParams) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.CGroupID) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -3460,6 +3485,10 @@ func (m *MetadataMessage) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.CGroupID) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -3585,6 +3614,10 @@ func (m *ActivityDumpStopParams) SizeVT() (n int) { if l > 0 { n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) } + l = len(m.CGroupID) + if l > 0 { + n += 1 + l + protohelpers.SizeOfVarint(uint64(l)) + } n += len(m.unknownFields) return n } @@ -7212,6 +7245,38 @@ func (m *ActivityDumpParams) UnmarshalVT(dAtA []byte) error { } m.ContainerID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CGroupID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CGroupID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -7686,6 +7751,38 @@ func (m *MetadataMessage) UnmarshalVT(dAtA []byte) error { } m.Serialization = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 15: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CGroupID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CGroupID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) @@ -8485,6 +8582,38 @@ func (m *ActivityDumpStopParams) UnmarshalVT(dAtA []byte) error { } m.ContainerID = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field CGroupID", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return protohelpers.ErrIntOverflow + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return protohelpers.ErrInvalidLength + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return protohelpers.ErrInvalidLength + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.CGroupID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := protohelpers.Skip(dAtA[iNdEx:]) diff --git a/pkg/security/resolvers/resolvers_ebpf.go b/pkg/security/resolvers/resolvers_ebpf.go index 91e1392502840..b8899bbd99adf 100644 --- a/pkg/security/resolvers/resolvers_ebpf.go +++ b/pkg/security/resolvers/resolvers_ebpf.go @@ -217,35 +217,25 @@ func (r *EBPFResolvers) Start(ctx context.Context) error { return r.NamespaceResolver.Start(ctx) } -// ResolveCGroup resolves the path of cgroup for a process cache entry -func (r *EBPFResolvers) ResolveCGroup(pce *model.ProcessCacheEntry, pathKey model.PathKey, cgroupFlags containerutils.CGroupFlags) error { +// ResolveCGroupContext resolves the cgroup context from a cgroup path key +func (r *EBPFResolvers) ResolveCGroupContext(pathKey model.PathKey, cgroupFlags containerutils.CGroupFlags) (*model.CGroupContext, error) { path, err := r.DentryResolver.Resolve(pathKey, true) - if err == nil && path != "" { - cgroup := filepath.Dir(string(path)) - if cgroup == "/" { - cgroup = path - } - - cgroupFlags := containerutils.CGroupFlags(cgroupFlags) - cgroupContext := model.CGroupContext{ - CGroupID: containerutils.CGroupID(cgroup), - CGroupFlags: containerutils.CGroupFlags(cgroupFlags), - CGroupFile: pathKey, - } + if err != nil { + return nil, fmt.Errorf("failed to resolve cgroup file %v: %w", pathKey, err) + } - pce.Process.CGroup = cgroupContext - pce.CGroup = cgroupContext + cgroup := filepath.Dir(string(path)) + if cgroup == "/" { + cgroup = path + } - if cgroupFlags.IsContainer() { - containerID, _ := containerutils.FindContainerID(cgroupContext.CGroupID) - pce.ContainerID = containerID - pce.Process.ContainerID = containerID - } - } else { - return fmt.Errorf("failed to resolve cgroup file %v: %w", pathKey, err) + cgroupContext := &model.CGroupContext{ + CGroupID: containerutils.CGroupID(cgroup), + CGroupFlags: containerutils.CGroupFlags(cgroupFlags), + CGroupFile: pathKey, } - return nil + return cgroupContext, nil } // Snapshot collects data on the current state of the system to populate user space and kernel space caches. diff --git a/pkg/security/secl/model/unmarshallers_linux.go b/pkg/security/secl/model/unmarshallers_linux.go index d6bdfb32fa825..7c38d3127e1ba 100644 --- a/pkg/security/secl/model/unmarshallers_linux.go +++ b/pkg/security/secl/model/unmarshallers_linux.go @@ -972,7 +972,7 @@ func (e *CgroupTracingEvent) UnmarshalBinary(data []byte) (int, error) { } cursor := read - read, err = UnmarshalBinary(data, &e.CGroupContext) + read, err = UnmarshalBinary(data[cursor:], &e.CGroupContext) if err != nil { return 0, err } diff --git a/pkg/security/security_profile/activity_tree/metadata/metadata.go b/pkg/security/security_profile/activity_tree/metadata/metadata.go index 2e9ea31a3da09..882e7fb69f5c0 100644 --- a/pkg/security/security_profile/activity_tree/metadata/metadata.go +++ b/pkg/security/security_profile/activity_tree/metadata/metadata.go @@ -8,7 +8,12 @@ // Package metadata holds metadata related files package metadata -import "time" +import ( + "time" + + "github.com/DataDog/datadog-agent/pkg/security/secl/containerutils" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" +) // Metadata is used to provide context about the activity dump or the profile type Metadata struct { @@ -18,13 +23,13 @@ type Metadata struct { LinuxDistribution string `json:"linux_distribution"` Arch string `json:"arch"` - Name string `json:"name"` - ProtobufVersion string `json:"protobuf_version"` - DifferentiateArgs bool `json:"differentiate_args"` - ContainerID string `json:"-"` - ContainerFlags uint64 `json:"-"` - Start time.Time `json:"start"` - End time.Time `json:"end"` - Size uint64 `json:"activity_dump_size,omitempty"` - Serialization string `json:"serialization,omitempty"` + Name string `json:"name"` + ProtobufVersion string `json:"protobuf_version"` + DifferentiateArgs bool `json:"differentiate_args"` + ContainerID containerutils.ContainerID `json:"-"` + CGroupContext model.CGroupContext `json:"-"` + Start time.Time `json:"start"` + End time.Time `json:"end"` + Size uint64 `json:"activity_dump_size,omitempty"` + Serialization string `json:"serialization,omitempty"` } diff --git a/pkg/security/security_profile/activity_tree/metadata/metadata_proto_dec_v1.go b/pkg/security/security_profile/activity_tree/metadata/metadata_proto_dec_v1.go index 6f1f8ad0588f1..e08f483efac61 100644 --- a/pkg/security/security_profile/activity_tree/metadata/metadata_proto_dec_v1.go +++ b/pkg/security/security_profile/activity_tree/metadata/metadata_proto_dec_v1.go @@ -11,6 +11,7 @@ package metadata import ( adproto "github.com/DataDog/agent-payload/v5/cws/dumpsv1" + "github.com/DataDog/datadog-agent/pkg/security/secl/containerutils" activity_tree "github.com/DataDog/datadog-agent/pkg/security/security_profile/activity_tree" ) @@ -30,7 +31,7 @@ func ProtoMetadataToMetadata(meta *adproto.Metadata) Metadata { Name: meta.Name, ProtobufVersion: meta.ProtobufVersion, DifferentiateArgs: meta.DifferentiateArgs, - ContainerID: meta.ContainerId, + ContainerID: containerutils.ContainerID(meta.ContainerId), Start: activity_tree.ProtoDecodeTimestamp(meta.Start), End: activity_tree.ProtoDecodeTimestamp(meta.End), Size: meta.Size, diff --git a/pkg/security/security_profile/activity_tree/metadata/metadata_proto_enc_v1.go b/pkg/security/security_profile/activity_tree/metadata/metadata_proto_enc_v1.go index 2b02ef4004720..e28b2ce901c63 100644 --- a/pkg/security/security_profile/activity_tree/metadata/metadata_proto_enc_v1.go +++ b/pkg/security/security_profile/activity_tree/metadata/metadata_proto_enc_v1.go @@ -29,7 +29,7 @@ func ToProto(meta *Metadata) *adproto.Metadata { Name: meta.Name, ProtobufVersion: meta.ProtobufVersion, DifferentiateArgs: meta.DifferentiateArgs, - ContainerId: meta.ContainerID, + ContainerId: string(meta.ContainerID), Start: activity_tree.TimestampToProto(&meta.Start), End: activity_tree.TimestampToProto(&meta.End), Size: meta.Size, diff --git a/pkg/security/security_profile/dump/activity_dump.go b/pkg/security/security_profile/dump/activity_dump.go index 80d1bb2ebe011..4df6a218cebe3 100644 --- a/pkg/security/security_profile/dump/activity_dump.go +++ b/pkg/security/security_profile/dump/activity_dump.go @@ -222,11 +222,14 @@ func NewActivityDumpFromMessage(msg *api.ActivityDumpMessage) (*ActivityDump, er Name: metadata.GetName(), ProtobufVersion: metadata.GetProtobufVersion(), DifferentiateArgs: metadata.GetDifferentiateArgs(), - ContainerID: metadata.GetContainerID(), - Start: startTime, - End: startTime.Add(timeout), - Size: metadata.GetSize(), - Arch: metadata.GetArch(), + ContainerID: containerutils.ContainerID(metadata.GetContainerID()), + CGroupContext: model.CGroupContext{ + CGroupID: containerutils.CGroupID(metadata.GetCGroupID()), + }, + Start: startTime, + End: startTime.Add(timeout), + Size: metadata.GetSize(), + Arch: metadata.GetArch(), } ad.LoadConfig = NewActivityDumpLoadConfig( []model.EventType{}, @@ -265,14 +268,26 @@ func (ad *ActivityDump) GetWorkloadSelector() *cgroupModel.WorkloadSelector { if ad.selector != nil && ad.selector.IsReady() { return ad.selector } - imageTag := utils.GetTagValue("image_tag", ad.Tags) - selector, err := cgroupModel.NewWorkloadSelector(utils.GetTagValue("image_name", ad.Tags), imageTag) - if err != nil { - return nil + + var selector cgroupModel.WorkloadSelector + var err error + if ad.ContainerID != "" { + imageTag := utils.GetTagValue("image_tag", ad.Tags) + selector, err = cgroupModel.NewWorkloadSelector(utils.GetTagValue("image_name", ad.Tags), imageTag) + if err != nil { + return nil + } + } else if ad.CGroupContext.CGroupID != "" { + selector, err = cgroupModel.NewWorkloadSelector(utils.GetTagValue("service", ad.Tags), utils.GetTagValue("version", ad.Tags)) + if err != nil { + return nil + } + } + ad.selector = &selector - // Once per workload, when tags are resolved and the firs time we successfully get the selector, tag all the existing nodes - ad.ActivityTree.TagAllNodes(imageTag) + // Once per workload, when tags are resolved and the first time we successfully get the selector, tag all the existing nodes + ad.ActivityTree.TagAllNodes(selector.Image) return ad.selector } @@ -354,7 +369,7 @@ func (ad *ActivityDump) nameMatches(name string) bool { } // containerIDMatches returns true if the ActivityDump container ID matches the provided container ID -func (ad *ActivityDump) containerIDMatches(containerID string) bool { +func (ad *ActivityDump) containerIDMatches(containerID containerutils.ContainerID) bool { return ad.Metadata.ContainerID == containerID } @@ -365,7 +380,13 @@ func (ad *ActivityDump) MatchesSelector(entry *model.ProcessCacheEntry) bool { } if len(ad.Metadata.ContainerID) > 0 { - if !ad.containerIDMatches(string(entry.ContainerID)) { + if !ad.containerIDMatches(entry.ContainerID) { + return false + } + } + + if len(ad.Metadata.CGroupContext.CGroupID) > 0 { + if entry.CGroup.CGroupID != ad.Metadata.CGroupContext.CGroupID { return false } } @@ -395,13 +416,13 @@ func (ad *ActivityDump) enable() error { } } - if len(ad.Metadata.ContainerID) > 0 { + if !ad.Metadata.CGroupContext.CGroupFile.IsNull() { // insert container ID in traced_cgroups map (it might already exist, do not update in that case) - if err := ad.adm.tracedCgroupsMap.Update(ad.Metadata.ContainerID, ad.LoadConfigCookie, ebpf.UpdateNoExist); err != nil { + if err := ad.adm.tracedCgroupsMap.Update(ad.Metadata.CGroupContext.CGroupFile, ad.LoadConfigCookie, ebpf.UpdateNoExist); err != nil { if !errors.Is(err, ebpf.ErrKeyExist) { // delete activity dump load config _ = ad.adm.activityDumpsConfigMap.Delete(ad.LoadConfigCookie) - return fmt.Errorf("couldn't push activity dump container ID %s: %w", ad.Metadata.ContainerID, err) + return fmt.Errorf("couldn't push activity dump cgroup ID %s: %w", ad.Metadata.CGroupContext.CGroupID, err) } } } @@ -448,12 +469,10 @@ func (ad *ActivityDump) disable() error { } // remove container ID from kernel space - if len(ad.Metadata.ContainerID) > 0 { - containerIDB := make([]byte, model.ContainerIDLen) - copy(containerIDB, ad.Metadata.ContainerID) - err := ad.adm.tracedCgroupsMap.Delete(containerIDB) + if !ad.Metadata.CGroupContext.CGroupFile.IsNull() { + err := ad.adm.tracedCgroupsMap.Delete(ad.Metadata.CGroupContext.CGroupFile) if err != nil && !errors.Is(err, ebpf.ErrKeyNotExist) { - return fmt.Errorf("couldn't delete activity dump filter containerID(%s): %v", ad.Metadata.ContainerID, err) + return fmt.Errorf("couldn't delete activity dump filter cgroup %s: %v", ad.Metadata.CGroupContext.CGroupID, err) } } return nil @@ -575,6 +594,9 @@ func (ad *ActivityDump) getSelectorStr() string { if len(ad.Metadata.ContainerID) > 0 { tags = append(tags, fmt.Sprintf("container_id:%s", ad.Metadata.ContainerID)) } + if len(ad.Metadata.CGroupContext.CGroupID) > 0 { + tags = append(tags, fmt.Sprintf("cgroup_id:%s", ad.Metadata.CGroupContext.CGroupID)) + } if len(ad.Tags) > 0 { for _, tag := range ad.Tags { if !strings.HasPrefix(tag, "container_id") { @@ -614,6 +636,8 @@ func (ad *ActivityDump) ResolveTags() error { return ad.resolveTags() } +const systemdSystemDir = "/usr/lib/systemd/system" + // resolveTags thread unsafe version ot ResolveTags func (ad *ActivityDump) resolveTags() error { selector := ad.GetWorkloadSelector() @@ -621,10 +645,26 @@ func (ad *ActivityDump) resolveTags() error { return nil } - var err error - ad.Tags, err = ad.adm.resolvers.TagsResolver.ResolveWithErr(containerutils.ContainerID(ad.Metadata.ContainerID)) - if err != nil { - return fmt.Errorf("failed to resolve %s: %w", ad.Metadata.ContainerID, err) + if len(ad.Metadata.ContainerID) > 0 { + var err error + if ad.Tags, err = ad.adm.resolvers.TagsResolver.ResolveWithErr(containerutils.ContainerID(ad.Metadata.ContainerID)); err != nil { + return fmt.Errorf("failed to resolve %s: %w", ad.Metadata.ContainerID, err) + } + } else if len(ad.Metadata.CGroupContext.CGroupID) > 0 { + systemdService := filepath.Base(string(ad.Metadata.CGroupContext.CGroupID)) + serviceVersion := "" + servicePath := filepath.Join(systemdSystemDir, systemdService) + + if ad.adm.resolvers.SBOMResolver != nil { + if pkg := ad.adm.resolvers.SBOMResolver.ResolvePackage("", &model.FileEvent{PathnameStr: servicePath}); pkg != nil { + serviceVersion = pkg.Version + } + } + + ad.Tags = []string{ + "service:" + systemdService, + "version:" + serviceVersion, + } } return nil @@ -655,7 +695,8 @@ func (ad *ActivityDump) ToSecurityActivityDumpMessage() *api.ActivityDumpMessage Name: ad.Metadata.Name, ProtobufVersion: ad.Metadata.ProtobufVersion, DifferentiateArgs: ad.Metadata.DifferentiateArgs, - ContainerID: ad.Metadata.ContainerID, + ContainerID: string(ad.Metadata.ContainerID), + CGroupID: string(ad.Metadata.CGroupContext.CGroupID), Start: ad.Metadata.Start.Format(time.RFC822), Timeout: ad.LoadConfig.Timeout.String(), Size: ad.Metadata.Size, diff --git a/pkg/security/security_profile/dump/load_controller.go b/pkg/security/security_profile/dump/load_controller.go index 3d0148f168232..ae8767bcfd153 100644 --- a/pkg/security/security_profile/dump/load_controller.go +++ b/pkg/security/security_profile/dump/load_controller.go @@ -85,6 +85,7 @@ func (lc *ActivityDumpLoadController) PushCurrentConfig() error { func (lc *ActivityDumpLoadController) NextPartialDump(ad *ActivityDump) *ActivityDump { newDump := NewActivityDump(ad.adm) newDump.Metadata.ContainerID = ad.Metadata.ContainerID + newDump.Metadata.CGroupContext = ad.Metadata.CGroupContext newDump.Metadata.DifferentiateArgs = ad.Metadata.DifferentiateArgs newDump.Tags = ad.Tags newDump.selector = ad.selector diff --git a/pkg/security/security_profile/dump/manager.go b/pkg/security/security_profile/dump/manager.go index b37a2c7264f1a..d06600ed81bca 100644 --- a/pkg/security/security_profile/dump/manager.go +++ b/pkg/security/security_profile/dump/manager.go @@ -35,6 +35,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/resolvers" cgroupModel "github.com/DataDog/datadog-agent/pkg/security/resolvers/cgroup/model" "github.com/DataDog/datadog-agent/pkg/security/resolvers/tags" + "github.com/DataDog/datadog-agent/pkg/security/secl/containerutils" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/seclog" activity_tree "github.com/DataDog/datadog-agent/pkg/security/security_profile/activity_tree" @@ -70,7 +71,7 @@ type ActivityDumpManager struct { tracedCgroupsMap *ebpf.Map cgroupWaitList *ebpf.Map activityDumpsConfigMap *ebpf.Map - ignoreFromSnapshot map[string]bool + ignoreFromSnapshot map[model.PathKey]bool dumpLimiter *lru.Cache[cgroupModel.WorkloadSelector, *atomic.Uint64] workloadDenyList []cgroupModel.WorkloadSelector @@ -148,13 +149,13 @@ func (adm *ActivityDumpManager) cleanup() { // cleanup cgroup_wait_list map iterator := adm.cgroupWaitList.Iterate() - containerIDB := make([]byte, model.ContainerIDLen) + cgroupFile := make([]byte, model.PathKeySize) var timestamp uint64 - for iterator.Next(&containerIDB, ×tamp) { + for iterator.Next(&cgroupFile, ×tamp) { if time.Now().After(adm.resolvers.TimeResolver.ResolveMonotonicTimestamp(timestamp)) { - if err := adm.cgroupWaitList.Delete(&containerIDB); err != nil { - seclog.Errorf("couldn't delete cgroup_wait_list entry for (%s): %v", string(containerIDB), err) + if err := adm.cgroupWaitList.Delete(&cgroupFile); err != nil { + seclog.Errorf("couldn't delete cgroup_wait_list entry for (%v): %v", cgroupFile, err) } } } @@ -170,7 +171,7 @@ func (adm *ActivityDumpManager) getExpiredDumps() []*ActivityDump { for _, ad := range adm.activeDumps { if time.Now().After(ad.Metadata.End) || ad.state == Stopped { expiredDumps = append(expiredDumps, ad) - delete(adm.ignoreFromSnapshot, ad.Metadata.ContainerID) + delete(adm.ignoreFromSnapshot, ad.Metadata.CGroupContext.CGroupFile) } else { newDumps = append(newDumps, ad) } @@ -304,7 +305,7 @@ func NewActivityDumpManager(config *config.Config, statsdClient statsd.ClientInt cgroupWaitList: cgroupWaitList, activityDumpsConfigMap: activityDumpsConfigMap, snapshotQueue: make(chan *ActivityDump, 100), - ignoreFromSnapshot: make(map[string]bool), + ignoreFromSnapshot: make(map[model.PathKey]bool), dumpLimiter: limiter, workloadDenyList: denyList, workloadDenyListHits: atomic.NewUint64(0), @@ -360,19 +361,35 @@ func (adm *ActivityDumpManager) insertActivityDump(newDump *ActivityDump) error for _, ad := range adm.activeDumps { if ad.Metadata.ContainerID == newDump.Metadata.ContainerID { // an activity dump is already active for this container ID, ignore - return nil + return fmt.Errorf("dump for container %s already running", ad.Metadata.ContainerID) } } } - // enable the new dump to start collecting events from kernel space - if err := newDump.enable(); err != nil { - return fmt.Errorf("couldn't insert new dump: %w", err) + if len(newDump.Metadata.CGroupContext.CGroupID) > 0 { + // check if the provided container ID is new + for _, ad := range adm.activeDumps { + if ad.Metadata.CGroupContext.CGroupID == newDump.Metadata.CGroupContext.CGroupID { + // an activity dump is already active for this container ID, ignore + return fmt.Errorf("dump for cgroup %s already running", ad.Metadata.CGroupContext.CGroupID) + } + } } // loop through the process cache entry tree and push traced pids if necessary pces := adm.newProcessCacheEntrySearcher(newDump) - adm.resolvers.ProcessResolver.Walk(pces.SearchTracedProcessCacheEntry) + adm.resolvers.ProcessResolver.Walk(func(entry *model.ProcessCacheEntry) { + if !pces.ad.MatchesSelector(entry) { + return + } + pces.ad.Metadata.CGroupContext = entry.CGroup + pces.SearchTracedProcessCacheEntry(entry) + }) + + // enable the new dump to start collecting events from kernel space + if err := newDump.enable(); err != nil { + return fmt.Errorf("couldn't insert new dump: %w", err) + } // Delay the activity dump snapshot to reduce the overhead on the main goroutine select { @@ -391,10 +408,10 @@ func (adm *ActivityDumpManager) insertActivityDump(newDump *ActivityDump) error } // handleDefaultDumpRequest starts dumping a new workload with the provided load configuration and the default dump configuration -func (adm *ActivityDumpManager) startDumpWithConfig(containerID string, containerFlags, cookie uint64, loadConfig model.ActivityDumpLoadConfig) error { +func (adm *ActivityDumpManager) startDumpWithConfig(containerID containerutils.ContainerID, cgroupContext model.CGroupContext, cookie uint64, loadConfig model.ActivityDumpLoadConfig) error { newDump := NewActivityDump(adm, func(ad *ActivityDump) { ad.Metadata.ContainerID = containerID - ad.Metadata.ContainerFlags = containerFlags + ad.Metadata.CGroupContext = cgroupContext ad.SetLoadConfig(cookie, loadConfig) if adm.config.RuntimeSecurity.ActivityDumpCgroupDifferentiateArgs { @@ -437,7 +454,7 @@ func (adm *ActivityDumpManager) HandleCGroupTracingEvent(event *model.CgroupTrac return } - if err := adm.startDumpWithConfig(string(event.ContainerContext.ContainerID), uint64(event.CGroupContext.CGroupFlags), event.ConfigCookie, event.Config); err != nil { + if err := adm.startDumpWithConfig(event.ContainerContext.ContainerID, event.CGroupContext, event.ConfigCookie, event.Config); err != nil { seclog.Warnf("%v", err) } } @@ -497,7 +514,7 @@ workloadLoop: } // if we're still here, we can start tracing this workload - if err := adm.startDumpWithConfig(string(workloads[0].ContainerID), uint64(workloads[0].CGroupFlags), utils.NewCookie(), *adm.loadController.getDefaultLoadConfig()); err != nil { + if err := adm.startDumpWithConfig(workloads[0].ContainerID, workloads[0].CGroupContext, utils.NewCookie(), *adm.loadController.getDefaultLoadConfig()); err != nil { if !errors.Is(err, unix.E2BIG) { seclog.Debugf("%v", err) break @@ -523,11 +540,18 @@ func (adm *ActivityDumpManager) ListActivityDumps(_ *api.ActivityDumpListParams) // DumpActivity handles an activity dump request func (adm *ActivityDumpManager) DumpActivity(params *api.ActivityDumpParams) (*api.ActivityDumpMessage, error) { + if params.GetContainerID() == "" && params.GetCGroupID() == "" { + errMsg := fmt.Errorf("you must specify one selector between containerID and cgroupID") + return &api.ActivityDumpMessage{Error: errMsg.Error()}, errMsg + } + adm.Lock() defer adm.Unlock() newDump := NewActivityDump(adm, func(ad *ActivityDump) { - ad.Metadata.ContainerID = params.GetContainerID() + ad.Metadata.ContainerID = containerutils.ContainerID(params.GetContainerID()) + ad.Metadata.CGroupContext.CGroupID = containerutils.CGroupID(params.GetCGroupID()) + dumpDuration, _ := time.ParseDuration(params.Timeout) ad.SetTimeout(dumpDuration) @@ -560,15 +584,16 @@ func (adm *ActivityDumpManager) StopActivityDump(params *api.ActivityDumpStopPar adm.Lock() defer adm.Unlock() - if params.GetName() == "" && params.GetContainerID() == "" { - errMsg := fmt.Errorf("you must specify one selector between name and containerID") + if params.GetName() == "" && params.GetContainerID() == "" && params.GetCGroupID() == "" { + errMsg := fmt.Errorf("you must specify one selector between name, containerID and cgroupID") return &api.ActivityDumpStopMessage{Error: errMsg.Error()}, errMsg } toDelete := -1 for i, d := range adm.activeDumps { if (params.GetName() != "" && d.nameMatches(params.GetName())) || - (params.GetContainerID() != "" && d.containerIDMatches(params.GetContainerID())) { + (params.GetContainerID() != "" && d.containerIDMatches(containerutils.ContainerID(params.GetContainerID())) || + (params.GetCGroupID() != "" && string(d.Metadata.CGroupContext.CGroupID) == params.GetCGroupID())) { d.Finalize(true) seclog.Infof("tracing stopped for [%s]", d.GetSelectorStr()) toDelete = i @@ -593,8 +618,10 @@ func (adm *ActivityDumpManager) StopActivityDump(params *api.ActivityDumpStopPar var errMsg error if params.GetName() != "" { errMsg = fmt.Errorf("the activity dump manager does not contain any ActivityDump with the following name: %s", params.GetName()) - } else /* if params.GetContainerID() != "" */ { + } else if params.GetContainerID() != "" { errMsg = fmt.Errorf("the activity dump manager does not contain any ActivityDump with the following containerID: %s", params.GetContainerID()) + } else /* if params.GetCGroupID() != "" */ { + errMsg = fmt.Errorf("the activity dump manager does not contain any ActivityDump with the following cgroup ID: %s", params.GetCGroupID()) } return &api.ActivityDumpStopMessage{Error: errMsg.Error()}, errMsg } @@ -786,13 +813,14 @@ func (adm *ActivityDumpManager) SendStats() error { func (adm *ActivityDumpManager) SnapshotTracedCgroups() { var err error var event model.CgroupTracingEvent - containerIDB := make([]byte, model.ContainerIDLen) + var cgroupFile model.PathKey + iterator := adm.tracedCgroupsMap.Iterate() seclog.Infof("snapshotting traced_cgroups map") - for iterator.Next(&containerIDB, &event.ConfigCookie) { + for iterator.Next(&cgroupFile, &event.ConfigCookie) { adm.Lock() - if adm.ignoreFromSnapshot[string(containerIDB)] { + if adm.ignoreFromSnapshot[cgroupFile] { adm.Unlock() continue } @@ -800,15 +828,8 @@ func (adm *ActivityDumpManager) SnapshotTracedCgroups() { if err = adm.activityDumpsConfigMap.Lookup(&event.ConfigCookie, &event.Config); err != nil { // this config doesn't exist anymore, remove expired entries - seclog.Errorf("config not found for (%s): %v", string(containerIDB), err) - _ = adm.tracedCgroupsMap.Delete(containerIDB) - continue - } - - if _, err = event.ContainerContext.UnmarshalBinary(containerIDB[:]); err != nil { - seclog.Errorf("couldn't unmarshal container ID from traced_cgroups key: %v", err) - // remove invalid entry - _ = adm.tracedCgroupsMap.Delete(containerIDB) + seclog.Errorf("config not found for (%v): %v", cgroupFile, err) + _ = adm.tracedCgroupsMap.Delete(cgroupFile) continue } @@ -879,7 +900,7 @@ func (adm *ActivityDumpManager) triggerLoadController() { } // remove container ID from the map of ignored container IDs for the snapshot - delete(adm.ignoreFromSnapshot, ad.Metadata.ContainerID) + delete(adm.ignoreFromSnapshot, ad.Metadata.CGroupContext.CGroupFile) adm.Unlock() } } @@ -902,7 +923,7 @@ func (adm *ActivityDumpManager) getOverweightDumps() []*ActivityDump { if dumpSize >= int64(adm.config.RuntimeSecurity.ActivityDumpMaxDumpSize()) { toDelete = append([]int{i}, toDelete...) dumps = append(dumps, ad) - adm.ignoreFromSnapshot[ad.Metadata.ContainerID] = true + adm.ignoreFromSnapshot[ad.Metadata.CGroupContext.CGroupFile] = true } } for _, i := range toDelete { diff --git a/pkg/security/security_profile/dump/manager_test.go b/pkg/security/security_profile/dump/manager_test.go index f16d599230fd6..d7075b4d62f42 100644 --- a/pkg/security/security_profile/dump/manager_test.go +++ b/pkg/security/security_profile/dump/manager_test.go @@ -17,6 +17,7 @@ import ( "github.com/DataDog/datadog-go/v5/statsd" "github.com/DataDog/datadog-agent/pkg/security/config" + "github.com/DataDog/datadog-agent/pkg/security/secl/model" activity_tree "github.com/DataDog/datadog-agent/pkg/security/security_profile/activity_tree" mtdt "github.com/DataDog/datadog-agent/pkg/security/security_profile/activity_tree/metadata" ) @@ -197,7 +198,7 @@ func TestActivityDumpManager_getExpiredDumps(t *testing.T) { adm := &ActivityDumpManager{ activeDumps: tt.fields.activeDumps, - ignoreFromSnapshot: make(map[string]bool), + ignoreFromSnapshot: make(map[model.PathKey]bool), } expiredDumps := adm.getExpiredDumps() @@ -474,7 +475,7 @@ func TestActivityDumpManager_getOverweightDumps(t *testing.T) { }, }, statsdClient: &statsd.NoOpClient{}, - ignoreFromSnapshot: make(map[string]bool), + ignoreFromSnapshot: make(map[model.PathKey]bool), } compareListOfDumps(t, adm.getOverweightDumps(), tt.overweightDumps) diff --git a/pkg/security/security_profile/tests/activity_tree_test.go b/pkg/security/security_profile/tests/activity_tree_test.go index a814932f385fd..7f100d75c18d1 100644 --- a/pkg/security/security_profile/tests/activity_tree_test.go +++ b/pkg/security/security_profile/tests/activity_tree_test.go @@ -687,7 +687,7 @@ func TestActivityTree_CreateProcessNode(t *testing.T) { if tt == dumpTree { dump := dump.NewEmptyActivityDump(nil) - dump.Metadata.ContainerID = contID + dump.Metadata.ContainerID = containerutils.ContainerID(contID) at = dump.ActivityTree } else /* profileTree */ { profile := profile.NewSecurityProfile(cgroupModel.WorkloadSelector{Image: "image", Tag: "tag"}, []model.EventType{model.ExecEventType, model.DNSEventType}, nil) diff --git a/pkg/security/tests/activity_dumps_test.go b/pkg/security/tests/activity_dumps_test.go index 0046251a969bd..07189c9a8d2ba 100644 --- a/pkg/security/tests/activity_dumps_test.go +++ b/pkg/security/tests/activity_dumps_test.go @@ -93,7 +93,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -134,7 +134,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -211,7 +211,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -256,7 +256,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -295,7 +295,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -338,7 +338,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -394,7 +394,7 @@ func TestActivityDumps(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } diff --git a/pkg/security/tests/cmdwrapper.go b/pkg/security/tests/cmdwrapper.go index 1d25a6b4a7664..ca91fb42dd9ec 100644 --- a/pkg/security/tests/cmdwrapper.go +++ b/pkg/security/tests/cmdwrapper.go @@ -11,6 +11,7 @@ package tests import ( "fmt" "os/exec" + "strconv" "strings" "testing" @@ -78,8 +79,10 @@ type dockerCmdWrapper struct { executable string mountSrc string mountDest string + pid int64 containerName string containerID string + cgroupID string image string } @@ -101,9 +104,24 @@ func (d *dockerCmdWrapper) start() ([]byte, error) { d.containerName = fmt.Sprintf("docker-wrapper-%s", utils.RandString(6)) cmd := exec.Command(d.executable, "run", "--cap-add=SYS_PTRACE", "--security-opt", "seccomp=unconfined", "--rm", "--cap-add", "NET_ADMIN", "-d", "--name", d.containerName, "-v", d.mountSrc+":"+d.mountDest, d.image, "sleep", "1200") out, err := cmd.CombinedOutput() - if err == nil { - d.containerID = strings.TrimSpace(string(out)) + if err != nil { + return nil, err + } + + d.containerID = strings.TrimSpace(string(out)) + + cmd = exec.Command(d.executable, "inspect", "--format", "{{ .State.Pid }}", d.containerID) + out, err = cmd.CombinedOutput() + if err != nil { + return nil, err + } + + d.pid, _ = strconv.ParseInt(strings.TrimSpace(string(out)), 10, 64) + + if d.cgroupID, err = getPIDCGroup(uint32(d.pid)); err != nil { + return nil, err } + return out, err } diff --git a/pkg/security/tests/main_linux.go b/pkg/security/tests/main_linux.go index c64b83fbc917d..bea7537bf4f91 100644 --- a/pkg/security/tests/main_linux.go +++ b/pkg/security/tests/main_linux.go @@ -129,6 +129,20 @@ func SkipIfNotAvailable(t *testing.T) { } } +// getPIDCGroup returns the path of the first cgroup found for a PID +func getPIDCGroup(pid uint32) (string, error) { + cgroups, err := utils.GetProcControlGroups(pid, pid) + if err != nil { + return "", err + } + + if len(cgroups) == 0 { + return "", fmt.Errorf("failed to find cgroup for pid %d", pid) + } + + return cgroups[0].Path, nil +} + func preTestsHook() { if trace { args := slices.DeleteFunc(os.Args, func(arg string) bool { diff --git a/pkg/security/tests/main_windows.go b/pkg/security/tests/main_windows.go index 945126ae6c59c..4cc1f88754218 100644 --- a/pkg/security/tests/main_windows.go +++ b/pkg/security/tests/main_windows.go @@ -8,6 +8,13 @@ // Package tests holds tests related files package tests +import "errors" + +// getPIDCGroup returns the path of the first cgroup found for a PID +func getPIDCGroup(pid uint32) (string, error) { + return "", errors.New("cgroups are not supported on Windows") +} + func preTestsHook() {} func postTestsHook() {} diff --git a/pkg/security/tests/module_tester_linux.go b/pkg/security/tests/module_tester_linux.go index 54d262fdf599d..37a54cf6fee5b 100644 --- a/pkg/security/tests/module_tester_linux.go +++ b/pkg/security/tests/module_tester_linux.go @@ -43,6 +43,7 @@ import ( cgroupModel "github.com/DataDog/datadog-agent/pkg/security/resolvers/cgroup/model" rulesmodule "github.com/DataDog/datadog-agent/pkg/security/rules" "github.com/DataDog/datadog-agent/pkg/security/rules/bundled" + "github.com/DataDog/datadog-agent/pkg/security/secl/containerutils" "github.com/DataDog/datadog-agent/pkg/security/secl/model" "github.com/DataDog/datadog-agent/pkg/security/secl/rules" activity_tree "github.com/DataDog/datadog-agent/pkg/security/security_profile/activity_tree" @@ -1124,7 +1125,7 @@ func checkNetworkCompatibility(tb testing.TB) { }) } -func (tm *testModule) StopActivityDump(name, containerID string) error { +func (tm *testModule) StopActivityDump(name string) error { p, ok := tm.probe.PlatformProbe.(*sprobe.EBPFProbe) if !ok { return errors.New("not supported") @@ -1135,8 +1136,7 @@ func (tm *testModule) StopActivityDump(name, containerID string) error { return errors.New("no manager") } params := &api.ActivityDumpStopParams{ - Name: name, - ContainerID: containerID, + Name: name, } _, err := managers.StopActivityDump(params) if err != nil { @@ -1147,7 +1147,8 @@ func (tm *testModule) StopActivityDump(name, containerID string) error { type activityDumpIdentifier struct { Name string - ContainerID string + ContainerID containerutils.ContainerID + CGroupID containerutils.CGroupID Timeout string OutputFiles []string } @@ -1182,7 +1183,8 @@ func (tm *testModule) ListActivityDumps() ([]*activityDumpIdentifier, error) { dumps = append(dumps, &activityDumpIdentifier{ Name: dump.Metadata.Name, - ContainerID: dump.Metadata.ContainerID, + ContainerID: containerutils.ContainerID(dump.Metadata.ContainerID), + CGroupID: containerutils.CGroupID(dump.Metadata.CGroupID), Timeout: dump.Metadata.Timeout, OutputFiles: files, }) @@ -1252,6 +1254,7 @@ func (tm *testModule) StartADocker() (*dockerCmdWrapper, error) { } time.Sleep(1 * time.Second) // a quick sleep to ensure the dump has started + return docker, nil } @@ -1260,7 +1263,7 @@ func (tm *testModule) GetDumpFromDocker(dockerInstance *dockerCmdWrapper) (*acti if err != nil { return nil, err } - dump := findLearningContainerID(dumps, dockerInstance.containerID) + dump := findLearningContainerID(dumps, containerutils.ContainerID(dockerInstance.containerID)) if dump == nil { return nil, errors.New("ContainerID not found on activity dump list") } @@ -1281,7 +1284,7 @@ func (tm *testModule) StartADockerGetDump() (*dockerCmdWrapper, *activityDumpIde } //nolint:deadcode,unused -func findLearningContainerID(dumps []*activityDumpIdentifier, containerID string) *activityDumpIdentifier { +func findLearningContainerID(dumps []*activityDumpIdentifier, containerID containerutils.ContainerID) *activityDumpIdentifier { for _, dump := range dumps { if dump.ContainerID == containerID { return dump @@ -1538,7 +1541,7 @@ func (tm *testModule) StopAllActivityDumps() error { return nil } for _, dump := range dumps { - _ = tm.StopActivityDump(dump.Name, "") + _ = tm.StopActivityDump(dump.Name) } dumps, err = tm.ListActivityDumps() if err != nil { diff --git a/pkg/security/tests/security_profile_test.go b/pkg/security/tests/security_profile_test.go index 1c814ea196f5c..01faf63d61ed5 100644 --- a/pkg/security/tests/security_profile_test.go +++ b/pkg/security/tests/security_profile_test.go @@ -87,7 +87,7 @@ func TestSecurityProfile(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -97,7 +97,8 @@ func TestSecurityProfile(t *testing.T) { if sp.Metadata.Name != dump.Name { t.Errorf("Profile name %s != %s\n", sp.Metadata.Name, dump.Name) } - if sp.Metadata.ContainerID != dump.ContainerID { + if (sp.Metadata.ContainerID != dump.ContainerID) && + (sp.Metadata.CGroupContext.CGroupID != dump.CGroupID) { t.Errorf("Profile containerID %s != %s\n", sp.Metadata.ContainerID, dump.ContainerID) } @@ -105,7 +106,7 @@ func TestSecurityProfile(t *testing.T) { if ctx == nil { t.Errorf("No profile context found!") } else { - if !slices.Contains(ctx.Tags, "container_id:"+dump.ContainerID) { + if !slices.Contains(ctx.Tags, "container_id:"+string(dump.ContainerID)) { t.Errorf("Profile did not contains container_id tag: %v\n", ctx.Tags) } if !slices.Contains(ctx.Tags, "image_tag:latest") { @@ -140,7 +141,7 @@ func TestSecurityProfile(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -179,7 +180,7 @@ func TestSecurityProfile(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -267,7 +268,7 @@ func TestAnomalyDetection(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -299,7 +300,7 @@ func TestAnomalyDetection(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -332,7 +333,7 @@ func TestAnomalyDetection(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -368,7 +369,7 @@ func TestAnomalyDetection(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -443,7 +444,7 @@ func TestAnomalyDetectionWarmup(t *testing.T) { cmd.CombinedOutput() time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -605,7 +606,7 @@ func TestSecurityProfileReinsertionPeriod(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -639,7 +640,7 @@ func TestSecurityProfileReinsertionPeriod(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -669,7 +670,7 @@ func TestSecurityProfileReinsertionPeriod(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -706,7 +707,7 @@ func TestSecurityProfileReinsertionPeriod(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -840,7 +841,7 @@ func TestSecurityProfileAutoSuppression(t *testing.T) { } }) - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -981,7 +982,7 @@ func TestSecurityProfileDifferentiateArgs(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1078,7 +1079,7 @@ func TestSecurityProfileLifeCycleExecs(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1091,7 +1092,7 @@ func TestSecurityProfileLifeCycleExecs(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1252,7 +1253,7 @@ func TestSecurityProfileLifeCycleDNS(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1265,7 +1266,7 @@ func TestSecurityProfileLifeCycleDNS(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1425,7 +1426,7 @@ func TestSecurityProfileLifeCycleSyscall(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1438,7 +1439,7 @@ func TestSecurityProfileLifeCycleSyscall(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1614,7 +1615,7 @@ func TestSecurityProfileLifeCycleEvictionProcess(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1627,7 +1628,7 @@ func TestSecurityProfileLifeCycleEvictionProcess(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1792,7 +1793,7 @@ func TestSecurityProfileLifeCycleEvictionDNS(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1805,7 +1806,7 @@ func TestSecurityProfileLifeCycleEvictionDNS(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -1970,7 +1971,7 @@ func TestSecurityProfileLifeCycleEvictionProcessUnstable(t *testing.T) { t.Fatal(err) } - dockerInstanceV1, err := test.StartADocker() + dockerInstanceV1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } @@ -1983,7 +1984,7 @@ func TestSecurityProfileLifeCycleEvictionProcessUnstable(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump("", dockerInstanceV1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -2148,13 +2149,13 @@ func TestSecurityProfilePersistence(t *testing.T) { } defer test.Close() - dockerInstance1, err := test.StartADocker() + dockerInstance1, dump, err := test.StartADockerGetDump() if err != nil { t.Fatal(err) } defer dockerInstance1.stop() - err = test.StopActivityDump("", dockerInstance1.containerID) + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } diff --git a/pkg/security/tests/threat_score_test.go b/pkg/security/tests/threat_score_test.go index 2256847bc5f86..13d11ddc52451 100644 --- a/pkg/security/tests/threat_score_test.go +++ b/pkg/security/tests/threat_score_test.go @@ -100,7 +100,7 @@ func TestActivityDumpsThreatScore(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -150,7 +150,7 @@ func TestActivityDumpsThreatScore(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -191,7 +191,7 @@ func TestActivityDumpsThreatScore(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } @@ -236,7 +236,7 @@ func TestActivityDumpsThreatScore(t *testing.T) { } time.Sleep(1 * time.Second) // a quick sleep to let events to be added to the dump - err = test.StopActivityDump(dump.Name, "") + err = test.StopActivityDump(dump.Name) if err != nil { t.Fatal(err) } diff --git a/pkg/security/utils/cgroup.go b/pkg/security/utils/cgroup.go index b4a3de192d7de..5dbbb97c58af1 100644 --- a/pkg/security/utils/cgroup.go +++ b/pkg/security/utils/cgroup.go @@ -90,9 +90,10 @@ func GetProcContainerContext(tgid, pid uint32) (containerutils.ContainerID, mode return "", model.CGroupContext{}, err } - containerID, runtime := cgroups[0].GetContainerContext() + lastCgroup := len(cgroups) - 1 + containerID, runtime := cgroups[lastCgroup].GetContainerContext() cgroupContext := model.CGroupContext{ - CGroupID: containerutils.CGroupID(cgroups[0].Path), + CGroupID: containerutils.CGroupID(cgroups[lastCgroup].Path), CGroupFlags: runtime, } From 38f5a7745492575b4121c96d4880673c7dd01ad3 Mon Sep 17 00:00:00 2001 From: pducolin <45568537+pducolin@users.noreply.github.com> Date: Mon, 16 Dec 2024 12:00:42 +0100 Subject: [PATCH 16/78] [new-e2e] Bump dependencies (#32171) Co-authored-by: dependabot[bot] --- comp/otelcol/ddflareextension/impl/go.mod | 2 +- comp/otelcol/ddflareextension/impl/go.sum | 4 +- .../exporter/datadogexporter/go.mod | 2 +- .../exporter/datadogexporter/go.sum | 4 +- .../otlp/components/metricsclient/go.mod | 2 +- .../otlp/components/metricsclient/go.sum | 4 +- .../otlp/components/statsprocessor/go.mod | 2 +- .../otlp/components/statsprocessor/go.sum | 4 +- go.mod | 14 ++-- go.sum | 32 ++++---- internal/tools/go.mod | 2 +- internal/tools/go.sum | 8 +- pkg/config/remote/go.mod | 2 +- pkg/config/remote/go.sum | 4 +- pkg/obfuscate/go.mod | 2 +- pkg/obfuscate/go.sum | 4 +- pkg/trace/go.mod | 2 +- pkg/trace/go.sum | 4 +- pkg/trace/stats/oteltest/go.mod | 2 +- pkg/trace/stats/oteltest/go.sum | 4 +- test/new-e2e/go.mod | 30 ++++---- test/new-e2e/go.sum | 75 +++++++++++-------- test/otel/go.mod | 2 +- test/otel/go.sum | 4 +- 24 files changed, 114 insertions(+), 101 deletions(-) diff --git a/comp/otelcol/ddflareextension/impl/go.mod b/comp/otelcol/ddflareextension/impl/go.mod index 633e70723c7b1..0719700f9222a 100644 --- a/comp/otelcol/ddflareextension/impl/go.mod +++ b/comp/otelcol/ddflareextension/impl/go.mod @@ -263,7 +263,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.59.0 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.59.1 // indirect github.com/DataDog/datadog-api-client-go/v2 v2.33.0 // indirect - github.com/DataDog/datadog-go/v5 v5.5.0 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 // indirect github.com/DataDog/go-sqllexer v0.0.17 // indirect github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect diff --git a/comp/otelcol/ddflareextension/impl/go.sum b/comp/otelcol/ddflareextension/impl/go.sum index 86b5c591f955c..7a6bcb0649c64 100644 --- a/comp/otelcol/ddflareextension/impl/go.sum +++ b/comp/otelcol/ddflareextension/impl/go.sum @@ -66,8 +66,8 @@ github.com/DataDog/datadog-agent/comp/core/log v0.56.2/go.mod h1:ivJ/RMZjTNkoPPN github.com/DataDog/datadog-api-client-go/v2 v2.33.0 h1:OI6kDnJeQmkjfGzxmP0XUQUxMD4tp6oAPXnnJ4VpgUM= github.com/DataDog/datadog-api-client-go/v2 v2.33.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 h1:RoH7VLzTnxHEugRPIgnGlxwDFszFGI7b3WZZUtWuPRM= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod index 7ce89b2ee2d53..5023166f00df5 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod @@ -102,7 +102,7 @@ require ( github.com/DataDog/datadog-agent/pkg/proto v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/serializer v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.22.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/metrics v0.22.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/datadog v0.115.0 diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum index 1f0bcec425283..5107430987dd3 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum @@ -4,8 +4,8 @@ github.com/DataDog/agent-payload/v5 v5.0.138 h1:Wg7hmWuoLC/o0X3zZ+uGcfRHPyaytlju github.com/DataDog/agent-payload/v5 v5.0.138/go.mod h1:lxh9lb5xYrBXjblpIWYUi4deJqVbkIfkjwesi5nskDc= github.com/DataDog/datadog-api-client-go/v2 v2.33.0 h1:OI6kDnJeQmkjfGzxmP0XUQUxMD4tp6oAPXnnJ4VpgUM= github.com/DataDog/datadog-api-client-go/v2 v2.33.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 h1:RoH7VLzTnxHEugRPIgnGlxwDFszFGI7b3WZZUtWuPRM= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= diff --git a/comp/otelcol/otlp/components/metricsclient/go.mod b/comp/otelcol/otlp/components/metricsclient/go.mod index 953a7c7a017cd..9f3e33feb8bf7 100644 --- a/comp/otelcol/otlp/components/metricsclient/go.mod +++ b/comp/otelcol/otlp/components/metricsclient/go.mod @@ -6,7 +6,7 @@ replace github.com/DataDog/datadog-agent/pkg/trace => ../../../../../pkg/trace require ( github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/otel v1.32.0 go.opentelemetry.io/otel/metric v1.32.0 diff --git a/comp/otelcol/otlp/components/metricsclient/go.sum b/comp/otelcol/otlp/components/metricsclient/go.sum index d3cbee6f76083..08cca2d402cd6 100644 --- a/comp/otelcol/otlp/components/metricsclient/go.sum +++ b/comp/otelcol/otlp/components/metricsclient/go.sum @@ -1,5 +1,5 @@ -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= diff --git a/comp/otelcol/otlp/components/statsprocessor/go.mod b/comp/otelcol/otlp/components/statsprocessor/go.mod index 8a0a6409f886f..05eb43fdf4629 100644 --- a/comp/otelcol/otlp/components/statsprocessor/go.mod +++ b/comp/otelcol/otlp/components/statsprocessor/go.mod @@ -22,7 +22,7 @@ require ( github.com/DataDog/datadog-agent/comp/trace/compression/impl-gzip v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/proto v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.22.0 github.com/stretchr/testify v1.10.0 go.opentelemetry.io/collector/component/componenttest v0.115.0 diff --git a/comp/otelcol/otlp/components/statsprocessor/go.sum b/comp/otelcol/otlp/components/statsprocessor/go.sum index 13b6ed7206fcd..25a9e6fccac3c 100644 --- a/comp/otelcol/otlp/components/statsprocessor/go.sum +++ b/comp/otelcol/otlp/components/statsprocessor/go.sum @@ -1,5 +1,5 @@ -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= github.com/DataDog/go-sqllexer v0.0.17/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= diff --git a/go.mod b/go.mod index a01741a7d419f..40f6882509169 100644 --- a/go.mod +++ b/go.mod @@ -157,8 +157,8 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.59.1 github.com/DataDog/datadog-agent/pkg/util/pointer v0.59.0 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.59.1 - github.com/DataDog/datadog-go/v5 v5.5.0 - github.com/DataDog/datadog-operator v0.7.1-0.20241024104907-734366f3c0d1 + github.com/DataDog/datadog-go/v5 v5.6.0 + github.com/DataDog/datadog-operator v1.11.0-rc.2 github.com/DataDog/ebpf-manager v0.7.4 github.com/DataDog/gopsutil v1.2.2 github.com/DataDog/nikos v1.12.8 @@ -197,7 +197,7 @@ require ( github.com/cri-o/ocicni v0.4.3 github.com/cyphar/filepath-securejoin v0.3.4 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc - github.com/docker/docker v27.3.1+incompatible + github.com/docker/docker v27.4.0+incompatible github.com/docker/go-connections v0.5.0 github.com/dustin/go-humanize v1.0.1 github.com/elastic/go-libaudit/v2 v2.5.0 @@ -323,9 +323,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v3 v3.0.1 gopkg.in/zorkian/go-datadog-api.v2 v2.30.0 - k8s.io/api v0.31.3 + k8s.io/api v0.31.4 k8s.io/apiextensions-apiserver v0.31.2 - k8s.io/apimachinery v0.31.3 + k8s.io/apimachinery v0.31.4 k8s.io/apiserver v0.31.2 // indirect k8s.io/autoscaler/vertical-pod-autoscaler v0.13.0 k8s.io/client-go v0.31.3 @@ -403,7 +403,7 @@ require ( github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/dgryski/go-jump v0.0.0-20211018200510-ba001c3ffce0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/docker/cli v27.1.1+incompatible // indirect + github.com/docker/cli v27.4.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.1 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect @@ -813,7 +813,9 @@ require ( github.com/Showmax/go-fqdn v1.0.0 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect + github.com/alecthomas/assert/v2 v2.6.0 // indirect github.com/alecthomas/participle/v2 v2.1.1 // indirect + github.com/alecthomas/repr v0.4.0 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/antchfx/xmlquery v1.4.2 // indirect github.com/antchfx/xpath v1.3.2 // indirect diff --git a/go.sum b/go.sum index 99aabf21db82e..b241ffba51c8e 100644 --- a/go.sum +++ b/go.sum @@ -128,10 +128,10 @@ github.com/DataDog/datadog-agent/comp/core/log v0.56.2/go.mod h1:ivJ/RMZjTNkoPPN github.com/DataDog/datadog-api-client-go/v2 v2.33.0 h1:OI6kDnJeQmkjfGzxmP0XUQUxMD4tp6oAPXnnJ4VpgUM= github.com/DataDog/datadog-api-client-go/v2 v2.33.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= -github.com/DataDog/datadog-operator v0.7.1-0.20241024104907-734366f3c0d1 h1:17MXCuo1IacLeVzdmwV+92ANVrXkquObm0yxjen1eJ0= -github.com/DataDog/datadog-operator v0.7.1-0.20241024104907-734366f3c0d1/go.mod h1:5Xp66c0HGadP85lOQtb6oVuSvuL5ZSSowQM58hNtTi8= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-operator v1.11.0-rc.2 h1:4tMZlxbYE0WEpRYAhoqEe8nLP67C/PDq7utJJcD8RM8= +github.com/DataDog/datadog-operator v1.11.0-rc.2/go.mod h1:mD+3PWR0wOSVJGaXjkpzsYEK/7PhqjOipx2usgfsxM0= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 h1:RoH7VLzTnxHEugRPIgnGlxwDFszFGI7b3WZZUtWuPRM= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= github.com/DataDog/dd-trace-go/v2 v2.0.0-beta.11 h1:6vwU//TjBIghQKMgIP9UyIRhN/LWS1y8tYzvRnu8JZw= @@ -241,16 +241,16 @@ github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7l github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY= github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/jsonschema v0.0.0-20210918223802-a1d3f4b43d7b/go.mod h1:/n6+1/DWPltRLWL/VKyUxg6tzsl5kHUCcraimt4vr60= github.com/alecthomas/participle v0.7.1 h1:2bN7reTw//5f0cugJcTOnY/NYZcWQOaajW+BwZB5xWs= github.com/alecthomas/participle v0.7.1/go.mod h1:HfdmEuwvr12HXQN44HPWXR0lHmVolVYe4dyL6lQ3duY= github.com/alecthomas/participle/v2 v2.1.1 h1:hrjKESvSqGHzRb4yW1ciisFJ4p3MGYih6icjJvbsmV8= github.com/alecthomas/participle/v2 v2.1.1/go.mod h1:Y1+hAs8DHPmc3YUFzqllV+eSQ9ljPTk0ZkPMtEdAx2c= github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= 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= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -552,12 +552,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= -github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= -github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.4.0+incompatible h1:/nJzWkcI1MDMN+U+px/YXnQWJqnu4J+QKGTfD6ptiTc= +github.com/docker/cli v27.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= -github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.4.0+incompatible h1:I9z7sQ5qyzO0BfAb9IMOawRkAGxhYsidKiTMcm0DU+A= +github.com/docker/docker v27.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo= github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -2666,13 +2666,13 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I= honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs= k8s.io/api v0.21.1/go.mod h1:FstGROTmsSHBarKc8bylzXih8BLNYTiS3TZcsoEDg2s= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= +k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM= k8s.io/apimachinery v0.21.1/go.mod h1:jbreFvJo3ov9rj7eWT7+sYiRx+qZuCYXwWT1bcDswPY= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= +k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE= k8s.io/autoscaler/vertical-pod-autoscaler v0.13.0 h1:pH6AsxeBZcyX6KBqcnl7SPIJqbN1d59RrEBuIE6Rq6c= diff --git a/internal/tools/go.mod b/internal/tools/go.mod index e2a60b08d8971..79be9ca61a043 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -32,7 +32,7 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect github.com/ProtonMail/go-crypto v1.1.0-alpha.0 // indirect - github.com/alecthomas/assert/v2 v2.3.0 // indirect + github.com/alecthomas/assert/v2 v2.6.0 // indirect github.com/alecthomas/go-check-sumtype v0.1.4 // indirect github.com/alexkohler/nakedret/v2 v2.0.4 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect diff --git a/internal/tools/go.sum b/internal/tools/go.sum index f5f1fb317b59a..2bf7f8b759bd6 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -38,12 +38,12 @@ github.com/aclements/go-gg v0.0.0-20170118225347-6dbb4e4fefb0/go.mod h1:55qNq4vc github.com/aclements/go-moremath v0.0.0-20161014184102-0ff62e0875ff/go.mod h1:idZL3yvz4kzx1dsBOAC+oYv6L92P1oFEhUXUB1A/lwQ= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= -github.com/alecthomas/assert/v2 v2.3.0 h1:mAsH2wmvjsuvyBvAmCtm7zFsBlb8mIHx5ySLVdDZXL0= -github.com/alecthomas/assert/v2 v2.3.0/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= -github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= -github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= +github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg= github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= diff --git a/pkg/config/remote/go.mod b/pkg/config/remote/go.mod index ec7d298386516..282d7f34ff86f 100644 --- a/pkg/config/remote/go.mod +++ b/pkg/config/remote/go.mod @@ -85,7 +85,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/system/socket v0.59.0 // indirect github.com/DataDog/datadog-agent/pkg/util/winutil v0.59.1 // indirect github.com/DataDog/datadog-agent/pkg/version v0.59.1 // indirect - github.com/DataDog/datadog-go/v5 v5.5.0 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect github.com/DataDog/go-libddwaf/v3 v3.5.1 // indirect github.com/DataDog/go-sqllexer v0.0.17 // indirect github.com/DataDog/sketches-go v1.4.6 // indirect diff --git a/pkg/config/remote/go.sum b/pkg/config/remote/go.sum index e9342de28e23c..84cc570d9ad84 100644 --- a/pkg/config/remote/go.sum +++ b/pkg/config/remote/go.sum @@ -7,8 +7,8 @@ cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/yb github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/appsec-internal-go v1.9.0 h1:cGOneFsg0JTRzWl5U2+og5dbtyW3N8XaYwc5nXe39Vw= github.com/DataDog/appsec-internal-go v1.9.0/go.mod h1:wW0cRfWBo4C044jHGwYiyh5moQV2x0AhnwqMuiX7O/g= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/go-libddwaf/v3 v3.5.1 h1:GWA4ln4DlLxiXm+X7HA/oj0ZLcdCwOS81KQitegRTyY= github.com/DataDog/go-libddwaf/v3 v3.5.1/go.mod h1:n98d9nZ1gzenRSk53wz8l6d34ikxS+hs62A31Fqmyi4= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= diff --git a/pkg/obfuscate/go.mod b/pkg/obfuscate/go.mod index 7085b75f3783d..b34df591521b5 100644 --- a/pkg/obfuscate/go.mod +++ b/pkg/obfuscate/go.mod @@ -3,7 +3,7 @@ module github.com/DataDog/datadog-agent/pkg/obfuscate go 1.22.0 require ( - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/DataDog/go-sqllexer v0.0.17 github.com/outcaste-io/ristretto v0.2.3 github.com/stretchr/testify v1.10.0 diff --git a/pkg/obfuscate/go.sum b/pkg/obfuscate/go.sum index e36a2867eaba6..063bd88a07005 100644 --- a/pkg/obfuscate/go.sum +++ b/pkg/obfuscate/go.sum @@ -1,5 +1,5 @@ -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= github.com/DataDog/go-sqllexer v0.0.17/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= diff --git a/pkg/trace/go.mod b/pkg/trace/go.mod index 6fbe7317b66c5..fa1ca487a3dc8 100644 --- a/pkg/trace/go.mod +++ b/pkg/trace/go.mod @@ -20,7 +20,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/log v0.59.0 github.com/DataDog/datadog-agent/pkg/util/pointer v0.59.0 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.59.0 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.22.0 github.com/DataDog/sketches-go v1.4.6 github.com/Microsoft/go-winio v0.6.2 diff --git a/pkg/trace/go.sum b/pkg/trace/go.sum index 75f5a8bd8498a..4613790f7cc34 100644 --- a/pkg/trace/go.sum +++ b/pkg/trace/go.sum @@ -1,5 +1,5 @@ -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= github.com/DataDog/go-sqllexer v0.0.17/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= diff --git a/pkg/trace/stats/oteltest/go.mod b/pkg/trace/stats/oteltest/go.mod index dafb9c2cd7a8b..306e0217b22ad 100644 --- a/pkg/trace/stats/oteltest/go.mod +++ b/pkg/trace/stats/oteltest/go.mod @@ -6,7 +6,7 @@ require ( github.com/DataDog/datadog-agent/comp/otelcol/otlp/components/statsprocessor v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/proto v0.56.0-rc.3 github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.22.0 github.com/google/go-cmp v0.6.0 github.com/stretchr/testify v1.10.0 diff --git a/pkg/trace/stats/oteltest/go.sum b/pkg/trace/stats/oteltest/go.sum index 13b6ed7206fcd..25a9e6fccac3c 100644 --- a/pkg/trace/stats/oteltest/go.sum +++ b/pkg/trace/stats/oteltest/go.sum @@ -1,5 +1,5 @@ -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= github.com/DataDog/go-sqllexer v0.0.17/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 8b0a384191503..008a6b32a4753 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -62,16 +62,16 @@ require ( github.com/aws/aws-sdk-go-v2 v1.32.5 github.com/aws/aws-sdk-go-v2/config v1.28.5 github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 - github.com/aws/aws-sdk-go-v2/service/eks v1.44.1 - github.com/aws/aws-sdk-go-v2/service/ssm v1.50.7 + github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 + github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2 github.com/cenkalti/backoff v2.2.1+incompatible - github.com/docker/cli v27.1.1+incompatible - github.com/docker/docker v27.3.1+incompatible + github.com/docker/cli v27.4.0+incompatible + github.com/docker/docker v27.4.0+incompatible github.com/fatih/color v1.18.0 github.com/google/uuid v1.6.0 github.com/kr/pretty v0.3.1 github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c - github.com/pkg/sftp v1.13.6 + github.com/pkg/sftp v1.13.7 github.com/pulumi/pulumi-aws/sdk/v6 v6.56.1 github.com/pulumi/pulumi-awsx/sdk/v2 v2.16.1 github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.3 @@ -84,8 +84,8 @@ require ( golang.org/x/term v0.27.0 gopkg.in/yaml.v2 v2.4.0 gopkg.in/zorkian/go-datadog-api.v2 v2.30.0 - k8s.io/api v0.31.3 - k8s.io/apimachinery v0.31.3 + k8s.io/api v0.31.4 + k8s.io/apimachinery v0.31.4 k8s.io/cli-runtime v0.31.2 k8s.io/client-go v0.31.3 k8s.io/kubectl v0.31.2 @@ -115,14 +115,13 @@ require ( github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 // indirect github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 // indirect github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.0 // indirect - github.com/aws/aws-sdk-go-v2/service/s3 v1.65.0 + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 // indirect github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect @@ -134,7 +133,6 @@ require ( github.com/charmbracelet/bubbles v0.18.0 // indirect github.com/charmbracelet/bubbletea v0.25.0 // indirect github.com/charmbracelet/lipgloss v0.10.0 // indirect - github.com/cheggaaa/pb v1.0.29 // indirect github.com/cloudflare/circl v1.3.7 // indirect github.com/containerd/console v1.0.4 // indirect github.com/containerd/log v0.1.0 // indirect @@ -284,16 +282,18 @@ require ( require ( github.com/DataDog/datadog-agent/comp/core/tagger/types v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 - github.com/DataDog/datadog-go/v5 v5.5.0 + github.com/DataDog/datadog-go/v5 v5.6.0 github.com/aws/aws-sdk-go v1.55.5 - github.com/aws/session-manager-plugin v0.0.0-20241010233726-61cf1288c7c6 + github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 + github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492 github.com/digitalocean/go-libvirt v0.0.0-20240812180835-9c6c0a310c6c - github.com/hairyhenderson/go-codeowners v0.5.0 + github.com/hairyhenderson/go-codeowners v0.7.0 ) require ( github.com/DataDog/datadog-agent/comp/core/tagger/utils v0.59.0 // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cheggaaa/pb v1.0.29 // indirect github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/creack/pty v1.1.20 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index d8ba72c668e62..cbbd971dd8ac3 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -13,8 +13,8 @@ github.com/DataDog/datadog-api-client-go v1.16.0 h1:5jOZv1m98criCvYTa3qpW8Hzv301 github.com/DataDog/datadog-api-client-go v1.16.0/go.mod h1:PgrP2ABuJWL3Auw2iEkemAJ/r72ghG4DQQmb5sgnKW4= github.com/DataDog/datadog-api-client-go/v2 v2.33.0 h1:OI6kDnJeQmkjfGzxmP0XUQUxMD4tp6oAPXnnJ4VpgUM= github.com/DataDog/datadog-api-client-go/v2 v2.33.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 h1:EbzDX8HPk5uE2FsJYxD74QmMw0/3CqSKhEr6teh0ncQ= github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= github.com/DataDog/test-infra-definitions v0.0.0-20241211124138-9c7c5005ca28 h1:LaZgAke+RN4wBKNl+R10ewdtKe/C2MJCbp9ozXKlLP8= @@ -68,36 +68,36 @@ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.19 h1:FKdiFzTxlTRO71p0C7VrLbkkdW8qfMKF5+ej6bTmkT0= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.19/go.mod h1:abO3pCj7WLQPTllnSeYImqFfkGrmJV0JovWo/gqT5N0= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 h1:k97fGog9Tl0woxTiSIHN14Qs5ehqK6GXejUwkhJYyL0= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0/go.mod h1:mzj8EEjIHSN2oZRXiw1Dd+uB4HZTl7hC8nBzX9IZMWw= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs= github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0= github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 h1:CTkPGE8fiElvLtYWl/U+Eu5+1fVXiZbJUjyVCRSRgxk= github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4/go.mod h1:sMFLFhL27cKYa/eQYZp4asvIwHsnJWrAzTUpy9AQdnU= -github.com/aws/aws-sdk-go-v2/service/eks v1.44.1 h1:onUAzZXDsyXzyrmOGw/9p8Csl1NZkTDEs4URZ8covUY= -github.com/aws/aws-sdk-go-v2/service/eks v1.44.1/go.mod h1:dg9l/W4hXygeRNydRB4LWKY/MwHJhfUomGJUBwI29Dw= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 h1:BYyB+byjQ7oyupe3v+YjTp1yfmfNEwChYA2naCc85xI= +github.com/aws/aws-sdk-go-v2/service/eks v1.51.0/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.0 h1:FQNWhRuSq8QwW74GtU0MrveNhZbqvHsA4dkA9w8fTDQ= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.0/go.mod h1:j/zZ3zmWfGCK91K73YsfHP53BSTLSjL/y6YN39XbBLM= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.0 h1:1NKXS8XfhMM0bg5wVYa/eOH8AM2f6JijugbKEyQFTIg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.0/go.mod h1:ph931DUfVfgrhZR7py9olSvHCiRpvaGxNvlWBcXxFds= -github.com/aws/aws-sdk-go-v2/service/s3 v1.65.0 h1:2dSm7frMrw2tdJ0QvyccQNJyPGaP24dyDgZ6h1QJMGU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.65.0/go.mod h1:4XSVpw66upN8wND3JZA29eXl2NOZvfFVq7DIP6xvfuQ= -github.com/aws/aws-sdk-go-v2/service/ssm v1.50.7 h1:GkRsyFS9MmX/ybCvOncmp1A4XYn75v0x/ReWnIUao6E= -github.com/aws/aws-sdk-go-v2/service/ssm v1.50.7/go.mod h1:oBlt+H2x16bM5mSUNhmzIR2BWWnMsLUa1Qqs5auS1Bs= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 h1:xA6XhTF7PE89BCNHJbQi8VvPzcgMtmGC5dr8S8N7lHk= +github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8= +github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2 h1:z6Pq4+jtKlhK4wWJGHRGwMLGjC1HZwAO3KJr/Na0tSU= +github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2/go.mod h1:DSmu/VZzpQlAubWBbAvNpt+S4k/XweglJi4XaDGyvQk= github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= -github.com/aws/session-manager-plugin v0.0.0-20241010233726-61cf1288c7c6 h1:iQc6pdTje/w3D3vrocVIvcosNVQGjoGxqBgPpwG28BY= -github.com/aws/session-manager-plugin v0.0.0-20241010233726-61cf1288c7c6/go.mod h1:7n17tunRPUsniNBu5Ja9C7WwJWTdOzaLqr/H0Ns3uuI= +github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492 h1:Ihams/fjKo4iWwM313ng2gCJWoetsL7ZQkXhOTmVUq4= +github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492/go.mod h1:7n17tunRPUsniNBu5Ja9C7WwJWTdOzaLqr/H0Ns3uuI= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -153,10 +153,10 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c= github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0= -github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= -github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI= -github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/cli v27.4.0+incompatible h1:/nJzWkcI1MDMN+U+px/YXnQWJqnu4J+QKGTfD6ptiTc= +github.com/docker/cli v27.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/docker v27.4.0+incompatible h1:I9z7sQ5qyzO0BfAb9IMOawRkAGxhYsidKiTMcm0DU+A= +github.com/docker/docker v27.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= @@ -259,8 +259,8 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645 h1:MJG/KsmcqMwFAkh8mTnAwhyKoB+sTAnY4CACC110tbU= github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= -github.com/hairyhenderson/go-codeowners v0.5.0 h1:dpQB+hVHiRc2VVvc2BHxkuM+tmu9Qej/as3apqUbsWc= -github.com/hairyhenderson/go-codeowners v0.5.0/go.mod h1:R3uW1OQXEj2Gu6/OvZ7bt6hr0qdkLvUWPiqNaWnexpo= +github.com/hairyhenderson/go-codeowners v0.7.0 h1:s0W4wF8bdsBEjTWzwzSlsatSthWtTAF2xLgo4a4RwAo= +github.com/hairyhenderson/go-codeowners v0.7.0/go.mod h1:wUlNgQ3QjqC4z8DnM5nnCYVq/icpqXJyJOukKx5U8/Q= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -381,8 +381,8 @@ github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFz github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= -github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= +github.com/pkg/sftp v1.13.7 h1:uv+I3nNJvlKZIQGSr8JVQLNHFU9YhhNpvC14Y6KgmSM= +github.com/pkg/sftp v1.13.7/go.mod h1:KMKI0t3T6hfA+lTR/ssZdunHo+uwq7ghoN09/FSu3DY= github.com/pkg/term v1.1.0 h1:xIAAdCMh3QIAy+5FrE8Ad8XoDhEU4ufwbaSozViP9kk= github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -560,7 +560,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -575,6 +575,7 @@ 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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -592,7 +593,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v 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.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -607,6 +609,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -632,19 +635,26 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.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.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.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= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= 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.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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= @@ -661,6 +671,7 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -715,10 +726,10 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= 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= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/api v0.31.4 h1:I2QNzitPVsPeLQvexMEsj945QumYraqv9m74isPDKhM= +k8s.io/api v0.31.4/go.mod h1:d+7vgXLvmcdT1BCo79VEgJxHHryww3V5np2OYTr6jdw= +k8s.io/apimachinery v0.31.4 h1:8xjE2C4CzhYVm9DGf60yohpNUh5AEBnPxCryPBECmlM= +k8s.io/apimachinery v0.31.4/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ= k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q= k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= diff --git a/test/otel/go.mod b/test/otel/go.mod index c1d17501161ea..fd0d05c30abe4 100644 --- a/test/otel/go.mod +++ b/test/otel/go.mod @@ -175,7 +175,7 @@ require ( github.com/DataDog/datadog-agent/pkg/util/winutil v0.59.1 // indirect github.com/DataDog/datadog-agent/pkg/version v0.59.1 // indirect github.com/DataDog/datadog-api-client-go/v2 v2.33.0 // indirect - github.com/DataDog/datadog-go/v5 v5.5.0 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 // indirect github.com/DataDog/go-sqllexer v0.0.17 // indirect github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect diff --git a/test/otel/go.sum b/test/otel/go.sum index e8d514c34bee5..270aa7b812248 100644 --- a/test/otel/go.sum +++ b/test/otel/go.sum @@ -4,8 +4,8 @@ github.com/DataDog/agent-payload/v5 v5.0.138 h1:Wg7hmWuoLC/o0X3zZ+uGcfRHPyaytlju github.com/DataDog/agent-payload/v5 v5.0.138/go.mod h1:lxh9lb5xYrBXjblpIWYUi4deJqVbkIfkjwesi5nskDc= github.com/DataDog/datadog-api-client-go/v2 v2.33.0 h1:OI6kDnJeQmkjfGzxmP0XUQUxMD4tp6oAPXnnJ4VpgUM= github.com/DataDog/datadog-api-client-go/v2 v2.33.0/go.mod h1:d3tOEgUd2kfsr9uuHQdY+nXrWp4uikgTgVCPdKNK30U= -github.com/DataDog/datadog-go/v5 v5.5.0 h1:G5KHeB8pWBNXT4Jtw0zAkhdxEAWSpWH00geHI6LDrKU= -github.com/DataDog/datadog-go/v5 v5.5.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42 h1:RoH7VLzTnxHEugRPIgnGlxwDFszFGI7b3WZZUtWuPRM= github.com/DataDog/dd-sensitive-data-scanner/sds-go/go v0.0.0-20240816154533-f7f9beb53a42/go.mod h1:TX7CTOQ3LbQjfAi4SwqUoR5gY1zfUk7VRBDTuArjaDc= github.com/DataDog/go-sqllexer v0.0.17 h1:u47fJAVg/+5DA74ZW3w0Qu+3qXHd3GtnA8ZBYixdPrM= From 84333661b9f85f891b8016df2a38eaf0e15d9dc5 Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Mon, 16 Dec 2024 12:00:50 +0100 Subject: [PATCH 17/78] fix(issue.assign-owner): Change default team (#32194) --- tasks/issue.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tasks/issue.py b/tasks/issue.py index 4cd05c97abcdc..4615d39716dae 100644 --- a/tasks/issue.py +++ b/tasks/issue.py @@ -25,9 +25,7 @@ def assign_owner(_, issue_id, dry_run=False): from slack_sdk import WebClient client = WebClient(os.environ['SLACK_API_TOKEN']) - channel = next( - (chan for team, chan in GITHUB_SLACK_MAP.items() if owner.lower() in team), '#agent-ask-anything' - ) + channel = next((chan for team, chan in GITHUB_SLACK_MAP.items() if owner.lower() in team), '#agent-devx-help') message = f':githubstatus_partial_outage: *New Community Issue*\n{issue.title} <{issue.html_url}|{gh.repo.name}#{issue_id}>\n' if channel == '#agent-ask-anything': message += "The CI bot failed to assign this issue to a team.\nPlease assign it manually." From 93555c374bbd1beb399d9f1306047344296219ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Juli=C3=A1n?= Date: Mon, 16 Dec 2024 12:17:46 +0100 Subject: [PATCH 18/78] [EBPF] gpu: validate e2e environment before running tests (#32097) Co-authored-by: val06 --- test/new-e2e/tests/gpu/gpu_test.go | 58 ++----- test/new-e2e/tests/gpu/provisioner.go | 237 ++++++++++++++++++++++++++ 2 files changed, 250 insertions(+), 45 deletions(-) create mode 100644 test/new-e2e/tests/gpu/provisioner.go diff --git a/test/new-e2e/tests/gpu/gpu_test.go b/test/new-e2e/tests/gpu/gpu_test.go index 74b1229437a46..0c71a1fe7f74c 100644 --- a/test/new-e2e/tests/gpu/gpu_test.go +++ b/test/new-e2e/tests/gpu/gpu_test.go @@ -3,6 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2024-present Datadog, Inc. +// Package gpu contains e2e tests for the GPU monitoring module package gpu import ( @@ -14,18 +15,13 @@ import ( "testing" "time" - "github.com/DataDog/test-infra-definitions/scenarios/aws/ec2" "github.com/stretchr/testify/assert" "github.com/DataDog/datadog-agent/pkg/util/testutil/flake" "github.com/DataDog/datadog-agent/test/fakeintake/client" - "github.com/DataDog/test-infra-definitions/components/datadog/agentparams" - "github.com/DataDog/test-infra-definitions/components/os" - "github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments" - awshost "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments/aws/host" "github.com/DataDog/datadog-agent/test/new-e2e/pkg/utils/e2e/client/agentclient" ) @@ -34,63 +30,35 @@ var imageTag = flag.String("image-tag", "main", "Docker image tag to use") type gpuSuite struct { e2e.BaseSuite[environments.Host] - imageTag string containerNameCounter int } -const defaultGpuCheckConfig = ` -init_config: - min_collection_interval: 5 - -instances: - - {} -` - -const defaultSysprobeConfig = ` -gpu_monitoring: - enabled: true -` - const vectorAddDockerImg = "ghcr.io/datadog/apps-cuda-basic" -const gpuEnabledAMI = "ami-0f71e237bb2ba34be" // Ubuntu 22.04 with GPU drivers + +func dockerImageName() string { + return fmt.Sprintf("%s:%s", vectorAddDockerImg, *imageTag) +} // TestGPUSuite runs tests for the VM interface to ensure its implementation is correct. // Not to be run in parallel, as some tests wait until the checks are available. func TestGPUSuite(t *testing.T) { - provisioner := awshost.Provisioner( - awshost.WithEC2InstanceOptions( - ec2.WithInstanceType("g4dn.xlarge"), - ec2.WithAMI(gpuEnabledAMI, os.Ubuntu2204, os.AMD64Arch), - ), - awshost.WithAgentOptions( - agentparams.WithIntegration("gpu.d", defaultGpuCheckConfig), - agentparams.WithSystemProbeConfig(defaultSysprobeConfig), - ), - awshost.WithDocker(), - ) + provParams := getDefaultProvisionerParams() + + // Append our vectorAdd image for testing + provParams.dockerImages = append(provParams.dockerImages, dockerImageName()) + + provisioner := gpuInstanceProvisioner(provParams) suiteParams := []e2e.SuiteOption{e2e.WithProvisioner(provisioner)} if *devMode { suiteParams = append(suiteParams, e2e.WithDevMode()) } - suite := &gpuSuite{ - imageTag: *imageTag, - } + suite := &gpuSuite{} e2e.Run(t, suite, suiteParams...) } -func (v *gpuSuite) SetupSuite() { - v.BaseSuite.SetupSuite() - - v.Env().RemoteHost.MustExecute(fmt.Sprintf("docker pull %s", v.dockerImageName())) -} - -func (v *gpuSuite) dockerImageName() string { - return fmt.Sprintf("%s:%s", vectorAddDockerImg, v.imageTag) -} - // TODO: Extract this to common package? service_discovery uses it too type checkStatus struct { CheckID string `json:"CheckID"` @@ -118,7 +86,7 @@ func (v *gpuSuite) runCudaDockerWorkload() string { containerName := fmt.Sprintf("cuda-basic-%d", v.containerNameCounter) v.containerNameCounter++ - cmd := fmt.Sprintf("docker run --gpus all --name %s %s %s %d %d %d", containerName, v.dockerImageName(), binary, vectorSize, numLoops, waitTimeSeconds) + cmd := fmt.Sprintf("docker run --gpus all --name %s %s %s %d %d %d", containerName, dockerImageName(), binary, vectorSize, numLoops, waitTimeSeconds) out, err := v.Env().RemoteHost.Execute(cmd) v.Require().NoError(err) v.Require().NotEmpty(out) diff --git a/test/new-e2e/tests/gpu/provisioner.go b/test/new-e2e/tests/gpu/provisioner.go new file mode 100644 index 0000000000000..ee552dcca7d8d --- /dev/null +++ b/test/new-e2e/tests/gpu/provisioner.go @@ -0,0 +1,237 @@ +// 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 gpu + +import ( + "fmt" + "strconv" + + "github.com/pulumi/pulumi/sdk/v3/go/pulumi" + + "github.com/DataDog/test-infra-definitions/common/utils" + "github.com/DataDog/test-infra-definitions/components/command" + "github.com/DataDog/test-infra-definitions/components/datadog/agent" + "github.com/DataDog/test-infra-definitions/components/datadog/agentparams" + "github.com/DataDog/test-infra-definitions/components/docker" + "github.com/DataDog/test-infra-definitions/components/os" + "github.com/DataDog/test-infra-definitions/components/remote" + "github.com/DataDog/test-infra-definitions/resources/aws" + "github.com/DataDog/test-infra-definitions/scenarios/aws/ec2" + "github.com/DataDog/test-infra-definitions/scenarios/aws/fakeintake" + + "github.com/DataDog/datadog-agent/test/new-e2e/pkg/e2e" + "github.com/DataDog/datadog-agent/test/new-e2e/pkg/environments" +) + +// gpuEnabledAMI is an AMI that has GPU drivers pre-installed. In this case it's +// an Ubuntu 22.04 with NVIDIA drivers +const gpuEnabledAMI = "ami-0f71e237bb2ba34be" + +// gpuInstanceType is the instance type to use. By default we use g4dn.xlarge, +// which is the cheapest GPU instance type +const gpuInstanceType = "g4dn.xlarge" + +// nvidiaPCIVendorID is the PCI vendor ID for NVIDIA GPUs, used to identify the +// GPU devices with lspci +const nvidiaPCIVendorID = "10de" + +// cudaSanityCheckImage is a Docker image that contains a CUDA sample to +// validate the GPU setup with the default CUDA installation. Note that the CUDA +// version in this image must be equal or less than the one installed in the +// AMI. +const cudaSanityCheckImage = "669783387624.dkr.ecr.us-east-1.amazonaws.com/dockerhub/nvidia/cuda:12.6.3-base-ubuntu22.04" + +// nvidiaSMIValidationCmd is a command that checks if the nvidia-smi command is +// available and can list the GPUs +const nvidiaSMIValidationCmd = "nvidia-smi -L | grep GPU" + +// validationCommandMarker is a command that can be appended to all validation commands +// to identify them in the output, which can be useful to later force retries. Retries +// are controlled in test/new-e2e/pkg/utils/infra/retriable_errors.go, and the way to +// identify them are based on the pulumi logs. This command will be echoed to the output +// and can be used to identify the validation commands. +const validationCommandMarker = "echo 'gpu-validation-command'" + +const defaultGpuCheckConfig = ` +init_config: + min_collection_interval: 5 + +instances: + - {} +` + +const defaultSysprobeConfig = ` +gpu_monitoring: + enabled: true +` + +type provisionerParams struct { + agentOptions []agentparams.Option + ami string + amiOS os.Descriptor + instanceType string + dockerImages []string +} + +func getDefaultProvisionerParams() *provisionerParams { + return &provisionerParams{ + agentOptions: []agentparams.Option{ + agentparams.WithIntegration("gpu.d", defaultGpuCheckConfig), + agentparams.WithSystemProbeConfig(defaultSysprobeConfig), + }, + ami: gpuEnabledAMI, + amiOS: os.Ubuntu2204, + instanceType: gpuInstanceType, + dockerImages: []string{cudaSanityCheckImage}, + } +} + +func gpuInstanceProvisioner(params *provisionerParams) e2e.Provisioner { + return e2e.NewTypedPulumiProvisioner[environments.Host]("gpu", func(ctx *pulumi.Context, env *environments.Host) error { + name := "gpuvm" + awsEnv, err := aws.NewEnvironment(ctx) + if err != nil { + return err + } + + // Create the EC2 instance + host, err := ec2.NewVM(awsEnv, name, + ec2.WithInstanceType(params.instanceType), + ec2.WithAMI(params.ami, params.amiOS, os.AMD64Arch), + ) + if err != nil { + return err + } + err = host.Export(ctx, &env.RemoteHost.HostOutput) + if err != nil { + return err + } + + // Create the fakeintake instance + fakeIntake, err := fakeintake.NewECSFargateInstance(awsEnv, name) + if err != nil { + return err + } + err = fakeIntake.Export(ctx, &env.FakeIntake.FakeintakeOutput) + if err != nil { + return err + } + + // install the ECR credentials helper + // required to get pipeline agent images or other internally hosted images + installEcrCredsHelperCmd, err := ec2.InstallECRCredentialsHelper(awsEnv, host) + if err != nil { + return err + } + + // Validate GPU devices + validateGPUDevicesCmd, err := validateGPUDevices(awsEnv, host) + if err != nil { + return err + } + + // Install Docker (only after GPU devices are validated and the ECR credentials helper is installed) + dockerManager, err := docker.NewManager(&awsEnv, host, utils.PulumiDependsOn(installEcrCredsHelperCmd)) + if err != nil { + return err + } + + // Pull all the docker images required for the tests + dockerPullCmds, err := downloadDockerImages(awsEnv, host, params.dockerImages, dockerManager) + if err != nil { + return err + } + + // Validate that Docker can run CUDA samples + dockerCudaDeps := append(dockerPullCmds, validateGPUDevicesCmd...) + err = validateDockerCuda(awsEnv, host, dockerCudaDeps...) + if err != nil { + return err + } + + // Combine agent options from the parameters with the fakeintake and docker dependencies + params.agentOptions = append(params.agentOptions, + agentparams.WithFakeintake(fakeIntake), + agentparams.WithPulumiResourceOptions(utils.PulumiDependsOn(dockerManager)), // Depend on Docker to avoid apt lock issues + ) + + // Set updater to nil as we're not using it + env.Updater = nil + + // Install the agent + agent, err := agent.NewHostAgent(&awsEnv, host, params.agentOptions...) + if err != nil { + return err + } + + err = agent.Export(ctx, &env.Agent.HostAgentOutput) + if err != nil { + return err + } + + return nil + }, nil) +} + +// validateGPUDevices checks that there are GPU devices present and accesible +func validateGPUDevices(e aws.Environment, vm *remote.Host) ([]pulumi.Resource, error) { + commands := map[string]string{ + "pci": fmt.Sprintf("lspci -d %s:: | grep NVIDIA", nvidiaPCIVendorID), + "driver": "lsmod | grep nvidia", + "nvidia": "nvidia-smi -L | grep GPU", + } + + var cmds []pulumi.Resource + + for name, cmd := range commands { + c, err := vm.OS.Runner().Command( + e.CommonNamer().ResourceName("gpu-validate", name), + &command.Args{ + Create: pulumi.Sprintf("%s && %s", validationCommandMarker, cmd), + Sudo: false, + }, + ) + if err != nil { + return nil, err + } + cmds = append(cmds, c) + } + + return cmds, nil +} + +func downloadDockerImages(e aws.Environment, vm *remote.Host, images []string, dependsOn ...pulumi.Resource) ([]pulumi.Resource, error) { + var cmds []pulumi.Resource + + for i, image := range images { + cmd, err := vm.OS.Runner().Command( + e.CommonNamer().ResourceName("docker-pull", strconv.Itoa(i)), + &command.Args{ + Create: pulumi.Sprintf("docker pull %s", image), + }, + utils.PulumiDependsOn(dependsOn...), + ) + if err != nil { + return nil, err + } + + cmds = append(cmds, cmd) + } + + return cmds, nil +} + +func validateDockerCuda(e aws.Environment, vm *remote.Host, dependsOn ...pulumi.Resource) error { + _, err := vm.OS.Runner().Command( + e.CommonNamer().ResourceName("docker-cuda-validate"), + &command.Args{ + Create: pulumi.Sprintf("%s && docker run --gpus all --rm %s bash -c \"%s\"", validationCommandMarker, cudaSanityCheckImage, nvidiaSMIValidationCmd), + }, + utils.PulumiDependsOn(dependsOn...), + ) + + return err +} From 72fae75fc7f6f2f2257487178e3fd5a16818af8d Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 16 Dec 2024 13:35:13 +0100 Subject: [PATCH 19/78] pkg/util/trivy: gate additional containerd code under build tag (#32205) --- pkg/util/trivy/containerd.go | 2 +- pkg/util/trivy/trivy.go | 3 --- pkg/util/trivy/trivy_containerd.go | 4 ++++ 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pkg/util/trivy/containerd.go b/pkg/util/trivy/containerd.go index 94b8b7dab0f8b..d3ed7ddf3f84c 100644 --- a/pkg/util/trivy/containerd.go +++ b/pkg/util/trivy/containerd.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build trivy +//go:build trivy && containerd package trivy diff --git a/pkg/util/trivy/trivy.go b/pkg/util/trivy/trivy.go index 5709f4d384657..e78690f98ab6b 100644 --- a/pkg/util/trivy/trivy.go +++ b/pkg/util/trivy/trivy.go @@ -19,7 +19,6 @@ import ( "sort" "strings" "sync" - "time" "github.com/containerd/containerd/mount" @@ -53,8 +52,6 @@ import ( ) const ( - cleanupTimeout = 30 * time.Second - OSAnalyzers = "os" // OSAnalyzers defines an OS analyzer LanguagesAnalyzers = "languages" // LanguagesAnalyzers defines a language analyzer SecretAnalyzers = "secret" // SecretAnalyzers defines a secret analyzer diff --git a/pkg/util/trivy/trivy_containerd.go b/pkg/util/trivy/trivy_containerd.go index 6611a56c217dc..1a1a14345420b 100644 --- a/pkg/util/trivy/trivy_containerd.go +++ b/pkg/util/trivy/trivy_containerd.go @@ -24,6 +24,10 @@ import ( "github.com/containerd/errdefs" ) +const ( + cleanupTimeout = 30 * time.Second +) + // ContainerdAccessor is a function that should return a containerd client type ContainerdAccessor func() (cutil.ContainerdItf, error) From 21cd52ea94f0ec2f9581b9d330c0d7d198b4e639 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 16 Dec 2024 14:15:17 +0100 Subject: [PATCH 20/78] pkg/util/trivy: remove some `//nolint` comments (#32212) --- pkg/util/trivy/overlayfs.go | 2 +- pkg/util/trivy/trivy_containerd.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/util/trivy/overlayfs.go b/pkg/util/trivy/overlayfs.go index 8c5a73cf8e3ea..f1f8e243983cc 100644 --- a/pkg/util/trivy/overlayfs.go +++ b/pkg/util/trivy/overlayfs.go @@ -23,7 +23,7 @@ import ( // whiteoutCharDev is defined as zero and is not const only for testing as it // is not allowed to mknod a 0/0 char dev in userns. -var whiteoutCharDev uint64 = 0 //nolint:revive +var whiteoutCharDev uint64 // = 0 var whiteout *fs.DirEntry diff --git a/pkg/util/trivy/trivy_containerd.go b/pkg/util/trivy/trivy_containerd.go index 1a1a14345420b..bd036354c514d 100644 --- a/pkg/util/trivy/trivy_containerd.go +++ b/pkg/util/trivy/trivy_containerd.go @@ -86,8 +86,7 @@ func (c *Collector) ScanContainerdImage(ctx context.Context, imgMeta *workloadme // ScanContainerdImageFromFilesystem scans containerd image from file-system func (c *Collector) ScanContainerdImageFromFilesystem(ctx context.Context, imgMeta *workloadmeta.ContainerImageMetadata, img containerd.Image, client cutil.ContainerdItf, scanOptions sbom.ScanOptions) (sbom.Report, error) { - //nolint:gosimple // TODO(CINT) Fix go simple linte - imagePath, err := os.MkdirTemp(os.TempDir(), fmt.Sprintf("containerd-image-*")) + imagePath, err := os.MkdirTemp("", "containerd-image-*") if err != nil { return nil, fmt.Errorf("unable to create temp dir, err: %w", err) } From 918a8c3b32ae0f0e4046dd0cc85394c2fcdf4187 Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Mon, 16 Dec 2024 14:18:17 +0100 Subject: [PATCH 21/78] upgrade(installer): Support apm libraries configs (#31841) --- cmd/installer/subcommands/daemon/status.tmpl | 10 +- pkg/fleet/daemon/daemon.go | 52 +++++++-- pkg/fleet/installer/installer.go | 13 ++- pkg/fleet/installer/packages/datadog_agent.go | 1 + pkg/fleet/internal/cdn/cdn.go | 34 +++++- .../internal/cdn/config_datadog_agent.go | 27 +---- ...og_apm.go => config_datadog_apm_inject.go} | 20 ++-- .../cdn/config_datadog_apm_libraries.go | 101 ++++++++++++++++++ .../installer/unix/upgrade_scenario_test.go | 4 + 9 files changed, 212 insertions(+), 50 deletions(-) rename pkg/fleet/internal/cdn/{config_datadog_apm.go => config_datadog_apm_inject.go} (83%) create mode 100644 pkg/fleet/internal/cdn/config_datadog_apm_libraries.go diff --git a/cmd/installer/subcommands/daemon/status.tmpl b/cmd/installer/subcommands/daemon/status.tmpl index 045b819d53764..02481dea5c50d 100644 --- a/cmd/installer/subcommands/daemon/status.tmpl +++ b/cmd/installer/subcommands/daemon/status.tmpl @@ -1,12 +1,12 @@ Datadog Installer v{{ htmlSafe .Version }} {{ range $name, $package := .Packages }} {{ boldText $name }} - State: {{ if $package.Experiment -}}{{ yellowText "Upgrading" }}{{- else if $package.Stable -}}{{ greenText "OK" }}{{- else -}}{{ redText "no stable version" }}{{- end }} + State: {{ if $package.Experiment -}}{{ yellowText "Upgrading" }}{{- else if $package.Stable -}}{{ greenText "OK" }}{{- else -}}config only{{- end }} Installed versions: {{- if $package.Stable }} {{ greenText "●" }} stable: v{{ htmlSafe $package.Stable }} {{- else }} - {{ redText "●" }} stable: none + ● stable: none {{- end }} {{- if $package.Experiment }} {{ yellowText "●" }} experiment: v{{ htmlSafe $package.Experiment }} @@ -23,9 +23,9 @@ Datadog Installer v{{ htmlSafe .Version }} Remote configuration client state: StableVersion: {{ $remoteConfig.StableVersion }} ExperimentVersion: {{ $remoteConfig.ExperimentVersion }} - StableConfigVersion: {{ $remoteConfig.StableConfigVersion }} - ExperimentConfigVersion: {{ $remoteConfig.ExperimentConfigVersion }} - RemoteConfigVersion: {{ $remoteConfig.RemoteConfigVersion }} + StableConfigVersion: {{ if $remoteConfig.StableConfigState }}{{ $remoteConfig.StableConfigState.Version }}{{ else }}{{ "" }}{{ end }} + ExperimentConfigVersion: {{ if $remoteConfig.ExperimentConfigState }}{{ $remoteConfig.ExperimentConfigState.Version }}{{ else }}{{ "" }}{{ end }} + RemoteConfigVersion: {{ if $remoteConfig.RemoteConfigState }}{{ $remoteConfig.RemoteConfigState.Version }}{{ else }}{{ "" }}{{ end }} Task: {{- if $remoteConfig.Task }} Id: {{ $remoteConfig.Task.Id }} diff --git a/pkg/fleet/daemon/daemon.go b/pkg/fleet/daemon/daemon.go index 63ed3e2a3a626..ba79d5456dd3e 100644 --- a/pkg/fleet/daemon/daemon.go +++ b/pkg/fleet/daemon/daemon.go @@ -140,7 +140,34 @@ func (d *daemonImpl) GetState() (map[string]repository.State, error) { d.m.Lock() defer d.m.Unlock() - return d.installer.States() + states, err := d.installer.States() + if err != nil { + return nil, err + } + + var configStates map[string]repository.State + if d.env.RemotePolicies { + configStates, err = d.installer.ConfigStates() + if err != nil { + return nil, err + } + } + + res := make(map[string]repository.State) + for pkg, state := range states { + res[pkg] = state + } + for pkg, state := range configStates { + if _, ok := res[pkg]; !ok { + res[pkg] = repository.State{ + Stable: "", + Experiment: "", + StablePoliciesState: state.StablePoliciesState, + ExperimentPoliciesState: state.ExperimentPoliciesState, + } + } + } + return res, nil } // GetRemoteConfigState returns the remote config state. @@ -599,17 +626,24 @@ func (d *daemonImpl) refreshState(ctx context.Context) { log.Errorf("could not get available size: %v", err) } + for pkg, configState := range configState { + if _, ok := state[pkg]; !ok { + state[pkg] = repository.State{} + } + tmp := state[pkg] + tmp.StablePoliciesState = configState.StablePoliciesState + tmp.ExperimentPoliciesState = configState.ExperimentPoliciesState + state[pkg] = tmp + } + var packages []*pbgo.PackageState for pkg, s := range state { p := &pbgo.PackageState{ - Package: pkg, - StableVersion: s.Stable, - ExperimentVersion: s.Experiment, - } - cs, hasConfig := configState[pkg] - if hasConfig { - p.StableConfigState = cs.StablePoliciesState - p.ExperimentConfigState = cs.ExperimentPoliciesState + Package: pkg, + StableVersion: s.Stable, + ExperimentVersion: s.Experiment, + StableConfigState: s.StablePoliciesState, + ExperimentConfigState: s.ExperimentPoliciesState, } configState, err := d.resolveRemoteConfigVersion(ctx, pkg) diff --git a/pkg/fleet/installer/installer.go b/pkg/fleet/installer/installer.go index 3dd39b8fddb81..d9a2a910f45ef 100644 --- a/pkg/fleet/installer/installer.go +++ b/pkg/fleet/installer/installer.go @@ -34,6 +34,7 @@ import ( const ( packageDatadogAgent = "datadog-agent" packageAPMInjector = "datadog-apm-inject" + packageAPMLibraries = "datadog-apm-libraries" packageDatadogInstaller = "datadog-installer" ) @@ -210,6 +211,16 @@ func (i *installerImpl) Install(ctx context.Context, url string, args []string) if err != nil { return fmt.Errorf("could not configure package: %w", err) } + if pkg.Name == packageDatadogInstaller { + // We must handle the configuration of some packages that are not + // don't have an OCI. To properly configure their configuration repositories, + // we call configurePackage when setting up the installer; which is the only + // package that is always installed. + err = i.configurePackage(ctx, packageAPMLibraries) + if err != nil { + return fmt.Errorf("could not configure package: %w", err) + } + } err = i.setupPackage(ctx, pkg.Name, args) // Postinst if err != nil { return fmt.Errorf("could not setup package: %w", err) @@ -662,7 +673,7 @@ func (i *installerImpl) configurePackage(ctx context.Context, pkg string) (err e defer func() { span.Finish(err) }() switch pkg { - case packageDatadogAgent, packageAPMInjector: + case packageDatadogAgent, packageAPMInjector, packageAPMLibraries: config, err := i.cdn.Get(ctx, pkg) if err != nil { return fmt.Errorf("could not get %s CDN config: %w", pkg, err) diff --git a/pkg/fleet/installer/packages/datadog_agent.go b/pkg/fleet/installer/packages/datadog_agent.go index 34dbac394050f..c3b56f0e9c87b 100644 --- a/pkg/fleet/installer/packages/datadog_agent.go +++ b/pkg/fleet/installer/packages/datadog_agent.go @@ -60,6 +60,7 @@ var ( "system-probe.yaml", "inject/tracer.yaml", "inject", + "managed", } // matches omnibus/package-scripts/agent-deb/postinst rootOwnedAgentPaths = []string{ diff --git a/pkg/fleet/internal/cdn/cdn.go b/pkg/fleet/internal/cdn/cdn.go index 4f65108dfa90f..f35418cc0b7bc 100644 --- a/pkg/fleet/internal/cdn/cdn.go +++ b/pkg/fleet/internal/cdn/cdn.go @@ -7,6 +7,7 @@ package cdn import ( + "bytes" "context" "encoding/json" "errors" @@ -18,9 +19,13 @@ import ( "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" + "gopkg.in/yaml.v2" ) -const policyMetadataFilename = "policy.metadata" +const ( + policyMetadataFilename = "policy.metadata" + doNotEditDisclaimer = `# This configuration was generated by Datadog's Fleet Automation. DO NOT EDIT.` +) var ( // ErrProductNotSupported is returned when the product is not supported. @@ -134,7 +139,16 @@ func (c *CDN) Get(ctx context.Context, pkg string) (cfg Config, err error) { if err != nil { return nil, err } - cfg, err = newAPMConfig(c.hostTagsGetter.get(), orderedLayers...) + cfg, err = newAPMSSIConfig(c.hostTagsGetter.get(), orderedLayers...) + if err != nil { + return nil, err + } + case "datadog-apm-libraries": + orderedLayers, err := c.fetcher.get(ctx) + if err != nil { + return nil, err + } + cfg, err = newAPMLibrariesConfig(c.hostTagsGetter.get(), orderedLayers...) if err != nil { return nil, err } @@ -175,3 +189,19 @@ func writePolicyMetadata(config Config, dir string) error { } return nil } + +// marshalYAMLConfig marshals the config as YAML. +func marshalYAMLConfig(c map[string]interface{}) ([]byte, error) { + if len(c) == 0 { + return nil, nil + } + var b bytes.Buffer + b.WriteString(doNotEditDisclaimer) + b.WriteString("\n") + rawConfig, err := yaml.Marshal(c) + if err != nil { + return nil, err + } + b.Write(rawConfig) + return b.Bytes(), nil +} diff --git a/pkg/fleet/internal/cdn/config_datadog_agent.go b/pkg/fleet/internal/cdn/config_datadog_agent.go index b9882ebaad469..71e96d0d2655e 100644 --- a/pkg/fleet/internal/cdn/config_datadog_agent.go +++ b/pkg/fleet/internal/cdn/config_datadog_agent.go @@ -6,7 +6,6 @@ package cdn import ( - "bytes" "crypto/sha256" "encoding/json" "fmt" @@ -18,12 +17,10 @@ import ( pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" "github.com/DataDog/datadog-agent/pkg/util/log" - "gopkg.in/yaml.v2" ) const ( - layerKeys = "fleet_layers" - doNotEditDisclaimer = `# This configuration was generated by Datadog's Fleet Automation. DO NOT EDIT.` + layerKeys = "fleet_layers" configDatadogYAML = "datadog.yaml" configSecurityAgentYAML = "security-agent.yaml" @@ -106,15 +103,15 @@ func newAgentConfig(orderedLayers ...[]byte) (*agentConfig, error) { compiledLayer.AgentConfig[layerKeys] = policyIDs // Marshal into YAML configs - config, err := marshalAgentConfig(compiledLayer.AgentConfig) + config, err := marshalYAMLConfig(compiledLayer.AgentConfig) if err != nil { return nil, err } - securityAgentConfig, err := marshalAgentConfig(compiledLayer.SecurityAgentConfig) + securityAgentConfig, err := marshalYAMLConfig(compiledLayer.SecurityAgentConfig) if err != nil { return nil, err } - systemProbeConfig, err := marshalAgentConfig(compiledLayer.SystemProbeConfig) + systemProbeConfig, err := marshalYAMLConfig(compiledLayer.SystemProbeConfig) if err != nil { return nil, err } @@ -182,22 +179,6 @@ func (a *agentConfig) Write(dir string) error { return writePolicyMetadata(a, dir) } -// marshalAgentConfig marshals the config as YAML. -func marshalAgentConfig(c map[string]interface{}) ([]byte, error) { - if len(c) == 0 { - return nil, nil - } - var b bytes.Buffer - b.WriteString(doNotEditDisclaimer) - b.WriteString("\n") - rawConfig, err := yaml.Marshal(c) - if err != nil { - return nil, err - } - b.Write(rawConfig) - return b.Bytes(), nil -} - // getAgentIDs returns the UID and GID of the dd-agent user and group. func getAgentIDs() (uid, gid int, err error) { ddAgentUser, err := user.Lookup("dd-agent") diff --git a/pkg/fleet/internal/cdn/config_datadog_apm.go b/pkg/fleet/internal/cdn/config_datadog_apm_inject.go similarity index 83% rename from pkg/fleet/internal/cdn/config_datadog_apm.go rename to pkg/fleet/internal/cdn/config_datadog_apm_inject.go index fd171f06e0d92..f5cc04894b7f7 100644 --- a/pkg/fleet/internal/cdn/config_datadog_apm.go +++ b/pkg/fleet/internal/cdn/config_datadog_apm_inject.go @@ -21,37 +21,37 @@ const ( injectorConfigFilename = "injector.msgpack" ) -// apmConfig represents the injector configuration from the CDN. -type apmConfig struct { +// apmSSIConfig represents the injector configuration from the CDN. +type apmSSIConfig struct { version string policyIDs []string injectorConfig []byte } -// apmConfigLayer is a config layer that can be merged with other layers into a config. -type apmConfigLayer struct { +// apmSSIConfigLayer is a config layer that can be merged with other layers into a config. +type apmSSIConfigLayer struct { ID string `json:"name"` InjectorConfig map[string]interface{} `json:"apm_ssi_config"` } // State returns the APM configs state -func (i *apmConfig) State() *pbgo.PoliciesState { +func (i *apmSSIConfig) State() *pbgo.PoliciesState { return &pbgo.PoliciesState{ MatchedPolicies: i.policyIDs, Version: i.version, } } -func newAPMConfig(hostTags []string, orderedLayers ...[]byte) (*apmConfig, error) { +func newAPMSSIConfig(hostTags []string, orderedLayers ...[]byte) (*apmSSIConfig, error) { // Compile ordered layers into a single config // TODO: maybe we don't want that and we should reject if there are more than one config? policyIDs := []string{} - compiledLayer := &apmConfigLayer{ + compiledLayer := &apmSSIConfigLayer{ InjectorConfig: map[string]interface{}{}, } for _, rawLayer := range orderedLayers { - layer := &apmConfigLayer{} + layer := &apmSSIConfigLayer{} if err := json.Unmarshal(rawLayer, layer); err != nil { log.Warnf("Failed to unmarshal layer: %v", err) continue @@ -84,7 +84,7 @@ func newAPMConfig(hostTags []string, orderedLayers ...[]byte) (*apmConfig, error return nil, err } - return &apmConfig{ + return &apmSSIConfig{ version: fmt.Sprintf("%x", hash.Sum(nil)), policyIDs: policyIDs, @@ -93,7 +93,7 @@ func newAPMConfig(hostTags []string, orderedLayers ...[]byte) (*apmConfig, error } // Write writes the agent configuration to the given directory. -func (i *apmConfig) Write(dir string) error { +func (i *apmSSIConfig) Write(dir string) error { if i.injectorConfig != nil { err := os.WriteFile(filepath.Join(dir, injectorConfigFilename), []byte(i.injectorConfig), 0644) // Must be world readable if err != nil { diff --git a/pkg/fleet/internal/cdn/config_datadog_apm_libraries.go b/pkg/fleet/internal/cdn/config_datadog_apm_libraries.go new file mode 100644 index 0000000000000..bf31d18bd69a3 --- /dev/null +++ b/pkg/fleet/internal/cdn/config_datadog_apm_libraries.go @@ -0,0 +1,101 @@ +// 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 cdn + +import ( + "crypto/sha256" + "encoding/json" + "fmt" + "os" + "path/filepath" + + pbgo "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" + "github.com/DataDog/datadog-agent/pkg/util/log" +) + +const ( + apmLibrariesConfigPath = "libraries_config.yaml" +) + +// apmLibrariesConfig represents the injector configuration from the CDN. +type apmLibrariesConfig struct { + version string + policyIDs []string + + apmLibrariesConfig []byte +} + +// apmLibrariesConfigLayer is a config layer that can be merged with other layers into a config. +type apmLibrariesConfigLayer struct { + ID string `json:"name"` + APMLibrariesConfig map[string]interface{} `json:"apm_libraries_config"` +} + +// State returns the APM configs state +func (i *apmLibrariesConfig) State() *pbgo.PoliciesState { + return &pbgo.PoliciesState{ + MatchedPolicies: i.policyIDs, + Version: i.version, + } +} + +func newAPMLibrariesConfig(hostTags []string, orderedLayers ...[]byte) (*apmLibrariesConfig, error) { + // Compile ordered layers into a single config + policyIDs := []string{} + compiledLayer := &apmLibrariesConfigLayer{ + APMLibrariesConfig: map[string]interface{}{}, + } + for _, rawLayer := range orderedLayers { + layer := &apmLibrariesConfigLayer{} + if err := json.Unmarshal(rawLayer, layer); err != nil { + log.Warnf("Failed to unmarshal layer: %v", err) + continue + } + + if layer.APMLibrariesConfig != nil { + cfg, err := merge(compiledLayer.APMLibrariesConfig, layer.APMLibrariesConfig) + if err != nil { + return nil, err + } + compiledLayer.APMLibrariesConfig = cfg.(map[string]interface{}) + policyIDs = append(policyIDs, layer.ID) + } + } + + hash := sha256.New() + version, err := json.Marshal(compiledLayer) + if err != nil { + return nil, err + } + hash.Write(version) + + // Add host tags AFTER compiling the version -- we don't want to trigger noop updates + compiledLayer.APMLibrariesConfig["host_tags"] = hostTags + + // Marshal into msgpack configs + yamlCfg, err := marshalYAMLConfig(compiledLayer.APMLibrariesConfig) + if err != nil { + return nil, err + } + + return &apmLibrariesConfig{ + version: fmt.Sprintf("%x", hash.Sum(nil)), + policyIDs: policyIDs, + + apmLibrariesConfig: yamlCfg, + }, nil +} + +// Write writes the agent configuration to the given directory. +func (i *apmLibrariesConfig) Write(dir string) error { + if i.apmLibrariesConfig != nil { + err := os.WriteFile(filepath.Join(dir, apmLibrariesConfigPath), []byte(i.apmLibrariesConfig), 0644) // Must be world readable + if err != nil { + return fmt.Errorf("could not write %s: %w", apmLibrariesConfigPath, err) + } + } + return writePolicyMetadata(i, dir) +} diff --git a/test/new-e2e/tests/installer/unix/upgrade_scenario_test.go b/test/new-e2e/tests/installer/unix/upgrade_scenario_test.go index 74d3b888cab58..6230ef741410c 100644 --- a/test/new-e2e/tests/installer/unix/upgrade_scenario_test.go +++ b/test/new-e2e/tests/installer/unix/upgrade_scenario_test.go @@ -404,6 +404,10 @@ func (s *upgradeScenarioSuite) TestConfigUpgradeSuccessful() { s.host.WaitForFileExists(true, "/opt/datadog-packages/run/installer.sock") state := s.host.State() + // Assert setup successful + state.AssertDirExists("/etc/datadog-agent/managed", 0755, "root", "root") + state.AssertDirExists("/etc/datadog-agent/managed/datadog-apm-libraries", 0755, "root", "root") + state.AssertDirExists("/etc/datadog-agent/managed/datadog-agent", 0755, "root", "root") state.AssertSymlinkExists("/etc/datadog-agent/managed/datadog-agent/stable", "/etc/datadog-agent/managed/datadog-agent/e94406c45ae766b7d34d2793e4759b9c4d15ed5d5e2b7f73ce1bf0e6836f728d", "root", "root") // Verify metadata state.AssertFileExists("/etc/datadog-agent/managed/datadog-agent/e94406c45ae766b7d34d2793e4759b9c4d15ed5d5e2b7f73ce1bf0e6836f728d/policy.metadata", 0440, "dd-agent", "dd-agent") From e681f1f73fdf7c01797954f37ee7aff9e64b19fa Mon Sep 17 00:00:00 2001 From: Adel Haj Hassan <41540817+adel121@users.noreply.github.com> Date: Mon, 16 Dec 2024 15:03:41 +0100 Subject: [PATCH 22/78] unify inclusion/exclusion logic for container images (#32013) Co-authored-by: DeForest Richards <56796055+drichards-87@users.noreply.github.com> --- .../corechecks/containers/generic/filters.go | 2 +- .../containers/kubelet/common/pod.go | 2 +- pkg/process/util/containers/containers.go | 4 +- pkg/util/containers/filter.go | 23 +++++++++ pkg/util/containers/filter_test.go | 51 ++++++++++++++++--- ...e_filters_consistent-14fd44a352723e50.yaml | 16 ++++++ 6 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 releasenotes/notes/make_image_filters_consistent-14fd44a352723e50.yaml diff --git a/pkg/collector/corechecks/containers/generic/filters.go b/pkg/collector/corechecks/containers/generic/filters.go index 788a88664bcb5..fbf0fb544e25a 100644 --- a/pkg/collector/corechecks/containers/generic/filters.go +++ b/pkg/collector/corechecks/containers/generic/filters.go @@ -56,7 +56,7 @@ func (f LegacyContainerFilter) IsExcluded(container *workloadmeta.Container) boo annotations = pod.Annotations } - return f.OldFilter.IsExcluded(annotations, container.Name, container.Image.Name, container.Labels[kubernetes.CriContainerNamespaceLabel]) + return f.OldFilter.IsExcluded(annotations, container.Name, container.Image.RawName, container.Labels[kubernetes.CriContainerNamespaceLabel]) } // RuntimeContainerFilter filters containers by runtime diff --git a/pkg/collector/corechecks/containers/kubelet/common/pod.go b/pkg/collector/corechecks/containers/kubelet/common/pod.go index f0689b2c852d1..da2706b143de5 100644 --- a/pkg/collector/corechecks/containers/kubelet/common/pod.go +++ b/pkg/collector/corechecks/containers/kubelet/common/pod.go @@ -190,7 +190,7 @@ func GetContainerID(store workloadmeta.Component, metric model.Metric, filter *c return "", ErrContainerNotFound } - if filter.IsExcluded(pod.EntityMeta.Annotations, container.Name, container.Image.Name, pod.Namespace) { + if filter.IsExcluded(pod.EntityMeta.Annotations, container.Name, container.Image.RawName, pod.Namespace) { return "", ErrContainerExcluded } diff --git a/pkg/process/util/containers/containers.go b/pkg/process/util/containers/containers.go index 2beafeced27f5..c5ad7cae1c9b3 100644 --- a/pkg/process/util/containers/containers.go +++ b/pkg/process/util/containers/containers.go @@ -120,7 +120,7 @@ func (p *containerProvider) GetContainers(cacheValidity time.Duration, previousC annotations = pod.Annotations } - if p.filter != nil && p.filter.IsExcluded(annotations, container.Name, container.Image.Name, container.Labels[kubernetes.CriContainerNamespaceLabel]) { + if p.filter != nil && p.filter.IsExcluded(annotations, container.Name, container.Image.RawName, container.Labels[kubernetes.CriContainerNamespaceLabel]) { continue } @@ -209,7 +209,7 @@ func (p *containerProvider) GetPidToCid(cacheValidity time.Duration) map[int]str annotations = pod.Annotations } - if p.filter != nil && p.filter.IsExcluded(annotations, container.Name, container.Image.Name, container.Labels[kubernetes.CriContainerNamespaceLabel]) { + if p.filter != nil && p.filter.IsExcluded(annotations, container.Name, container.Image.RawName, container.Labels[kubernetes.CriContainerNamespaceLabel]) { continue } diff --git a/pkg/util/containers/filter.go b/pkg/util/containers/filter.go index 1e2ab52934e77..63676bc59533d 100644 --- a/pkg/util/containers/filter.go +++ b/pkg/util/containers/filter.go @@ -112,6 +112,7 @@ func parseFilters(filters []string) (imageFilters, nameFilters, namespaceFilters for _, filter := range filters { switch { case strings.HasPrefix(filter, imageFilterPrefix): + filter = preprocessImageFilter(filter) r, err := filterToRegex(filter, imageFilterPrefix) if err != nil { filterErrs = append(filterErrs, err.Error()) @@ -145,6 +146,19 @@ func parseFilters(filters []string) (imageFilters, nameFilters, namespaceFilters return imageFilters, nameFilters, namespaceFilters, filterWarnings, nil } +// preprocessImageFilter modifies image filters having the format `name$`, where {name} doesn't include a colon (e.g. nginx$, ^nginx$), to +// `name:.*`. +// This is done so that image filters can still match even if the matched image contains the tag or digest. +func preprocessImageFilter(imageFilter string) string { + regexVal := strings.TrimPrefix(imageFilter, imageFilterPrefix) + if strings.HasSuffix(regexVal, "$") && !strings.Contains(regexVal, ":") { + mutatedRegexVal := regexVal[:len(regexVal)-1] + "(@sha256)?:.*" + return imageFilterPrefix + mutatedRegexVal + } + + return imageFilter +} + // filterToRegex checks a filter's regex func filterToRegex(filter string, filterPrefix string) (*regexp.Regexp, error) { pat := strings.TrimPrefix(filter, filterPrefix) @@ -327,7 +341,16 @@ func NewAutodiscoveryFilter(ft FilterType) (*Filter, error) { // based on the filters in the containerFilter instance. Consider also using // Note: exclude filters are not applied to empty container names, empty // images and empty namespaces. +// +// containerImage may or may not contain the image tag or image digest. (e.g. nginx:latest and nginx are both valid) func (cf Filter) IsExcluded(annotations map[string]string, containerName, containerImage, podNamespace string) bool { + + // If containerImage doesn't include the tag or digest, add a colon so that it + // can match image filters + if len(containerImage) > 0 && !strings.Contains(containerImage, ":") { + containerImage += ":" + } + if cf.isExcludedByAnnotation(annotations, containerName) { return true } diff --git a/pkg/util/containers/filter_test.go b/pkg/util/containers/filter_test.go index 1c1d768a4dfc0..ea33a72134f46 100644 --- a/pkg/util/containers/filter_test.go +++ b/pkg/util/containers/filter_test.go @@ -28,6 +28,14 @@ func TestFilter(t *testing.T) { c ctnDef ns string }{ + { + c: ctnDef{ + ID: "0", + Name: "container-with-sha", + Image: "docker-dd-agent@sha256:1892862abcdef61516516", + }, + ns: "default", + }, { c: ctnDef{ ID: "1", @@ -268,25 +276,33 @@ func TestFilter(t *testing.T) { expectedIDs []string }{ { - expectedIDs: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + expectedIDs: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, { - excludeList: []string{"name:secret"}, + excludeList: []string{"image:^docker-dd-agent$"}, expectedIDs: []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, + { + excludeList: []string{"image:^apache$"}, + expectedIDs: []string{"0", "1", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + }, + { + excludeList: []string{"name:secret"}, + expectedIDs: []string{"0", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + }, { excludeList: []string{"image:secret"}, - expectedIDs: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + expectedIDs: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, { includeList: []string{}, excludeList: []string{"image:apache", "image:alpine"}, - expectedIDs: []string{"1", "3", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + expectedIDs: []string{"0", "1", "3", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, { includeList: []string{"name:mysql"}, excludeList: []string{"name:dd"}, - expectedIDs: []string{"3", "5", "6", "7", "8", "9", "10", "11", "12", "13", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + expectedIDs: []string{"0", "3", "5", "6", "7", "8", "9", "10", "11", "12", "13", "16", "17", "18", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, { excludeList: []string{"kube_namespace:.*"}, @@ -295,7 +311,7 @@ func TestFilter(t *testing.T) { }, { excludeList: []string{"kube_namespace:bar"}, - expectedIDs: []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, + expectedIDs: []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "19", "20", "23", "24", "25", "26", "27", "28", "29", "30", "31"}, }, { excludeList: []string{"name:.*"}, @@ -305,7 +321,17 @@ func TestFilter(t *testing.T) { { excludeList: []string{"image:.*"}, includeList: []string{"image:docker-dd-agent"}, - expectedIDs: []string{"1", "30"}, + expectedIDs: []string{"0", "1", "30"}, + }, + { + excludeList: []string{"image:.*"}, + includeList: []string{"image:^docker-dd-agent$"}, + expectedIDs: []string{"0", "1", "30"}, + }, + { + excludeList: []string{"image:.*"}, + includeList: []string{"image:^docker-dd-agent$"}, + expectedIDs: []string{"0", "1", "30"}, }, // Test kubernetes defaults { @@ -326,7 +352,7 @@ func TestFilter(t *testing.T) { pauseContainerUpstream, pauseContainerCDK, }, - expectedIDs: []string{"1", "2", "3", "4", "5", "14", "15", "29", "30", "31"}, + expectedIDs: []string{"0", "1", "2", "3", "4", "5", "14", "15", "29", "30", "31"}, }, } { t.Run("", func(t *testing.T) { @@ -624,6 +650,15 @@ func TestParseFilters(t *testing.T) { expectedErrMsg: nil, filterErrors: nil, }, + { + desc: "valid filters, image filter strict match without tag or digest", + filters: []string{"image:^nginx$", "name:xyz-.*", "kube_namespace:sandbox.*", "name:abc"}, + imageFilters: []*regexp.Regexp{regexp.MustCompile("^nginx(@sha256)?:.*")}, + nameFilters: []*regexp.Regexp{regexp.MustCompile("xyz-.*"), regexp.MustCompile("abc")}, + namespaceFilters: []*regexp.Regexp{regexp.MustCompile("sandbox.*")}, + expectedErrMsg: nil, + filterErrors: nil, + }, { desc: "invalid regex", filters: []string{"image:apache.*", "name:a(?=b)", "kube_namespace:sandbox.*", "name:abc"}, diff --git a/releasenotes/notes/make_image_filters_consistent-14fd44a352723e50.yaml b/releasenotes/notes/make_image_filters_consistent-14fd44a352723e50.yaml new file mode 100644 index 0000000000000..d0b292f5f2267 --- /dev/null +++ b/releasenotes/notes/make_image_filters_consistent-14fd44a352723e50.yaml @@ -0,0 +1,16 @@ +# 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: + - | + Fixes consistency issue with container image filters. + Depending on the Agent configuration, filters were sometimes behaving differently + for metrics and logs. For example, an image filter that worked for excluding logs + didn't work when used to exclude metrics, and vice versa. + The exclusion logic is now consistent between metrics and logs. + From e56059fbac2741b4a6ee33926ad9849ef9c20e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9lian=20Raimbault?= <161456554+CelianR@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:06:46 -0500 Subject: [PATCH 23/78] Remove interactive questions for release.create-rc (#32127) --- tasks/libs/common/git.py | 40 +++++++++++++++++++++++++++++++--------- tasks/release.py | 33 ++++++++++++++++----------------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/tasks/libs/common/git.py b/tasks/libs/common/git.py index dfff77eda03f7..b50184c3af411 100644 --- a/tasks/libs/common/git.py +++ b/tasks/libs/common/git.py @@ -1,8 +1,10 @@ from __future__ import annotations import os +import sys import tempfile from contextlib import contextmanager +from time import sleep from typing import TYPE_CHECKING from invoke import Context @@ -161,25 +163,45 @@ def check_base_branch(branch, release_version): return branch == get_default_branch() or branch == release_version.branch() -def try_git_command(ctx, git_command): - """ - Try a git command that should be retried (after user confirmation) if it fails. +def try_git_command(ctx, git_command, non_interactive_retries=2, non_interactive_delay=5): + """Try a git command that should be retried (after user confirmation) if it fails. Primarily useful for commands which can fail if commit signing fails: we don't want the whole workflow to fail if that happens, we want to retry. + + Args: + ctx: The invoke context. + git_command: The git command to run. + non_interactive_retries: The number of times to retry the command if it fails when running non-interactively. + non_interactive_delay: The delay in seconds to retry the command if it fails when running non-interactively. """ do_retry = True + n_retries = 0 + interactive = sys.stdin.isatty() while do_retry: res = ctx.run(git_command, warn=True) if res.exited is None or res.exited > 0: - print( - color_message( - f"Failed to run \"{git_command}\" (did the commit/tag signing operation fail?)", - "orange", + if interactive: + print( + color_message( + f"Failed to run \"{git_command}\" (did the commit/tag signing operation fail?)", + "orange", + ) + ) + do_retry = yes_no_question("Do you want to retry this operation?", color="orange", default=True) + else: + # Non interactive, retry in `non_interactive_delay` seconds if we haven't reached the limit + n_retries += 1 + if n_retries > non_interactive_retries: + print(f'{color_message("Error", Color.RED)}: Failed to run git command', file=sys.stderr) + return False + + print( + f'{color_message("Warning", Color.ORANGE)}: Retrying git command in {non_interactive_delay}s', + file=sys.stderr, ) - ) - do_retry = yes_no_question("Do you want to retry this operation?", color="orange", default=True) + sleep(non_interactive_delay) continue return True diff --git a/tasks/release.py b/tasks/release.py index b91e4a48742a6..aa16b7fd7045f 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -84,16 +84,21 @@ BACKPORT_LABEL_COLOR = "5319e7" -def deduce_and_ask_version(ctx, branch, as_str=True, trust=False) -> str | Version: +def deduce_version(ctx, branch, as_str=True, trust=False) -> str | Version: release_version = get_next_version_from_branch(ctx, branch, as_str=as_str) - if trust: - return release_version + print( + f'{color_message("Info", Color.BLUE)}: Version {release_version} deduced from branch {branch}', file=sys.stderr + ) - if not os.isatty(sys.stdin.fileno()) or yes_no_question( - f'Version {release_version} deduced from branch {branch}. Is this the version you want to use?', - color="orange", - default=False, + if ( + trust + or not os.isatty(sys.stdin.fileno()) + or yes_no_question( + 'Is this the version you want to use?', + color="orange", + default=False, + ) ): return release_version @@ -170,7 +175,7 @@ def update_modules(ctx, release_branch=None, version=None, trust=False): assert release_branch or version - agent_version = version or deduce_and_ask_version(ctx, release_branch, trust=trust) + agent_version = version or deduce_version(ctx, release_branch, trust=trust) with agent_context(ctx, release_branch, skip_checkout=release_branch is None): modules = get_default_modules() @@ -235,7 +240,7 @@ def tag_modules( assert release_branch or version - agent_version = version or deduce_and_ask_version(ctx, release_branch, trust=trust) + agent_version = version or deduce_version(ctx, release_branch, trust=trust) tags = [] with agent_context(ctx, release_branch, skip_checkout=release_branch is None): @@ -274,7 +279,7 @@ def tag_version( assert release_branch or version - agent_version = version or deduce_and_ask_version(ctx, release_branch, trust=trust) + agent_version = version or deduce_version(ctx, release_branch, trust=trust) # Always tag the main module force_option = __get_force_option(force) @@ -463,12 +468,6 @@ def create_rc(ctx, release_branch, patch_version=False, upstream="origin", slack # Step 1: Update release entries print(color_message("Updating release entries", "bold")) new_version = next_rc_version(ctx, major_version, patch_version) - if not yes_no_question( - f'Do you want to create release candidate with:\n- new version: {new_version}\n- new highest version: {new_highest_version}\n- new final version: {new_final_version}?', - color="bold", - default=False, - ): - raise Exit(color_message("Aborting.", "red"), code=1) update_release_json(new_version, new_final_version) @@ -1257,7 +1256,7 @@ def create_github_release(ctx, release_branch, draft=True): ) notes = [] - version = deduce_and_ask_version(ctx, release_branch) + version = deduce_version(ctx, release_branch) with agent_context(ctx, release_branch): for section, filename in sections: From f028703ea10c5417545fda6954a658bd41c2f976 Mon Sep 17 00:00:00 2001 From: Raphael Gavache Date: Mon, 16 Dec 2024 15:10:21 +0100 Subject: [PATCH 24/78] [fleet] fix telemetry (#32218) --- pkg/fleet/telemetry/telemetry.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/fleet/telemetry/telemetry.go b/pkg/fleet/telemetry/telemetry.go index df641722cb18a..5bbfd8ca773c4 100644 --- a/pkg/fleet/telemetry/telemetry.go +++ b/pkg/fleet/telemetry/telemetry.go @@ -219,10 +219,9 @@ func StartSpanFromIDs(ctx context.Context, operationName, traceID, parentID stri spanCtx, err := tracer.Extract(ctxCarrier) if err != nil { log.Debugf("failed to extract span context from install script params: %v", err) - return Span{tracer.StartSpan("remote_request")}, ctx + return StartSpanFromContext(ctx, operationName, spanOptions...) } spanOptions = append([]ddtrace.StartSpanOption{tracer.ChildOf(spanCtx)}, spanOptions...) - return StartSpanFromContext(ctx, operationName, spanOptions...) } From 35a9baf10bc26f19b9557afa3ee786b0e248a2ef Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Mon, 16 Dec 2024 15:16:58 +0100 Subject: [PATCH 25/78] chore(installer): Remove the amazon-ecr-credential-helper dependency (#32149) --- .gitlab/deploy_packages/e2e.yml | 26 ------------------- .gitlab/deploy_packages/oci.yml | 3 ++- .gitlab/e2e/e2e.yml | 16 ++++++------ LICENSE-3rdparty.csv | 11 -------- go.mod | 3 --- go.sum | 6 ----- pkg/fleet/installer/oci/download.go | 5 ---- .../installer/unix/package_definitions.go | 4 +-- 8 files changed, 12 insertions(+), 62 deletions(-) diff --git a/.gitlab/deploy_packages/e2e.yml b/.gitlab/deploy_packages/e2e.yml index a72844e37c245..78bb3286f1292 100644 --- a/.gitlab/deploy_packages/e2e.yml +++ b/.gitlab/deploy_packages/e2e.yml @@ -1,31 +1,5 @@ # Jobs that deploy agent packages on QA environment, to be used by e2e tests -qa_agent_oci: - extends: .docker_publish_job_definition - stage: deploy_packages - rules: - - !reference [.on_installer_or_e2e_changes] - - !reference [.manual] - needs: - - deploy_agent_oci - variables: - IMG_REGISTRIES: agent-qa - IMG_SOURCES: registry.ddbuild.io/ci/remote-updates/datadog-agent:pipeline-${CI_PIPELINE_ID} - IMG_DESTINATIONS: agent-package:pipeline-${CI_PIPELINE_ID} - -qa_installer_oci: - extends: .docker_publish_job_definition - stage: deploy_packages - rules: - - !reference [.on_installer_or_e2e_changes] - - !reference [.manual] - needs: - - deploy_installer_oci - variables: - IMG_REGISTRIES: agent-qa - IMG_SOURCES: registry.ddbuild.io/ci/remote-updates/datadog-installer:pipeline-${CI_PIPELINE_ID} - IMG_DESTINATIONS: installer-package:pipeline-${CI_PIPELINE_ID} - qa_installer_script: image: registry.ddbuild.io/ci/datadog-agent-buildimages/gitlab_agent_deploy$DATADOG_AGENT_BUILDIMAGES_SUFFIX:$DATADOG_AGENT_BUILDIMAGES stage: deploy_packages diff --git a/.gitlab/deploy_packages/oci.yml b/.gitlab/deploy_packages/oci.yml index 7db0433b743dd..bcefacac8d134 100644 --- a/.gitlab/deploy_packages/oci.yml +++ b/.gitlab/deploy_packages/oci.yml @@ -24,9 +24,10 @@ include: - datadog-package push registry.ddbuild.io/ci/remote-updates/${OCI_PRODUCT}:${VERSION} ${OMNIBUS_PACKAGE_DIR}/${OCI_PRODUCT}-${VERSION}.oci.tar # This is used for E2E tests. Doesn't cost more than an additional tag to the registry. - datadog-package push registry.ddbuild.io/ci/remote-updates/${OCI_PRODUCT}:pipeline-${CI_PIPELINE_ID} ${OMNIBUS_PACKAGE_DIR}/${OCI_PRODUCT}-${VERSION}.oci.tar - # Used for install scripts e2e tests + # Used for e2e tests - datadog-package replicate-s3 registry.ddbuild.io/ci/remote-updates/${OCI_PRODUCT}:pipeline-${CI_PIPELINE_ID} us-east-1 ${INSTALLER_TESTING_S3_BUCKET} ${S3_PACKAGE} ${VERSION} - datadog-package replicate-s3 registry.ddbuild.io/ci/remote-updates/${OCI_PRODUCT}:pipeline-${CI_PIPELINE_ID} us-east-1 ${INSTALLER_TESTING_S3_BUCKET} ${S3_PACKAGE} ${CI_COMMIT_SHA} + - datadog-package replicate-s3 registry.ddbuild.io/ci/remote-updates/${OCI_PRODUCT}:pipeline-${CI_PIPELINE_ID} us-east-1 ${INSTALLER_TESTING_S3_BUCKET} ${S3_PACKAGE} pipeline-${CI_PIPELINE_ID} variables: MAJOR_VERSION: 7 diff --git a/.gitlab/e2e/e2e.yml b/.gitlab/e2e/e2e.yml index b23939329f011..f944f407fa2dd 100644 --- a/.gitlab/e2e/e2e.yml +++ b/.gitlab/e2e/e2e.yml @@ -412,8 +412,8 @@ new-e2e-installer: - deploy_rpm_testing-a7_x64 - deploy_suse_rpm_testing_arm64-a7 - deploy_suse_rpm_testing_x64-a7 - - qa_installer_oci - - qa_agent_oci + - deploy_installer_oci + - deploy_agent_oci variables: TARGETS: ./tests/installer/unix TEAM: fleet @@ -428,8 +428,8 @@ new-e2e-installer-windows: needs: - !reference [.needs_new_e2e_template] - deploy_windows_testing-a7 - - qa_installer_oci - - qa_agent_oci + - deploy_installer_oci + - deploy_agent_oci before_script: # CURRENT_AGENT_VERSION is used to verify the installed agent version # Must run before new_e2e_template changes the aws profile @@ -471,8 +471,8 @@ new-e2e-installer-ansible: - deploy_rpm_testing-a7_x64 - deploy_suse_rpm_testing_arm64-a7 - deploy_suse_rpm_testing_x64-a7 - - qa_installer_oci - - qa_agent_oci + - deploy_installer_oci + - deploy_agent_oci variables: TARGETS: ./tests/installer/unix TEAM: fleet @@ -663,8 +663,8 @@ generate-flakes-finder-pipeline: - deploy_suse_rpm_testing_arm64-a7 - deploy_suse_rpm_testing_x64-a7 - deploy_windows_testing-a7 - - qa_installer_oci - - qa_agent_oci + - deploy_installer_oci + - deploy_agent_oci - qa_cws_instrumentation - qa_dca - qa_dogstatsd diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 8c766a337eb1c..d424a9ed015c7 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -596,12 +596,6 @@ core,github.com/aws/aws-sdk-go-v2/service/ebs/types,Apache-2.0,"Copyright 2014-2 core,github.com/aws/aws-sdk-go-v2/service/ec2,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/aws/aws-sdk-go-v2/service/ec2/internal/endpoints,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/aws/aws-sdk-go-v2/service/ec2/types,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecr,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecr/internal/endpoints,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecr/types,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecrpublic,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecrpublic/internal/endpoints,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/aws/aws-sdk-go-v2/service/ecrpublic/types,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/aws/aws-sdk-go-v2/service/internal/presigned-url,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/aws/aws-sdk-go-v2/service/kms,Apache-2.0,"Copyright 2014-2015 Stripe, Inc. | Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved." @@ -692,11 +686,6 @@ core,github.com/aws/smithy-go/tracing,Apache-2.0,"Copyright Amazon.com, Inc. or core,github.com/aws/smithy-go/transport/http,Apache-2.0,"Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved" core,github.com/aws/smithy-go/transport/http/internal/io,Apache-2.0,"Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved" core,github.com/aws/smithy-go/waiter,Apache-2.0,"Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved" -core,github.com/awslabs/amazon-ecr-credential-helper/ecr-login,Apache-2.0,"Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/awslabs/amazon-ecr-credential-helper/ecr-login/api,Apache-2.0,"Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cache,Apache-2.0,"Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/awslabs/amazon-ecr-credential-helper/ecr-login/config,Apache-2.0,"Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved." -core,github.com/awslabs/amazon-ecr-credential-helper/ecr-login/version,Apache-2.0,"Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved." core,github.com/bahlo/generic-list-go,BSD-3-Clause,Copyright (c) 2009 The Go Authors. All rights reserved core,github.com/beevik/ntp,BSD-2-Clause,Al Cutter (AlCutter) | Andrey Smirnov (smira) | Anton Tolchanov (knyar) | Ask Bjørn Hansen (abh) | Brett Vickers (beevik) | Christopher Batey (chbatey) | Copyright © 2015-2023 Brett Vickers. All rights reserved | Leonid Evdokimov (darkk) | Meng Zhuo (mengzhuo) | Mikhail Salosin (AlphaB) | Silves-Xiang (silves-xiang) core,github.com/benbjohnson/clock,MIT,Copyright (c) 2014 Ben Johnson diff --git a/go.mod b/go.mod index 40f6882509169..0a6de46e14ef7 100644 --- a/go.mod +++ b/go.mod @@ -741,7 +741,6 @@ require ( github.com/aws/aws-sdk-go-v2/service/kms v1.37.6 github.com/aws/aws-sdk-go-v2/service/rds v1.90.0 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6 - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240409155312-26d1ea377073 github.com/cloudfoundry-community/go-cfclient/v2 v2.0.1-0.20230503155151-3d15366c5820 github.com/containerd/cgroups/v3 v3.0.4 github.com/containerd/typeurl/v2 v2.2.3 @@ -822,8 +821,6 @@ require ( github.com/antlr4-go/antlr/v4 v4.13.0 // indirect github.com/apache/thrift v0.21.0 // indirect github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.0 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c // indirect diff --git a/go.sum b/go.sum index b241ffba51c8e..5fd04067f4905 100644 --- a/go.sum +++ b/go.sum @@ -347,10 +347,6 @@ github.com/aws/aws-sdk-go-v2/service/ebs v1.27.0 h1:4zuGQITyy9O+GlSGcs+aUz3+Smlv github.com/aws/aws-sdk-go-v2/service/ebs v1.27.0/go.mod h1:T0t6q7wBD2P11xwVcc6GvwmuDT3i6ZJgZ+13ziQUUnA= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 h1:k97fGog9Tl0woxTiSIHN14Qs5ehqK6GXejUwkhJYyL0= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0/go.mod h1:mzj8EEjIHSN2oZRXiw1Dd+uB4HZTl7hC8nBzX9IZMWw= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.0 h1:k4ykVLeoO2JohTC7BIfIqWYvmf8HcirbWHf8Zn0SPpI= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.27.0/go.mod h1:vJMqaxbvogPsWZYEInEwK82EBwCfc7cnE/5BEqnvTYI= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.15/go.mod h1:26SQUPcTNgV1Tapwdt4a1rOsYRsnBsJHLMPoxK2b0d8= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= @@ -378,8 +374,6 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyN github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240409155312-26d1ea377073 h1:9XtHL16FtbSDAedz9AnboTDqfKacYqc5BmwtUxzwwD8= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20240409155312-26d1ea377073/go.mod h1:2nlYPkG0rFrODp6R875pk/kOnB8Ivj3+onhzk2mO57g= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3 h1:6df1vn4bBlDDo4tARvBm7l6KA9iVMnE3NWizDeWSrps= diff --git a/pkg/fleet/installer/oci/download.go b/pkg/fleet/installer/oci/download.go index 9260d19bc0e97..7fcce0234512c 100644 --- a/pkg/fleet/installer/oci/download.go +++ b/pkg/fleet/installer/oci/download.go @@ -20,7 +20,6 @@ import ( "syscall" "time" - "github.com/awslabs/amazon-ecr-credential-helper/ecr-login" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" oci "github.com/google/go-containerregistry/pkg/v1" @@ -44,8 +43,6 @@ const ( RegistryAuthDefault string = "docker" // RegistryAuthGCR is the Google Container Registry authentication method. RegistryAuthGCR string = "gcr" - // RegistryAuthECR is the Amazon Elastic Container Registry authentication method. - RegistryAuthECR string = "ecr" // RegistryAuthPassword is the password registry authentication method. RegistryAuthPassword string = "password" ) @@ -154,8 +151,6 @@ func getKeychain(auth string, username string, password string) authn.Keychain { switch auth { case RegistryAuthGCR: return google.Keychain - case RegistryAuthECR: - return authn.NewKeychainFromHelper(ecr.NewECRHelper()) case RegistryAuthPassword: return usernamePasswordKeychain{ username: username, diff --git a/test/new-e2e/tests/installer/unix/package_definitions.go b/test/new-e2e/tests/installer/unix/package_definitions.go index 2f7c2d15057dd..78c5ac64fd75b 100644 --- a/test/new-e2e/tests/installer/unix/package_definitions.go +++ b/test/new-e2e/tests/installer/unix/package_definitions.go @@ -67,8 +67,8 @@ func WithAlias(alias string) PackageOption { // PackagesConfig is the list of known packages configuration for testing var PackagesConfig = []TestPackageConfig{ - {Name: "datadog-installer", Version: fmt.Sprintf("pipeline-%v", os.Getenv("E2E_PIPELINE_ID")), Registry: "669783387624.dkr.ecr.us-east-1.amazonaws.com", Auth: "ecr"}, - {Name: "datadog-agent", Alias: "agent-package", Version: fmt.Sprintf("pipeline-%v", os.Getenv("E2E_PIPELINE_ID")), Registry: "669783387624.dkr.ecr.us-east-1.amazonaws.com", Auth: "ecr"}, + {Name: "datadog-installer", Version: fmt.Sprintf("pipeline-%v", os.Getenv("E2E_PIPELINE_ID")), Registry: "installtesting.datad0g.com"}, + {Name: "datadog-agent", Alias: "agent-package", Version: fmt.Sprintf("pipeline-%v", os.Getenv("E2E_PIPELINE_ID")), Registry: "installtesting.datad0g.com"}, {Name: "datadog-apm-inject", Version: "latest"}, {Name: "datadog-apm-library-java", Version: "latest"}, {Name: "datadog-apm-library-ruby", Version: "latest"}, From 7fc4bc502f434a2d8633150ef41ff9b5f0f8642f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillermo=20Juli=C3=A1n?= Date: Mon, 16 Dec 2024 15:23:14 +0100 Subject: [PATCH 26/78] [EBPF] gpu: compute utilization based on active GPU (#32023) --- pkg/gpu/aggregator.go | 14 +++++------- pkg/gpu/context.go | 14 ++++++++++++ pkg/gpu/stats.go | 28 ++++++++++++++++++++---- pkg/gpu/stats_test.go | 46 +++++++++++++++++++++++++++++++++++++++ pkg/gpu/testutil/mocks.go | 11 ++++++---- 5 files changed, 97 insertions(+), 16 deletions(-) diff --git a/pkg/gpu/aggregator.go b/pkg/gpu/aggregator.go index 6e907c24428a1..30a20e9146efd 100644 --- a/pkg/gpu/aggregator.go +++ b/pkg/gpu/aggregator.go @@ -38,13 +38,13 @@ type aggregator struct { // processTerminated is true if the process has ended and this aggregator should be deleted processTerminated bool - // sysCtx is the system context with global GPU-system data - sysCtx *systemContext + // deviceMaxThreads is the maximum number of threads the GPU can run in parallel, for utilization calculations + deviceMaxThreads uint64 } -func newAggregator(sysCtx *systemContext) *aggregator { +func newAggregator(deviceMaxThreads uint64) *aggregator { return &aggregator{ - sysCtx: sysCtx, + deviceMaxThreads: deviceMaxThreads, } } @@ -60,8 +60,7 @@ func (agg *aggregator) processKernelSpan(span *kernelSpan) { } durationSec := float64(tsEnd-tsStart) / float64(time.Second.Nanoseconds()) - maxThreads := uint64(agg.sysCtx.maxGpuThreadsPerDevice[0]) // TODO: MultiGPU support not enabled yet - agg.totalThreadSecondsUsed += durationSec * float64(min(span.avgThreadCount, maxThreads)) // we can't use more threads than the GPU has + agg.totalThreadSecondsUsed += durationSec * float64(min(span.avgThreadCount, agg.deviceMaxThreads)) // we can't use more threads than the GPU has } // processPastData takes spans/allocations that have already been closed @@ -86,8 +85,7 @@ func (agg *aggregator) processCurrentData(data *streamData) { func (agg *aggregator) getGPUUtilization() float64 { intervalSecs := float64(agg.measuredIntervalNs) / float64(time.Second.Nanoseconds()) if intervalSecs > 0 { - // TODO: MultiGPU support not enabled yet - availableThreadSeconds := float64(agg.sysCtx.maxGpuThreadsPerDevice[0]) * intervalSecs + availableThreadSeconds := float64(agg.deviceMaxThreads) * intervalSecs return agg.totalThreadSecondsUsed / availableThreadSeconds } diff --git a/pkg/gpu/context.go b/pkg/gpu/context.go index 6c38ae4ad978c..77bac5185339f 100644 --- a/pkg/gpu/context.go +++ b/pkg/gpu/context.go @@ -248,3 +248,17 @@ func (ctx *systemContext) setDeviceSelection(pid int, tid int, deviceIndex int32 ctx.selectedDeviceByPIDAndTID[pid][tid] = deviceIndex } + +// getDeviceByUUID returns the device with the given UUID. +func (ctx *systemContext) getDeviceByUUID(uuid string) (nvml.Device, error) { + for _, dev := range ctx.gpuDevices { + devUUID, ret := dev.GetUUID() + if ret != nvml.SUCCESS { + return nil, fmt.Errorf("error getting device UUID: %s", nvml.ErrorString(ret)) + } + if devUUID == uuid { + return dev, nil + } + } + return nil, fmt.Errorf("device with UUID %s not found", uuid) +} diff --git a/pkg/gpu/stats.go b/pkg/gpu/stats.go index 753fadcf084d9..ba50db6c5226a 100644 --- a/pkg/gpu/stats.go +++ b/pkg/gpu/stats.go @@ -8,8 +8,13 @@ package gpu import ( + "fmt" + + "github.com/NVIDIA/go-nvml/pkg/nvml" + "github.com/DataDog/datadog-agent/pkg/collector/corechecks/gpu/model" ddebpf "github.com/DataDog/datadog-agent/pkg/ebpf" + "github.com/DataDog/datadog-agent/pkg/util/log" ) // statsGenerator connects to the active stream handlers and generates stats for the GPU monitoring, by distributing @@ -40,7 +45,12 @@ func (g *statsGenerator) getStats(nowKtime int64) *model.GPUStats { g.currGenerationKTime = nowKtime for key, handler := range g.streamHandlers { - aggr := g.getOrCreateAggregator(key) + aggr, err := g.getOrCreateAggregator(key) + if err != nil { + log.Errorf("Error getting or creating aggregator for key %v: %s", key, err) + continue + } + currData := handler.getCurrentData(uint64(nowKtime)) pastData := handler.getPastData(true) @@ -76,7 +86,7 @@ func (g *statsGenerator) getStats(nowKtime int64) *model.GPUStats { return stats } -func (g *statsGenerator) getOrCreateAggregator(sKey streamKey) *aggregator { +func (g *statsGenerator) getOrCreateAggregator(sKey streamKey) (*aggregator, error) { aggKey := model.StatsKey{ PID: sKey.pid, DeviceUUID: sKey.gpuUUID, @@ -84,13 +94,23 @@ func (g *statsGenerator) getOrCreateAggregator(sKey streamKey) *aggregator { } if _, ok := g.aggregators[aggKey]; !ok { - g.aggregators[aggKey] = newAggregator(g.sysCtx) + gpuDevice, err := g.sysCtx.getDeviceByUUID(sKey.gpuUUID) + if err != nil { + return nil, fmt.Errorf("Error getting device by UUID %s: %s", sKey.gpuUUID, err) + } + + maxThreads, ret := gpuDevice.GetNumGpuCores() + if ret != nvml.SUCCESS { + return nil, fmt.Errorf("Error getting number of GPU cores: %s", nvml.ErrorString(ret)) + } + + g.aggregators[aggKey] = newAggregator(uint64(maxThreads)) } // Update the last check time and the measured interval, as these change between check runs g.aggregators[aggKey].lastCheckKtime = uint64(g.lastGenerationKTime) g.aggregators[aggKey].measuredIntervalNs = g.currGenerationKTime - g.lastGenerationKTime - return g.aggregators[aggKey] + return g.aggregators[aggKey], nil } // getNormalizationFactor returns the factor to use for utilization diff --git a/pkg/gpu/stats_test.go b/pkg/gpu/stats_test.go index 4ebbec874ecdc..c0445e6c4e869 100644 --- a/pkg/gpu/stats_test.go +++ b/pkg/gpu/stats_test.go @@ -233,3 +233,49 @@ func TestGetStatsWithPastAndCurrentData(t *testing.T) { expectedUtil := expectedUtilKern1 + expectedUtilKern2 require.InDelta(t, expectedUtil, metrics.UtilizationPercentage, 0.001) } + +func TestGetStatsMultiGPU(t *testing.T) { + statsGen, streamHandlers, ktime := getStatsGeneratorForTest(t) + + startKtime := ktime + int64(1*time.Second) + endKtime := startKtime + int64(1*time.Second) + + pid := uint32(1) + numThreads := uint64(5) + + // Add kernels for all devices + for i, uuid := range testutil.GPUUUIDs { + streamID := uint64(i) + streamKey := streamKey{pid: pid, stream: streamID, gpuUUID: uuid} + streamHandlers[streamKey] = &StreamHandler{ + processEnded: false, + kernelSpans: []*kernelSpan{ + { + startKtime: uint64(startKtime), + endKtime: uint64(endKtime), + avgThreadCount: numThreads, + numKernels: 10, + }, + }, + } + } + + checkDuration := 10 * time.Second + checkKtime := ktime + int64(checkDuration) + stats := statsGen.getStats(checkKtime) + require.NotNil(t, stats) + + // Check the metrics for each device + for i, uuid := range testutil.GPUUUIDs { + metricsKey := model.StatsKey{PID: pid, DeviceUUID: uuid} + metrics := getMetricsEntry(metricsKey, stats) + require.NotNil(t, metrics, "cannot find metrics for key %+v", metricsKey) + + gpuCores := float64(testutil.GPUCores[i]) + threadSecondsUsed := float64(numThreads) * float64(endKtime-startKtime) / 1e9 + threadSecondsAvailable := gpuCores * checkDuration.Seconds() + expectedUtil := threadSecondsUsed / threadSecondsAvailable + + require.InDelta(t, expectedUtil, metrics.UtilizationPercentage, 0.001, "invalid utilization for device %d (uuid=%s)", i, uuid) + } +} diff --git a/pkg/gpu/testutil/mocks.go b/pkg/gpu/testutil/mocks.go index b703a39001ef4..a7dadd39215ad 100644 --- a/pkg/gpu/testutil/mocks.go +++ b/pkg/gpu/testutil/mocks.go @@ -23,20 +23,23 @@ var GPUUUIDs = []string{ "GPU-00000000-1234-1234-1234-123456789014", } +// GPUCores is a list of number of cores for the devices returned by the mock, should be the same length as GPUUUIDs +var GPUCores = []int{DefaultGpuCores, 20, 30} + // DefaultGpuUUID is the UUID for the default device returned by the mock var DefaultGpuUUID = GPUUUIDs[0] // GetDeviceMock returns a mock of the nvml.Device with the given UUID. -func GetDeviceMock(uuid string) *nvmlmock.Device { +func GetDeviceMock(deviceIdx int) *nvmlmock.Device { return &nvmlmock.Device{ GetNumGpuCoresFunc: func() (int, nvml.Return) { - return DefaultGpuCores, nvml.SUCCESS + return GPUCores[deviceIdx], nvml.SUCCESS }, GetCudaComputeCapabilityFunc: func() (int, int, nvml.Return) { return 7, 5, nvml.SUCCESS }, GetUUIDFunc: func() (string, nvml.Return) { - return uuid, nvml.SUCCESS + return GPUUUIDs[deviceIdx], nvml.SUCCESS }, } } @@ -49,7 +52,7 @@ func GetBasicNvmlMock() *nvmlmock.Interface { return len(GPUUUIDs), nvml.SUCCESS }, DeviceGetHandleByIndexFunc: func(index int) (nvml.Device, nvml.Return) { - return GetDeviceMock(GPUUUIDs[index]), nvml.SUCCESS + return GetDeviceMock(index), nvml.SUCCESS }, DeviceGetCudaComputeCapabilityFunc: func(nvml.Device) (int, int, nvml.Return) { return 7, 5, nvml.SUCCESS From ed416e047042c94cd1fbc6cc5def12c0d19c8f90 Mon Sep 17 00:00:00 2001 From: Mackenzie <63265430+mackjmr@users.noreply.github.com> Date: Mon, 16 Dec 2024 16:16:26 +0100 Subject: [PATCH 27/78] Update converter and ddflareextension README (#32105) --- comp/otelcol/converter/README.md | 10 +--------- comp/otelcol/ddflareextension/README.md | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/comp/otelcol/converter/README.md b/comp/otelcol/converter/README.md index 31c0c53f7176b..ad8c831ada147 100644 --- a/comp/otelcol/converter/README.md +++ b/comp/otelcol/converter/README.md @@ -1,8 +1,6 @@ # Converter Component -The converter: -- Enhances the user provided configuration -- Provides an API which returns the provided and enhanced configurations +The converter enhances the user provided configuration. ## Autoconfigure logic @@ -32,12 +30,6 @@ If `api_key` is unset, set to an empty string or set to a secret, the converter The converter will automatically set `datadogconnector` config `trace.span_name_as_resource_name` to true in any datadog connectors in your configuration. -## Provided and enhanced config - -`GetProvidedConf` and `GetEnhancedConf` return the string representation of the user provided and autoconfigured conf respectively. Currently, these APIs have two limitations: -- They do not redact sensitive data -- They do not provide the effective config (including defaults...etc) - ## Opting out of converter It is possible to opt out of the converter by setting env var `DD_OTELCOLLECTOR_CONVERTER_ENABLED` or agent config `otelcollector.converter.enabled` to `false` (`true` by default). Please note that by doing so, you are removing functionality including flare collection from otel-agent, health metrics from collector, or infra level tagging on your telemetry data. If you want to opt out of some components, you can disable all and add the components that you require manually: diff --git a/comp/otelcol/ddflareextension/README.md b/comp/otelcol/ddflareextension/README.md index 13f5902877c4e..c54027041c70f 100644 --- a/comp/otelcol/ddflareextension/README.md +++ b/comp/otelcol/ddflareextension/README.md @@ -32,7 +32,7 @@ The port is the location in which the otel-agent will expose the data required t The flare will collect both the provided collector config and the enhanced config (enhanced via [converter](../converter/README.md)). -The provided collector configs can be found in `otel/otel-flare/customer.cfg` and the enhanced config can be found in `otel/otel-flare/customer.cfg`. +The provided collector configs can be found in `otel/otel-flare/customer.cfg` and the enhanced config can be found in `otel/otel-flare/runtime.cfg`. ### Environment variables From 9a5365e40cbaca79d47b7e9b08eb64b491bf18db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Mon, 16 Dec 2024 16:30:01 +0100 Subject: [PATCH 28/78] update last stable version (#32223) --- release.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.json b/release.json index 24cb211dbbdc3..841e115eac8e4 100644 --- a/release.json +++ b/release.json @@ -3,7 +3,7 @@ "current_milestone": "7.62.0", "last_stable": { "6": "6.53.0", - "7": "7.59.1" + "7": "7.60.0" }, "nightly": { "INTEGRATIONS_CORE_VERSION": "master", From 2daad7fb23614df54a78247f0e1e2c1b3fd458ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Mon, 16 Dec 2024 16:30:25 +0100 Subject: [PATCH 29/78] [incident-33304] Mark `dogstatsd` e2e tests as flaky (#32220) --- flakes.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flakes.yaml b/flakes.yaml index ab2af6b55f3ea..bde9554d2f5f7 100644 --- a/flakes.yaml +++ b/flakes.yaml @@ -11,3 +11,8 @@ test/new-e2e/tests/containers: - TestECSSuite/TestCPU/metric___container.cpu.usage{^ecs_container_name:stress-ng$} - TestEKSSuite/TestCPU/metric___container.cpu.usage{^kube_deployment:stress-ng$,^kube_namespace:workload-cpustress$} - TestKindSuite/TestCPU/metric___container.cpu.usage{^kube_deployment:stress-ng$,^kube_namespace:workload-cpustress$} + + - TestEKSSuite/TestDogstatsdInAgent/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} + - TestEKSSuite/TestDogstatsdStandalone/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} + - TestKindSuite/TestDogstatsdInAgent/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} + - TestKindSuite/TestDogstatsdStandalone/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} From 53eec23ed7dd5ab324190d9294c98c36959c2a3d Mon Sep 17 00:00:00 2001 From: Alexandre Yang Date: Mon, 16 Dec 2024 16:30:33 +0100 Subject: [PATCH 30/78] Revert "Revert "[snmp] Add agent_group tag for snmp integration"" + Fix tests (#32198) --- comp/haagent/helpers/helpers.go | 26 ++++++++++ comp/haagent/helpers/helpers_test.go | 33 ++++++++++++ comp/haagent/impl/config.go | 5 +- comp/metadata/host/hostimpl/hosttags/tags.go | 5 +- .../snmp/internal/devicecheck/devicecheck.go | 17 +++++-- .../internal/devicecheck/devicecheck_test.go | 51 ++++++++++++++++--- .../snmp/internal/discovery/discovery.go | 7 ++- .../snmp/internal/discovery/discovery_test.go | 33 +++++++----- .../snmp/internal/discovery/testing.go | 20 -------- pkg/collector/corechecks/snmp/snmp.go | 15 ++++-- pkg/collector/corechecks/snmp/snmp_test.go | 13 ++--- pkg/commonchecks/corechecks.go | 2 +- 12 files changed, 164 insertions(+), 63 deletions(-) create mode 100644 comp/haagent/helpers/helpers.go create mode 100644 comp/haagent/helpers/helpers_test.go delete mode 100644 pkg/collector/corechecks/snmp/internal/discovery/testing.go diff --git a/comp/haagent/helpers/helpers.go b/comp/haagent/helpers/helpers.go new file mode 100644 index 0000000000000..4b5e755e04936 --- /dev/null +++ b/comp/haagent/helpers/helpers.go @@ -0,0 +1,26 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2024-present Datadog, Inc. + +// Package haagenthelpers provides helpers for haagent component +package haagenthelpers + +import ( + "github.com/DataDog/datadog-agent/pkg/config/model" +) + +// IsEnabled returns true if HA Agent is enabled +func IsEnabled(agentConfig model.Reader) bool { + return agentConfig.GetBool("ha_agent.enabled") +} + +// GetGroup returns HA Agent group +func GetGroup(agentConfig model.Reader) string { + return agentConfig.GetString("ha_agent.group") +} + +// GetHaAgentTags returns HA Agent related tags +func GetHaAgentTags(agentConfig model.Reader) []string { + return []string{"agent_group:" + GetGroup(agentConfig)} +} diff --git a/comp/haagent/helpers/helpers_test.go b/comp/haagent/helpers/helpers_test.go new file mode 100644 index 0000000000000..987ab6a5cccf4 --- /dev/null +++ b/comp/haagent/helpers/helpers_test.go @@ -0,0 +1,33 @@ +// 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 haagenthelpers + +import ( + "testing" + + "github.com/DataDog/datadog-agent/comp/core/config" + "github.com/stretchr/testify/assert" +) + +func TestIsEnabled(t *testing.T) { + cfg := config.NewMock(t) + assert.False(t, IsEnabled(cfg)) + + cfg.SetWithoutSource("ha_agent.enabled", true) + assert.True(t, IsEnabled(cfg)) +} + +func TestGetGroup(t *testing.T) { + cfg := config.NewMock(t) + cfg.SetWithoutSource("ha_agent.group", "my-group") + assert.Equal(t, "my-group", GetGroup(cfg)) +} + +func TestGetHaAgentTags(t *testing.T) { + cfg := config.NewMock(t) + cfg.SetWithoutSource("ha_agent.group", "my-group") + assert.Equal(t, []string{"agent_group:my-group"}, GetHaAgentTags(cfg)) +} diff --git a/comp/haagent/impl/config.go b/comp/haagent/impl/config.go index 2a6c4e20a8d12..ea9f54d9f16ea 100644 --- a/comp/haagent/impl/config.go +++ b/comp/haagent/impl/config.go @@ -7,6 +7,7 @@ package haagentimpl import ( "github.com/DataDog/datadog-agent/comp/core/config" + helpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" ) // validHaIntegrations represent the list of integrations that will be considered as @@ -30,7 +31,7 @@ type haAgentConfigs struct { func newHaAgentConfigs(agentConfig config.Component) *haAgentConfigs { return &haAgentConfigs{ - enabled: agentConfig.GetBool("ha_agent.enabled"), - group: agentConfig.GetString("ha_agent.group"), + enabled: helpers.IsEnabled(agentConfig), + group: helpers.GetGroup(agentConfig), } } diff --git a/comp/metadata/host/hostimpl/hosttags/tags.go b/comp/metadata/host/hostimpl/hosttags/tags.go index 01cfff7c0810f..92d610d49e079 100644 --- a/comp/metadata/host/hostimpl/hosttags/tags.go +++ b/comp/metadata/host/hostimpl/hosttags/tags.go @@ -12,6 +12,7 @@ import ( "strings" "time" + haagenthelpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" "github.com/DataDog/datadog-agent/pkg/config/env" "github.com/DataDog/datadog-agent/pkg/config/model" configUtils "github.com/DataDog/datadog-agent/pkg/config/utils" @@ -133,8 +134,8 @@ func Get(ctx context.Context, cached bool, conf model.Reader) *Tags { hostTags = appendToHostTags(hostTags, clusterNameTags) } - if conf.GetBool("ha_agent.enabled") { - hostTags = appendToHostTags(hostTags, []string{"agent_group:" + conf.GetString("ha_agent.group")}) + if haagenthelpers.IsEnabled(conf) { + hostTags = appendToHostTags(hostTags, haagenthelpers.GetHaAgentTags(conf)) } gceTags := []string{} diff --git a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go index 969fd45d2da52..d23d5642a2470 100644 --- a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go +++ b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck.go @@ -17,8 +17,9 @@ import ( "go.uber.org/atomic" + "github.com/DataDog/datadog-agent/comp/core/config" + haagenthelpers "github.com/DataDog/datadog-agent/comp/haagent/helpers" "github.com/DataDog/datadog-agent/pkg/collector/externalhost" - pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" configUtils "github.com/DataDog/datadog-agent/pkg/config/utils" "github.com/DataDog/datadog-agent/pkg/metrics/servicecheck" "github.com/DataDog/datadog-agent/pkg/util/hostname/validate" @@ -66,12 +67,13 @@ type DeviceCheck struct { diagnoses *diagnoses.Diagnoses interfaceBandwidthState report.InterfaceBandwidthState cacheKey string + agentConfig config.Component } const cacheKeyPrefix = "snmp-tags" // NewDeviceCheck returns a new DeviceCheck -func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFactory session.Factory) (*DeviceCheck, error) { +func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFactory session.Factory, agentConfig config.Component) (*DeviceCheck, error) { newConfig := config.CopyWithNewIP(ipAddress) var devicePinger pinger.Pinger @@ -94,6 +96,7 @@ func NewDeviceCheck(config *checkconfig.CheckConfig, ipAddress string, sessionFa diagnoses: diagnoses.NewDeviceDiagnoses(newConfig.DeviceID), interfaceBandwidthState: report.MakeInterfaceBandwidthState(), cacheKey: cacheKey, + agentConfig: agentConfig, } d.readTagsFromCache() @@ -244,11 +247,19 @@ func (d *DeviceCheck) setDeviceHostExternalTags() { if deviceHostname == "" || err != nil { return } - agentTags := configUtils.GetConfiguredTags(pkgconfigsetup.Datadog(), false) + agentTags := d.buildExternalTags() log.Debugf("Set external tags for device host, host=`%s`, agentTags=`%v`", deviceHostname, agentTags) externalhost.SetExternalTags(deviceHostname, common.SnmpExternalTagsSourceType, agentTags) } +func (d *DeviceCheck) buildExternalTags() []string { + agentTags := configUtils.GetConfiguredTags(d.agentConfig, false) + if haagenthelpers.IsEnabled(d.agentConfig) { + agentTags = append(agentTags, haagenthelpers.GetHaAgentTags(d.agentConfig)...) + } + return agentTags +} + func (d *DeviceCheck) getValuesAndTags() (bool, []string, *valuestore.ResultValueStore, error) { var deviceReachable bool var checkErrors []string diff --git a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go index c56235171d3a2..1e7ad7fc7a001 100644 --- a/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go +++ b/pkg/collector/corechecks/snmp/internal/devicecheck/devicecheck_test.go @@ -17,6 +17,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/metrics/servicecheck" "github.com/DataDog/datadog-agent/pkg/version" @@ -57,7 +58,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -197,7 +198,7 @@ global_metrics: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -246,7 +247,7 @@ community_string: public config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession, agentconfig.NewMock(t)) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -277,7 +278,7 @@ community_string: public config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", session.NewMockSession, agentconfig.NewMock(t)) assert.Nil(t, err) hostname, err := deviceCk.GetDeviceHostname() @@ -343,7 +344,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) snmpTags := []string{"snmp_device:1.2.3.4", "device_ip:1.2.3.4", "device_id:default:1.2.3.4", "snmp_profile:f5-big-ip", "device_vendor:f5", "snmp_host:foo_sys_name", @@ -648,7 +649,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) sender := mocksender.NewMockSender("123") // required to initiate aggregator @@ -695,7 +696,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) // override pinger with mock pinger @@ -846,7 +847,7 @@ profiles: config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) assert.Nil(t, err) - deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory) + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, agentconfig.NewMock(t)) assert.Nil(t, err) // override pinger with mock pinger @@ -967,3 +968,37 @@ profiles: sender.AssertNotCalled(t, "Gauge", pingAvgRttMetric, mock.Anything, mock.Anything, mock.Anything) sender.AssertNotCalled(t, "Gauge", pingPacketLoss, mock.Anything, mock.Anything, mock.Anything) } + +func TestDeviceCheck_buildExternalTags(t *testing.T) { + // GIVEN + profile.SetConfdPathAndCleanProfiles() + sess := session.CreateFakeSession() + sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { + return sess, nil + } + + // language=yaml + rawInstanceConfig := []byte(` +ip_address: 1.2.3.4 +community_string: public +collect_topology: false +`) + // language=yaml + rawInitConfig := []byte(``) + + config, err := checkconfig.NewCheckConfig(rawInstanceConfig, rawInitConfig) + assert.Nil(t, err) + + cfg := agentconfig.NewMock(t) + cfg.SetWithoutSource("ha_agent.enabled", true) + cfg.SetWithoutSource("ha_agent.group", "my-group") + + deviceCk, err := NewDeviceCheck(config, "1.2.3.4", sessionFactory, cfg) + assert.Nil(t, err) + + // WHEN + externalTags := deviceCk.buildExternalTags() + + // THEN + assert.Equal(t, []string{"agent_group:my-group"}, externalTags) +} diff --git a/pkg/collector/corechecks/snmp/internal/discovery/discovery.go b/pkg/collector/corechecks/snmp/internal/discovery/discovery.go index b7fb91915209a..860f3cb781991 100644 --- a/pkg/collector/corechecks/snmp/internal/discovery/discovery.go +++ b/pkg/collector/corechecks/snmp/internal/discovery/discovery.go @@ -14,6 +14,7 @@ import ( "sync" "time" + "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/persistentcache" "github.com/DataDog/datadog-agent/pkg/util/log" "go.uber.org/atomic" @@ -43,6 +44,7 @@ type Discovery struct { discoveredDevices map[checkconfig.DeviceDigest]Device sessionFactory session.Factory + agentConfig config.Component } // Device implements and store results from the Service interface for the SNMP listener @@ -237,7 +239,7 @@ func (d *Discovery) getDevicesFound() []string { } func (d *Discovery) createDevice(deviceDigest checkconfig.DeviceDigest, subnet *snmpSubnet, deviceIP string, writeCache bool) { - deviceCk, err := devicecheck.NewDeviceCheck(subnet.config, deviceIP, d.sessionFactory) + deviceCk, err := devicecheck.NewDeviceCheck(subnet.config, deviceIP, d.sessionFactory, d.agentConfig) if err != nil { // should not happen since the deviceCheck is expected to be valid at this point // and are only changing the device ip @@ -335,11 +337,12 @@ func (d *Discovery) writeCache(subnet *snmpSubnet) { } // NewDiscovery return a new Discovery instance -func NewDiscovery(config *checkconfig.CheckConfig, sessionFactory session.Factory) *Discovery { +func NewDiscovery(config *checkconfig.CheckConfig, sessionFactory session.Factory, agentConfig config.Component) *Discovery { return &Discovery{ discoveredDevices: make(map[checkconfig.DeviceDigest]Device), stop: make(chan struct{}), config: config, sessionFactory: sessionFactory, + agentConfig: agentConfig, } } diff --git a/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go b/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go index 9d59def5890d0..4ca8ccc302bf2 100644 --- a/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go +++ b/pkg/collector/corechecks/snmp/internal/discovery/discovery_test.go @@ -8,16 +8,15 @@ package discovery import ( "fmt" "net" - "path/filepath" "testing" "time" "github.com/gosnmp/gosnmp" "github.com/stretchr/testify/assert" + agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp/internal/checkconfig" "github.com/DataDog/datadog-agent/pkg/collector/corechecks/snmp/internal/session" - pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" ) func waitForDiscoveredDevices(discovery *Discovery, expectedDeviceCount int, timeout time.Duration) error { @@ -33,8 +32,8 @@ func waitForDiscoveredDevices(discovery *Discovery, expectedDeviceCount int, tim } func TestDiscovery(t *testing.T) { - path, _ := filepath.Abs(filepath.Join(".", "test", "run_path", "TestDiscovery")) - pkgconfigsetup.Datadog().SetWithoutSource("run_path", path) + config := agentconfig.NewMock(t) + config.SetWithoutSource("run_path", t.TempDir()) sess := session.CreateMockSession() sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { @@ -59,7 +58,7 @@ func TestDiscovery(t *testing.T) { DiscoveryWorkers: 1, IgnoredIPAddresses: map[string]bool{"192.168.0.5": true}, } - discovery := NewDiscovery(checkConfig, sessionFactory) + discovery := NewDiscovery(checkConfig, sessionFactory, config) discovery.Start() assert.NoError(t, waitForDiscoveredDevices(discovery, 7, 2*time.Second)) discovery.Stop() @@ -84,8 +83,8 @@ func TestDiscovery(t *testing.T) { } func TestDiscoveryCache(t *testing.T) { - path, _ := filepath.Abs(filepath.Join(".", "test", "run_path", "TestDiscoveryCache")) - pkgconfigsetup.Datadog().SetWithoutSource("run_path", path) + config := agentconfig.NewMock(t) + config.SetWithoutSource("run_path", t.TempDir()) sess := session.CreateMockSession() sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { @@ -109,7 +108,7 @@ func TestDiscoveryCache(t *testing.T) { DiscoveryInterval: 3600, DiscoveryWorkers: 1, } - discovery := NewDiscovery(checkConfig, sessionFactory) + discovery := NewDiscovery(checkConfig, sessionFactory, config) discovery.Start() assert.NoError(t, waitForDiscoveredDevices(discovery, 4, 2*time.Second)) discovery.Stop() @@ -141,7 +140,7 @@ func TestDiscoveryCache(t *testing.T) { DiscoveryInterval: 3600, DiscoveryWorkers: 0, // no workers, the devices will be loaded from cache } - discovery2 := NewDiscovery(checkConfig, sessionFactory) + discovery2 := NewDiscovery(checkConfig, sessionFactory, config) discovery2.Start() assert.NoError(t, waitForDiscoveredDevices(discovery2, 4, 2*time.Second)) discovery2.Stop() @@ -158,6 +157,9 @@ func TestDiscoveryCache(t *testing.T) { func TestDiscoveryTicker(t *testing.T) { t.Skip() // TODO: FIX ME, currently this test is leading to data race when ran with other tests + config := agentconfig.NewMock(t) + config.SetWithoutSource("run_path", t.TempDir()) + sess := session.CreateMockSession() sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { return sess, nil @@ -180,7 +182,7 @@ func TestDiscoveryTicker(t *testing.T) { DiscoveryInterval: 1, DiscoveryWorkers: 1, } - discovery := NewDiscovery(checkConfig, sessionFactory) + discovery := NewDiscovery(checkConfig, sessionFactory, config) discovery.Start() time.Sleep(1500 * time.Millisecond) discovery.Stop() @@ -191,7 +193,8 @@ func TestDiscoveryTicker(t *testing.T) { } func TestDiscovery_checkDevice(t *testing.T) { - SetTestRunPath() + config := agentconfig.NewMock(t) + config.SetWithoutSource("run_path", t.TempDir()) checkConfig := &checkconfig.CheckConfig{ Network: "192.168.0.0/32", CommunityString: "public", @@ -227,7 +230,7 @@ func TestDiscovery_checkDevice(t *testing.T) { } var sess *session.MockSession - discovery := NewDiscovery(checkConfig, session.NewMockSession) + discovery := NewDiscovery(checkConfig, session.NewMockSession, config) checkDeviceOnce := func() { sess = session.CreateMockSession() @@ -306,7 +309,9 @@ func TestDiscovery_checkDevice(t *testing.T) { } func TestDiscovery_createDevice(t *testing.T) { - SetTestRunPath() + config := agentconfig.NewMock(t) + config.SetWithoutSource("run_path", t.TempDir()) + checkConfig := &checkconfig.CheckConfig{ Network: "192.168.0.0/32", CommunityString: "public", @@ -315,7 +320,7 @@ func TestDiscovery_createDevice(t *testing.T) { DiscoveryAllowedFailures: 3, Namespace: "default", } - discovery := NewDiscovery(checkConfig, session.NewMockSession) + discovery := NewDiscovery(checkConfig, session.NewMockSession, config) ipAddr, ipNet, err := net.ParseCIDR(checkConfig.Network) assert.Nil(t, err) startingIP := ipAddr.Mask(ipNet.Mask) diff --git a/pkg/collector/corechecks/snmp/internal/discovery/testing.go b/pkg/collector/corechecks/snmp/internal/discovery/testing.go deleted file mode 100644 index 820bc1747acd7..0000000000000 --- a/pkg/collector/corechecks/snmp/internal/discovery/testing.go +++ /dev/null @@ -1,20 +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 test - -package discovery - -import ( - "path/filepath" - - pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" -) - -// SetTestRunPath sets run_path for testing -func SetTestRunPath() { - path, _ := filepath.Abs(filepath.Join(".", "test", "run_path")) - pkgconfigsetup.Datadog().SetWithoutSource("run_path", path) -} diff --git a/pkg/collector/corechecks/snmp/snmp.go b/pkg/collector/corechecks/snmp/snmp.go index 6b59932b057c6..29e2ae2327c5b 100644 --- a/pkg/collector/corechecks/snmp/snmp.go +++ b/pkg/collector/corechecks/snmp/snmp.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/DataDog/datadog-agent/comp/core/config" "go.uber.org/atomic" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" @@ -44,6 +45,7 @@ type Check struct { discovery *discovery.Discovery sessionFactory session.Factory workerRunDeviceCheckErrors *atomic.Uint64 + agentConfig config.Component } // Run executes the check @@ -155,10 +157,10 @@ func (c *Check) Configure(senderManager sender.SenderManager, integrationConfigD } if c.config.IsDiscovery() { - c.discovery = discovery.NewDiscovery(c.config, c.sessionFactory) + c.discovery = discovery.NewDiscovery(c.config, c.sessionFactory, c.agentConfig) c.discovery.Start() } else { - c.singleDeviceCk, err = devicecheck.NewDeviceCheck(c.config, c.config.IPAddress, c.sessionFactory) + c.singleDeviceCk, err = devicecheck.NewDeviceCheck(c.config, c.config.IPAddress, c.sessionFactory, c.agentConfig) if err != nil { return fmt.Errorf("failed to create device check: %s", err) } @@ -196,14 +198,17 @@ func (c *Check) GetDiagnoses() ([]diagnosis.Diagnosis, error) { } // Factory creates a new check factory -func Factory() optional.Option[func() check.Check] { - return optional.NewOption(newCheck) +func Factory(agentConfig config.Component) optional.Option[func() check.Check] { + return optional.NewOption(func() check.Check { + return newCheck(agentConfig) + }) } -func newCheck() check.Check { +func newCheck(agentConfig config.Component) check.Check { return &Check{ CheckBase: core.NewCheckBase(common.SnmpIntegrationName), sessionFactory: session.NewGosnmpSession, workerRunDeviceCheckErrors: atomic.NewUint64(0), + agentConfig: agentConfig, } } diff --git a/pkg/collector/corechecks/snmp/snmp_test.go b/pkg/collector/corechecks/snmp/snmp_test.go index 88513ca8ce0d3..2f1095dc52027 100644 --- a/pkg/collector/corechecks/snmp/snmp_test.go +++ b/pkg/collector/corechecks/snmp/snmp_test.go @@ -25,6 +25,7 @@ import ( "github.com/DataDog/datadog-agent/comp/aggregator/demultiplexer/demultiplexerimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" + agentconfig "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/forwarder/defaultforwarder" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" "github.com/DataDog/datadog-agent/pkg/collector/externalhost" @@ -996,10 +997,10 @@ community_string: public func TestCheckID(t *testing.T) { profile.SetConfdPathAndCleanProfiles() - check1 := newCheck() - check2 := newCheck() - check3 := newCheck() - checkSubnet := newCheck() + check1 := newCheck(agentconfig.NewMock(t)) + check2 := newCheck(agentconfig.NewMock(t)) + check3 := newCheck(agentconfig.NewMock(t)) + checkSubnet := newCheck(agentconfig.NewMock(t)) // language=yaml rawInstanceConfig1 := []byte(` ip_address: 1.1.1.1 @@ -2165,7 +2166,7 @@ func TestDeviceIDAsHostname(t *testing.T) { sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { return sess, nil } - chk := Check{sessionFactory: sessionFactory} + chk := Check{sessionFactory: sessionFactory, agentConfig: agentconfig.NewMock(t)} pkgconfigsetup.Datadog().SetWithoutSource("hostname", "test-hostname") pkgconfigsetup.Datadog().SetWithoutSource("tags", []string{"agent_tag1:val1", "agent_tag2:val2"}) senderManager := deps.Demultiplexer @@ -2358,7 +2359,7 @@ func TestDiscoveryDeviceIDAsHostname(t *testing.T) { sessionFactory := func(*checkconfig.CheckConfig) (session.Session, error) { return sess, nil } - chk := Check{sessionFactory: sessionFactory} + chk := Check{sessionFactory: sessionFactory, agentConfig: agentconfig.NewMock(t)} pkgconfigsetup.Datadog().SetWithoutSource("hostname", "my-hostname") senderManager := deps.Demultiplexer diff --git a/pkg/commonchecks/corechecks.go b/pkg/commonchecks/corechecks.go index 9af1e7f9833c8..cb20f9412ab2a 100644 --- a/pkg/commonchecks/corechecks.go +++ b/pkg/commonchecks/corechecks.go @@ -62,7 +62,7 @@ func RegisterChecks(store workloadmeta.Component, tagger tagger.Component, cfg c corecheckLoader.RegisterCheck(uptime.CheckName, uptime.Factory()) corecheckLoader.RegisterCheck(telemetryCheck.CheckName, telemetryCheck.Factory(telemetry)) corecheckLoader.RegisterCheck(ntp.CheckName, ntp.Factory()) - corecheckLoader.RegisterCheck(snmp.CheckName, snmp.Factory()) + corecheckLoader.RegisterCheck(snmp.CheckName, snmp.Factory(cfg)) corecheckLoader.RegisterCheck(networkpath.CheckName, networkpath.Factory(telemetry)) corecheckLoader.RegisterCheck(io.CheckName, io.Factory()) corecheckLoader.RegisterCheck(filehandles.CheckName, filehandles.Factory()) From 5b903ef9eef116f41bea415f3f58f0ad031eba8a Mon Sep 17 00:00:00 2001 From: Nicolas Schweitzer Date: Mon, 16 Dec 2024 16:30:49 +0100 Subject: [PATCH 31/78] fix(build_rc): Enable compatibility with agent6 (#32100) --- tasks/libs/common/gomodules.py | 7 +++++- tasks/pipeline.py | 43 +++------------------------------- 2 files changed, 9 insertions(+), 41 deletions(-) diff --git a/tasks/libs/common/gomodules.py b/tasks/libs/common/gomodules.py index bd37387a010a2..e4805588f73f5 100644 --- a/tasks/libs/common/gomodules.py +++ b/tasks/libs/common/gomodules.py @@ -252,8 +252,13 @@ def tag(self, agent_version): >>> [mod.tag("7.27.0") for mod in mods] [["7.27.0"], ["pkg/util/log/v0.27.0"]] """ + from invoke import Context + + from tasks.libs.common.git import is_agent6 + + major = "6" if is_agent6(Context()) else "7" if self.path == ".": - return ["7" + agent_version[1:]] + return [major + agent_version[1:]] return [f"{self.path}/{self.__version(agent_version)}"] diff --git a/tasks/pipeline.py b/tasks/pipeline.py index bff7b05b8b1ab..59aa6a3666172 100644 --- a/tasks/pipeline.py +++ b/tasks/pipeline.py @@ -43,11 +43,9 @@ # Tasks to trigger pipelines -def check_deploy_pipeline(repo: Project, git_ref: str, release_version_6, release_version_7, repo_branch): +def check_deploy_pipeline(repo_branch): """ - Run checks to verify a deploy pipeline is valid: - - it targets a valid repo branch - - it has matching Agent 6 and Agent 7 tags (depending on release_version_* values) + Run checks to verify a deploy pipeline is valid (it targets a valid repo branch) """ # Check that the target repo branch is valid @@ -57,41 +55,6 @@ def check_deploy_pipeline(repo: Project, git_ref: str, release_version_6, releas ) raise Exit(code=1) - # - # If git_ref matches v7 pattern and release_version_6 is not empty, make sure Gitlab has v6 tag. - # If git_ref matches v6 pattern and release_version_7 is not empty, make sure Gitlab has v7 tag. - # v7 version pattern should be able to match 7.12.24-rc2 and 7.12.34 - # - v7_pattern = r'^7\.(\d+\.\d+)(-.+|)$' - v6_pattern = r'^6\.(\d+\.\d+)(-.+|)$' - - match = re.match(v7_pattern, git_ref) - - # TODO(@spencergilbert): remove cross reference check when all references to a6 are removed - if release_version_6 and match: - # release_version_6 is not empty and git_ref matches v7 pattern, construct v6 tag and check. - tag_name = "6." + "".join(match.groups()) - try: - repo.tags.get(tag_name) - except GitlabError: - print(f"Cannot find GitLab v6 tag {tag_name} while trying to build git ref {git_ref}") - print("v6 tags are no longer created, this check will be removed in a later commit") - - print(f"Successfully cross checked v6 tag {tag_name} and git ref {git_ref}") - else: - match = re.match(v6_pattern, git_ref) - - if release_version_7 and match: - # release_version_7 is not empty and git_ref matches v6 pattern, construct v7 tag and check. - tag_name = "7." + "".join(match.groups()) - try: - repo.tags.get(tag_name) - except GitlabError as e: - print(f"Cannot find GitLab v7 tag {tag_name} while trying to build git ref {git_ref}") - raise Exit(code=1) from e - - print(f"Successfully cross checked v7 tag {tag_name} and git ref {git_ref}") - @task def clean_running_pipelines(ctx, git_ref=None, here=False, use_latest_sha=False, sha=None): @@ -292,7 +255,7 @@ def run( if deploy or deploy_installer: # Check the validity of the deploy pipeline - check_deploy_pipeline(repo, git_ref, release_version_6, release_version_7, repo_branch) + check_deploy_pipeline(repo_branch) # Force all builds and e2e tests to be run if not all_builds: print( From 62639e3a6ebd6f4580195ec945bb0da4dcf718be Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Mon, 16 Dec 2024 12:02:21 -0500 Subject: [PATCH 32/78] Build FIPS Agent chocolatey package (#32070) --- .gitlab-ci.yml | 4 +- .gitlab/choco_build/choco_build.yml | 36 ++- .../offline}/datadog-agent-offline.nuspec | 2 +- .../offline/tools}/VERIFICATION.txt | 0 .../offline/tools}/chocolateyinstall.ps1 | 0 .../online}/datadog-agent-online.nuspec | 2 +- .../online/tools}/VERIFICATION.txt | 0 .../online/tools}/chocolateyinstall.ps1 | 0 .../online/datadog-fips-agent-online.nuspec | 31 +++ .../online/tools/VERIFICATION.txt | 5 + .../online/tools/chocolateyinstall.ps1 | 27 ++ .../Generate-Chocolatey-Package.ps1 | 230 ++++++++++++------ tasks/winbuildscripts/chocopack.bat | 11 - 13 files changed, 258 insertions(+), 90 deletions(-) rename chocolatey/{ => datadog-agent/offline}/datadog-agent-offline.nuspec (97%) rename chocolatey/{tools-offline => datadog-agent/offline/tools}/VERIFICATION.txt (100%) rename chocolatey/{tools-offline => datadog-agent/offline/tools}/chocolateyinstall.ps1 (100%) rename chocolatey/{ => datadog-agent/online}/datadog-agent-online.nuspec (97%) rename chocolatey/{tools-online => datadog-agent/online/tools}/VERIFICATION.txt (100%) rename chocolatey/{tools-online => datadog-agent/online/tools}/chocolateyinstall.ps1 (100%) create mode 100644 chocolatey/datadog-fips-agent/online/datadog-fips-agent-online.nuspec create mode 100644 chocolatey/datadog-fips-agent/online/tools/VERIFICATION.txt create mode 100644 chocolatey/datadog-fips-agent/online/tools/chocolateyinstall.ps1 delete mode 100644 tasks/winbuildscripts/chocopack.bat diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 05351cee64faf..8d256885317c3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -584,8 +584,10 @@ workflow: when: always .on_deploy_stable_or_beta_repo_branch: + - !reference [.except_mergequeue] - <<: *if_not_stable_or_beta_repo_branch - when: never + when: manual + allow_failure: true - <<: *if_deploy .on_deploy_stable_or_beta_repo_branch_manual: diff --git a/.gitlab/choco_build/choco_build.yml b/.gitlab/choco_build/choco_build.yml index 97ef76cd478f0..8ec5bac814d18 100644 --- a/.gitlab/choco_build/choco_build.yml +++ b/.gitlab/choco_build/choco_build.yml @@ -14,8 +14,13 @@ windows_choco_offline_7_x64: script: - $ErrorActionPreference = "Stop" - Get-ChildItem omnibus\pkg - - copy omnibus\pkg\*.msi .\chocolatey\tools-offline\ - - docker run --rm -v "$(Get-Location):c:\mnt" registry.ddbuild.io/ci/datadog-agent-buildimages/windows_1809_${ARCH}${Env:DATADOG_AGENT_WINBUILDIMAGES_SUFFIX}:${Env:DATADOG_AGENT_WINBUILDIMAGES} c:\mnt\tasks\winbuildscripts\chocopack.bat offline + - copy omnibus\pkg\*.msi .\chocolatey\datadog-agent\offline\tools\ + - > + docker run --rm + -v "$(Get-Location):c:\mnt" + -e AWS_NETWORKING=true + registry.ddbuild.io/ci/datadog-agent-buildimages/windows_1809_${ARCH}${Env:DATADOG_AGENT_WINBUILDIMAGES_SUFFIX}:${Env:DATADOG_AGENT_WINBUILDIMAGES} + powershell.exe -C "C:\mnt\tasks\winbuildscripts\Generate-Chocolatey-Package.ps1 -InstallMethod offline -Flavor $FLAVOR -InstallDeps 1" - If ($lastExitCode -ne "0") { throw "Previous command returned $lastExitCode" } - copy build-out\*.nupkg omnibus\pkg artifacts: @@ -24,12 +29,11 @@ windows_choco_offline_7_x64: - omnibus/pkg # The online version of the choco job gets the msi package through the gitlab artifacts -windows_choco_online_7_x64: +.windows_choco_online_7_x64: rules: !reference [.on_deploy_stable_or_beta_repo_branch] stage: choco_and_install_script_build tags: ["runner:windows-docker", "windowsversion:1809"] - needs: ["deploy_packages_windows-x64-7"] variables: ARCH: "x64" script: @@ -43,10 +47,12 @@ windows_choco_online_7_x64: - > docker run --rm -v "$(Get-Location):c:\mnt" + -e CI_PROJECT_NAME=${CI_PROJECT_NAME} -e CI_PIPELINE_ID=${CI_PIPELINE_ID} -e BUCKET_BRANCH="$BUCKET_BRANCH" + -e AWS_NETWORKING=true registry.ddbuild.io/ci/datadog-agent-buildimages/windows_1809_${ARCH}${Env:DATADOG_AGENT_WINBUILDIMAGES_SUFFIX}:${Env:DATADOG_AGENT_WINBUILDIMAGES} - c:\mnt\tasks\winbuildscripts\chocopack.bat online c:\mnt\temp + powershell.exe -C "C:\mnt\tasks\winbuildscripts\Generate-Chocolatey-Package.ps1 -InstallMethod online -MSIDirectory c:\mnt\temp -Flavor $FLAVOR -InstallDeps 1" - If ($lastExitCode -ne "0") { throw "Previous command returned $lastExitCode" } - Remove-Item -Path "temp\" -Recurse -Force - copy build-out\*.nupkg omnibus\pkg @@ -58,3 +64,23 @@ windows_choco_online_7_x64: - omnibus/pkg # Sometimes Chocolatey is flakey retry: 2 + +windows_choco_online_7_x64: + extends: .windows_choco_online_7_x64 + # On dev/PR branches: + # - if the job is run manually it will create a package, but before the + # package can be installed, the deploy_windows_testing-a7 job must + # be run to push the MSI to the dd-agent-mstesting bucket. + needs: ["windows_msi_and_bosh_zip_x64-a7"] + variables: + FLAVOR: "datadog-agent" + +windows_choco_online_7_x64-fips: + extends: .windows_choco_online_7_x64 + # On dev/PR branches: + # - if the job is run manually it will create a package, but before the + # package can be installed, the deploy_windows_testing-a7-fips job must + # be run to push the MSI to the dd-agent-mstesting bucket. + needs: ["windows_msi_and_bosh_zip_x64-a7-fips"] + variables: + FLAVOR: "datadog-fips-agent" diff --git a/chocolatey/datadog-agent-offline.nuspec b/chocolatey/datadog-agent/offline/datadog-agent-offline.nuspec similarity index 97% rename from chocolatey/datadog-agent-offline.nuspec rename to chocolatey/datadog-agent/offline/datadog-agent-offline.nuspec index d049625a0ef3b..aedf9555a1c51 100644 --- a/chocolatey/datadog-agent-offline.nuspec +++ b/chocolatey/datadog-agent/offline/datadog-agent-offline.nuspec @@ -26,6 +26,6 @@ For example, to set the API key you may run: $release_notes$ - + diff --git a/chocolatey/tools-offline/VERIFICATION.txt b/chocolatey/datadog-agent/offline/tools/VERIFICATION.txt similarity index 100% rename from chocolatey/tools-offline/VERIFICATION.txt rename to chocolatey/datadog-agent/offline/tools/VERIFICATION.txt diff --git a/chocolatey/tools-offline/chocolateyinstall.ps1 b/chocolatey/datadog-agent/offline/tools/chocolateyinstall.ps1 similarity index 100% rename from chocolatey/tools-offline/chocolateyinstall.ps1 rename to chocolatey/datadog-agent/offline/tools/chocolateyinstall.ps1 diff --git a/chocolatey/datadog-agent-online.nuspec b/chocolatey/datadog-agent/online/datadog-agent-online.nuspec similarity index 97% rename from chocolatey/datadog-agent-online.nuspec rename to chocolatey/datadog-agent/online/datadog-agent-online.nuspec index d3cf3133a1a98..73f96bfb83871 100644 --- a/chocolatey/datadog-agent-online.nuspec +++ b/chocolatey/datadog-agent/online/datadog-agent-online.nuspec @@ -26,6 +26,6 @@ For example, to set the API key you may run: $release_notes$ - + diff --git a/chocolatey/tools-online/VERIFICATION.txt b/chocolatey/datadog-agent/online/tools/VERIFICATION.txt similarity index 100% rename from chocolatey/tools-online/VERIFICATION.txt rename to chocolatey/datadog-agent/online/tools/VERIFICATION.txt diff --git a/chocolatey/tools-online/chocolateyinstall.ps1 b/chocolatey/datadog-agent/online/tools/chocolateyinstall.ps1 similarity index 100% rename from chocolatey/tools-online/chocolateyinstall.ps1 rename to chocolatey/datadog-agent/online/tools/chocolateyinstall.ps1 diff --git a/chocolatey/datadog-fips-agent/online/datadog-fips-agent-online.nuspec b/chocolatey/datadog-fips-agent/online/datadog-fips-agent-online.nuspec new file mode 100644 index 0000000000000..90b6de924c628 --- /dev/null +++ b/chocolatey/datadog-fips-agent/online/datadog-fips-agent-online.nuspec @@ -0,0 +1,31 @@ + + + + datadog-fips-agent + $version$ + https://github.com/DataDog/datadog-agent/tree/main/chocolatey + Datadog + Datadog FIPS Agent + Datadog + https://github.com/DataDog/datadog-agent + https://datadog-prod.imgix.net/img/dd_logo_70x75.png + $copyright$ + https://raw.githubusercontent.com/DataDog/datadog-agent/main/LICENSE + true + https://docs.datadoghq.com + datadog agent monitoring admin + The Datadog FIPS Agent for Microsoft Windows + The Datadog FIPS Agent faithfully collects events and metrics and brings them to Datadog on your behalf so that you can do something useful with your monitoring and performance data. + +## Package settings + +You may set [custom settings](https://docs.datadoghq.com/agent/basic_agent_usage/windows/?tab=commandline#installation) to the Agent when installing by using the [`--installer-arguments` option of `choco install`](https://chocolatey.org/docs/getting-started#overriding-default-install-directory-or-other-advanced-install-concepts). + +For example, to set the API key you may run: +`choco install -ia="APIKEY=""YOUR_DATADOG_API_KEY""" datadog-fips-agent` + $release_notes$ + + + + + diff --git a/chocolatey/datadog-fips-agent/online/tools/VERIFICATION.txt b/chocolatey/datadog-fips-agent/online/tools/VERIFICATION.txt new file mode 100644 index 0000000000000..b13ab096f9a76 --- /dev/null +++ b/chocolatey/datadog-fips-agent/online/tools/VERIFICATION.txt @@ -0,0 +1,5 @@ +VERIFICATION +Verification is intended to assist the Chocolatey moderators and community in verifying that this package's contents are trustworthy. + +This package is published by Datadog itself. +The binaries are identical to other package types for the Datadog FIPS Agent. diff --git a/chocolatey/datadog-fips-agent/online/tools/chocolateyinstall.ps1 b/chocolatey/datadog-fips-agent/online/tools/chocolateyinstall.ps1 new file mode 100644 index 0000000000000..d928fdf7bff78 --- /dev/null +++ b/chocolatey/datadog-fips-agent/online/tools/chocolateyinstall.ps1 @@ -0,0 +1,27 @@ +$ErrorActionPreference = 'Stop'; + +$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)" +$packageArgs = @{ + packageName = $env:ChocolateyPackageName + unzipLocation = $toolsDir + fileType = 'msi' + # Note: Url is replaced at build time with the full URL to the MSI + url64bit = $__url_from_ci__ + checksum64 = $__checksum_from_ci__ + checksumType = 'sha256' + softwareName = "Datadog FIPS Agent" + silentArgs = "/qn /norestart /l*v `"$($env:TEMP)\$($packageName).$($env:chocolateyPackageVersion).MsiInstall.log`"" + validExitCodes= @(0, 3010, 1641) +} +Install-ChocolateyPackage @packageArgs + +$installInfo = @" +--- +install_method: + tool: chocolatey + tool_version: chocolatey-$($env:CHOCOLATEY_VERSION) + installer_version: chocolatey_package-online +"@ + +$appDataDir = (Get-ItemProperty -Path "HKLM:\SOFTWARE\Datadog\Datadog Agent").ConfigRoot +Out-File -FilePath $appDataDir\install_info -InputObject $installInfo diff --git a/tasks/winbuildscripts/Generate-Chocolatey-Package.ps1 b/tasks/winbuildscripts/Generate-Chocolatey-Package.ps1 index 9780b4a3a4103..991317602152f 100644 --- a/tasks/winbuildscripts/Generate-Chocolatey-Package.ps1 +++ b/tasks/winbuildscripts/Generate-Chocolatey-Package.ps1 @@ -1,97 +1,185 @@ +<# +.SYNOPSIS +Generates a Chocolatey package for the Datadog Agent. + +.PARAMETER installMethod +Specifies the installation method. Valid values are "offline" and "online". This parameter is mandatory. + +.PARAMETER msiDirectory +Specifies the directory containing the MSI file that will be used to calculate the checksum. This parameter is mandatory when the installMethod is "online". + +.PARAMETER Flavor +Specifies the flavor of the Datadog Agent. The default value is "datadog-agent". + +.PARAMETER VersionOverride +Overrides the Agent version when building packages locally for testing. + +.PARAMETER InstallDeps +Indicates whether to install dependencies. The default value is $true. + +.EXAMPLE +.\Generate-Chocolatey-Package.ps1 -installMethod online -Flavor datadog-agent -VersionOverride "7.62.0" -msiDirectory C:\mnt\omnibus\pkg\ + +Generates a chocolatey package for 7.62.0, requires the MSI file to be present in MSIDirectory. + +.EXAMPLE +$env:CI_PIPELINE_ID="50910739"; .\Generate-Chocolatey-Package.ps1 -installMethod online -Flavor datadog-agent -VersionOverride "7.62.0-devel.git.276.e59b1b3.pipeline.50910739" -msiDirectory C:\mnt\omnibus\pkg + +Generates a chocolatey package for PR/devel build 7.62.0-devel.git.276.e59b1b3.pipeline.50910739, requires the MSI file to be present in MSIDirectory. +The generated chocolatey package requires the MSI be uploaded to the dd-agent-mstesting bucket. +#> Param( - [Parameter(Mandatory=$true,Position=0)] + [Parameter(Mandatory=$true)] [ValidateSet("offline", "online")] [String] $installMethod, - [Parameter(Mandatory=$false,Position=1)] - [String] - $msiDirectory + [Parameter(Mandatory=$false)] + [String] + $msiDirectory, + + [Parameter(Mandatory=$false)] + [ValidateSet("datadog-agent", "datadog-fips-agent")] + [String] + $Flavor = "datadog-agent", + + [Parameter(Mandatory=$false)] + [String] + $VersionOverride, + + [bool] $InstallDeps = $true ) $ErrorActionPreference = 'Stop'; Set-Location c:\mnt -# Install chocolatey binary -$env:chocolateyUseWindowsCompression = 'true'; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) - -# Install dev tools, including invoke -pip3 install -r requirements.txt +if ($InstallDeps) { + # Install chocolatey + $env:chocolateyUseWindowsCompression = 'true'; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1')) + # Install dev tools, including invoke + pip3 install -r requirements.txt +} -$outputDirectory = "c:\mnt\build-out" -$rawAgentVersion = (inv agent.version --url-safe --major-version 7) +$repoRoot = "C:\mnt" +$outputDirectory = "$repoRoot\build-out" +if (![string]::IsNullOrEmpty($VersionOverride)) { + $rawAgentVersion = $VersionOverride +} else { + $rawAgentVersion = (inv agent.version --url-safe --major-version 7) +} $copyright = "Datadog {0}" -f (Get-Date).Year $releasePattern = "(\d+\.\d+\.\d+)" $releaseCandidatePattern = "(\d+\.\d+\.\d+)-rc\.(\d+)" $develPattern = "(\d+\.\d+\.\d+)-devel\.git\.\d+\.(.+)" -$nuspecFile = "c:\mnt\chocolatey\datadog-agent-online.nuspec" -$licensePath = "c:\mnt\chocolatey\tools-online\LICENSE.txt" -$installScript = "c:\mnt\chocolatey\tools-online\chocolateyinstall.ps1" - -if ($installMethod -eq "offline") { - $nuspecFile = "c:\mnt\chocolatey\datadog-agent-offline.nuspec" - $licensePath = "c:\mnt\chocolatey\tools-offline\LICENSE.txt" +# Build the package in a temporary directory +# Some of the build steps modify the package source, so we don't want to do this in the source directory +$buildTempDir = [System.IO.Path]::GetTempPath() + "\datadog-choco-build" +if (Test-Path $buildTempDir) { + Remove-Item -Recurse -Force $buildTempDir } - -if ($rawAgentVersion -match $releaseCandidatePattern) { - $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $releaseCandidatePattern - $agentVersion = "{0}-rc-{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2].Value - # We don't have release notes for RCs but this way the user can always see what commits are included in this RC - $releaseNotes = "https://github.com/DataDog/datadog-agent/releases/tag/{0}-rc.{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2] - $url = "https://s3.amazonaws.com/dd-agent-mstesting/builds/beta/ddagent-cli-$($agentVersionMatches.Matches.Groups[1])-rc.$($agentVersionMatches.Matches.Groups[2]).msi" -} elseif ($rawAgentVersion -match $develPattern) { - if ($installMethod -eq "online") { - # We don't publish online chocolatey packages for dev branches, error out - Write-Host "Chocolatey packages are not built for dev branches aborting" - exit 2 +New-Item -ItemType Directory -Path $buildTempDir | Out-Null +Push-Location -Path $buildTempDir +try { + # Set the artifact name and package source based on the flavor + if ($Flavor -eq "datadog-agent") { + # For historical reasons, use a different artifact name for the datadog-agent flavor + # See agent-release-management for more details + $artifactName = "ddagent-cli" + $packageSource = "$repoRoot\chocolatey\datadog-agent\$installMethod" + $nuspecFile = "datadog-agent-$installMethod.nuspec" + } elseif ($Flavor -eq "datadog-fips-agent") { + if ($installMethod -eq "offline") { + Write-Error "Offline install method not supported for flavor $Flavor" + exit 1 + } + $artifactName = "datadog-fips-agent" + $packageSource = "$repoRoot\chocolatey\datadog-fips-agent\online" + $nuspecFile = "datadog-fips-agent-online.nuspec" + } else { + Write-Error "Unknown flavor $Flavor" + exit 1 } - $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $develPattern - $agentVersion = "{0}-devel-{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2].Value - # We don't have release notes for devel, so point it to the generic url - $releaseNotes = "https://github.com/DataDog/datadog-agent/releases" -} elseif ($rawAgentVersion -match $releasePattern) { - $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $releasePattern - $agentVersion = $agentVersionMatches.Matches.Groups[1].Value - $releaseNotes = "https://github.com/DataDog/datadog-agent/releases/tag/$agentVersion" - $url = "https://s3.amazonaws.com/ddagent-windows-stable/ddagent-cli-$($agentVersion).msi" -} else { - Write-Host "Unknown agent version '$rawAgentVersion', aborting" - exit 3 -} -Invoke-WebRequest -Uri "https://raw.githubusercontent.com/DataDog/datadog-agent/main/LICENSE" -OutFile $licensePath - -Write-Host "Generating Chocolatey $installMethod package version $agentVersion in $outputDirectory" + # These files/directories are referenced in the nuspec file + $licensePath = "tools\LICENSE.txt" + $installScript = "tools\chocolateyinstall.ps1" + + # Copy package source to build temp dir + Copy-Item -Recurse -Force -Path $packageSource\* -Destination $buildTempDir + Copy-Item -Force -Path $repoRoot\LICENSE -Destination $licensePath + + # Generate URLs based on the Agent version + if ($rawAgentVersion -match $releaseCandidatePattern) { + $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $releaseCandidatePattern + $agentVersion = "{0}-rc-{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2].Value + # We don't have release notes for RCs but this way the user can always see what commits are included in this RC + $releaseNotes = "https://github.com/DataDog/datadog-agent/releases/tag/{0}-rc.{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2] + $url = "https://s3.amazonaws.com/dd-agent-mstesting/builds/beta/$artifactName-$($agentVersionMatches.Matches.Groups[1])-rc.$($agentVersionMatches.Matches.Groups[2]).msi" + } elseif ($rawAgentVersion -match $develPattern) { + if ($installMethod -eq "online") { + # For devel builds/branches, use the dd-agent-mstesting bucket URL + # This allows us to build and test the package in PRs, and locally + # by using the `-VersionOverride` param. + if ([string]::IsNullOrEmpty($env:CI_PIPELINE_ID)) { + Write-Error "CI_PIPELINE_ID is not set, aborting" + exit 1 + } else { + if ($rawAgentVersion -notmatch $env:CI_PIPELINE_ID) { + Write-Error "CI_PIPELINE_ID is not found in the agent version, aborting" -ErrorAction Continue + if ([string]::IsNullOrEmpty($env:BUCKET_BRANCH)) { + # inv agent.version requires BUCKET_BRANCH to be set when including pipeline in version + Write-Error "BUCKET_BRANCH is not set, if you are running this locally, set `$env:BUCKET_BRANCH='dev' or pass the -VersionOverride parameter" -ErrorAction Continue + } + exit 1 + } + $url = "https://s3.amazonaws.com/dd-agent-mstesting/pipelines/A7/$env:CI_PIPELINE_ID/$flavor-$rawAgentVersion-1-x86_64.msi" + } + } + $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $develPattern + $agentVersion = "{0}-devel-{1}" -f $agentVersionMatches.Matches.Groups[1], $agentVersionMatches.Matches.Groups[2].Value + # We don't have release notes for devel, so point it to the generic url + $releaseNotes = "https://github.com/DataDog/datadog-agent/releases" + } elseif ($rawAgentVersion -match $releasePattern) { + $agentVersionMatches = $rawAgentVersion | Select-String -Pattern $releasePattern + $agentVersion = $agentVersionMatches.Matches.Groups[1].Value + $releaseNotes = "https://github.com/DataDog/datadog-agent/releases/tag/$agentVersion" + $url = "https://s3.amazonaws.com/ddagent-windows-stable/$artifactName-$($agentVersion).msi" + } else { + Write-Host "Unknown agent version '$rawAgentVersion', aborting" + exit 1 + } -if (!(Test-Path $outputDirectory)) { - New-Item -ItemType Directory -Path $outputDirectory -} + Write-Host "Generating Chocolatey $installMethod package $flavor version $agentVersion in $(Get-Location)" -if ($installMethod -eq "online") { - try { - $tempMsi = Join-Path -Path "$msiDirectory" "datadog-agent-$rawAgentVersion-1-x86_64.msi" - if (!(Test-Path $tempMsi)) { - Write-Host "Error: Could not find MSI file in $tempMsi" - Get-ChildItem "$msiDirectory" - exit 1 + # Template the install script with the URL and checksum + if ($installMethod -eq "online") { + try { + $tempMsi = Join-Path -Path "$msiDirectory" "$flavor-$rawAgentVersion-1-x86_64.msi" + if (!(Test-Path $tempMsi)) { + Write-Host "Error: Could not find MSI file in $tempMsi" + Get-ChildItem "$msiDirectory" + exit 1 + } + $checksum = (Get-FileHash $tempMsi -Algorithm SHA256).Hash + } + catch { + Write-Host "Error: Could not generate checksum for package $($tempMsi): $($_)" + exit 1 } - $checksum = (Get-FileHash $tempMsi -Algorithm SHA256).Hash - } - catch { - Write-Host "Error: Could not generate checksum for package $($tempMsi): $($_)" - exit 4 + # Set the $url in the install script + (Get-Content $installScript).replace('$__url_from_ci__', '"' + $url + '"').replace('$__checksum_from_ci__', '"' + $checksum + '"') | Set-Content $installScript } - # Set the $url in the install script - (Get-Content $installScript).replace('$__url_from_ci__', '"' + $url + '"').replace('$__checksum_from_ci__', '"' + $checksum + '"') | Set-Content $installScript -} - -Write-Host "Generated nupsec file:" -Write-Host (Get-Content $installScript | Out-String) -Write-Host choco pack --out=$outputDirectory $nuspecFile --version $agentVersion release_notes=$releaseNotes copyright=$copyright -choco pack --out=$outputDirectory $nuspecFile --version $agentVersion release_notes=$releaseNotes copyright=$copyright + Write-Host "Generated nuspec file:" + Write-Host (Get-Content $installScript | Out-String) -# restore installScript (useful for local testing/deployment) -git checkout $installScript + if (!(Test-Path $outputDirectory)) { + New-Item -ItemType Directory -Path $outputDirectory + } + Write-Host choco pack --out=$outputDirectory $nuspecFile --version $agentVersion release_notes=$releaseNotes copyright=$copyright + choco pack --out=$outputDirectory $nuspecFile --version $agentVersion release_notes=$releaseNotes copyright=$copyright +} finally { + Pop-Location +} diff --git a/tasks/winbuildscripts/chocopack.bat b/tasks/winbuildscripts/chocopack.bat deleted file mode 100644 index df8915ae14698..0000000000000 --- a/tasks/winbuildscripts/chocopack.bat +++ /dev/null @@ -1,11 +0,0 @@ -if not exist c:\mnt\ goto nomntdir - -@echo c:\mnt found, continuing - -Powershell -C "C:\mnt\tasks\winbuildscripts\Generate-Chocolatey-Package.ps1 %1 %2" || exit /b 1 -goto :EOF - -:nomntdir -@echo directory not mounted, parameters incorrect -exit /b 2 -goto :EOF From b3667a382d2d688d29575eb6b55b8be6d901d0b9 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Mon, 16 Dec 2024 18:07:48 +0100 Subject: [PATCH 33/78] remove `gce` build tag (#32226) --- pkg/util/cloudproviders/gce/gce_no_tags.go | 17 ----------------- pkg/util/cloudproviders/gce/gce_tags.go | 2 -- pkg/util/cloudproviders/gce/gce_tags_test.go | 2 -- tasks/build_tags.py | 6 ++---- 4 files changed, 2 insertions(+), 25 deletions(-) delete mode 100644 pkg/util/cloudproviders/gce/gce_no_tags.go diff --git a/pkg/util/cloudproviders/gce/gce_no_tags.go b/pkg/util/cloudproviders/gce/gce_no_tags.go deleted file mode 100644 index 270cf77ce59ce..0000000000000 --- a/pkg/util/cloudproviders/gce/gce_no_tags.go +++ /dev/null @@ -1,17 +0,0 @@ -// Unless explicitly stated otherwise all files in this repository are licensed -// under the Apache License Version 2.0. -// This product includes software developed at Datadog (https://www.datadoghq.com/). -// Copyright 2016-present Datadog, Inc. - -//go:build !gce - -package gce - -import "context" - -// GetTags gets the tags from the GCE api -func GetTags(_ context.Context) ([]string, error) { - tags := []string{} - - return tags, nil -} diff --git a/pkg/util/cloudproviders/gce/gce_tags.go b/pkg/util/cloudproviders/gce/gce_tags.go index 31c986277bc97..9440ba6ccc21b 100644 --- a/pkg/util/cloudproviders/gce/gce_tags.go +++ b/pkg/util/cloudproviders/gce/gce_tags.go @@ -3,8 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build gce - package gce import ( diff --git a/pkg/util/cloudproviders/gce/gce_tags_test.go b/pkg/util/cloudproviders/gce/gce_tags_test.go index 06f15ed938f21..80a803e3cc687 100644 --- a/pkg/util/cloudproviders/gce/gce_tags_test.go +++ b/pkg/util/cloudproviders/gce/gce_tags_test.go @@ -3,8 +3,6 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-present Datadog, Inc. -//go:build gce - package gce import ( diff --git a/tasks/build_tags.py b/tasks/build_tags.py index ddd993490f389..a3c13f088eb8b 100644 --- a/tasks/build_tags.py +++ b/tasks/build_tags.py @@ -28,7 +28,6 @@ "ec2", "etcd", "fargateprocess", - "gce", "jmx", "jetson", "kubeapiserver", @@ -68,7 +67,6 @@ "docker", "ec2", "etcd", - "gce", "jetson", "jmx", "kubeapiserver", @@ -109,7 +107,7 @@ FIPS_AGENT_TAGS = AGENT_TAGS.union({"goexperiment.systemcrypto"}) # 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"} +CLUSTER_AGENT_TAGS = {"clusterchecks", "datadog.no_waf", "kubeapiserver", "orchestrator", "zlib", "zstd", "ec2"} # CLUSTER_AGENT_CLOUDFOUNDRY_TAGS lists the tags needed when building the cloudfoundry cluster-agent CLUSTER_AGENT_CLOUDFOUNDRY_TAGS = {"clusterchecks"} @@ -121,7 +119,7 @@ IOT_AGENT_TAGS = {"jetson", "otlp", "systemd", "zlib", "zstd"} # INSTALLER_TAGS lists the tags needed when building the installer -INSTALLER_TAGS = {"docker", "ec2", "gce", "kubelet"} +INSTALLER_TAGS = {"docker", "ec2", "kubelet"} # PROCESS_AGENT_TAGS lists the tags necessary to build the process-agent PROCESS_AGENT_TAGS = AGENT_TAGS.union({"fargateprocess"}).difference({"otlp", "python", "trivy"}) From afd5e2a1a03e64b7b5b7d2c37224335ce0c9d07c Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 16 Dec 2024 12:07:56 -0500 Subject: [PATCH 34/78] [config] Don't log network_process.enabled by default (#32172) --- pkg/network/config/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/network/config/config.go b/pkg/network/config/config.go index c6d57230a4003..47f8c85cda215 100644 --- a/pkg/network/config/config.go +++ b/pkg/network/config/config.go @@ -414,8 +414,8 @@ func New() *Config { log.Info("network tracer DNS inspection disabled by configuration") } - if c.EnableProcessEventMonitoring { - log.Info("network process event monitoring enabled") + if !c.EnableProcessEventMonitoring { + log.Info("network process event monitoring disabled") } return c } From 328a1931eb51c8dddd549a3bf94ba2ceb5353851 Mon Sep 17 00:00:00 2001 From: Branden Clark Date: Mon, 16 Dec 2024 12:08:12 -0500 Subject: [PATCH 35/78] allow testing deploy jobs to be run manually (#32173) --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8d256885317c3..b2761698a9b16 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -616,7 +616,8 @@ workflow: .except_no_tests_no_deploy: - if: $DEPLOY_AGENT == "false" && $DDR_WORKFLOW_ID == null && $RUN_E2E_TESTS == "off" - when: never + when: manual + allow_failure: true .on_main_or_release_branch: - <<: *if_main_branch From 626f570cf380d1c1fc680ccb85934e5abcb24a9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Mon, 16 Dec 2024 18:08:21 +0100 Subject: [PATCH 36/78] [incident-33304] Expect container tags on dogstatsd metrics (#32222) Co-authored-by: Wassim Dhif --- test/new-e2e/tests/containers/k8s_test.go | 69 ++++++++--------------- 1 file changed, 22 insertions(+), 47 deletions(-) diff --git a/test/new-e2e/tests/containers/k8s_test.go b/test/new-e2e/tests/containers/k8s_test.go index fb235f979c951..872714c799525 100644 --- a/test/new-e2e/tests/containers/k8s_test.go +++ b/test/new-e2e/tests/containers/k8s_test.go @@ -41,6 +41,7 @@ const ( kubeNamespaceDogstatsWorkload = "workload-dogstatsd" kubeNamespaceDogstatsStandaloneWorkload = "workload-dogstatsd-standalone" kubeNamespaceTracegenWorkload = "workload-tracegen" + kubeDeploymentDogstatsdUDP = "dogstatsd-udp" kubeDeploymentDogstatsdUDPOrigin = "dogstatsd-udp-origin-detection" kubeDeploymentDogstatsdUDPExternalData = "dogstatsd-udp-external-data-only" kubeDeploymentDogstatsdUDS = "dogstatsd-uds" @@ -796,44 +797,53 @@ func (suite *k8sSuite) TestCPU() { func (suite *k8sSuite) TestDogstatsdInAgent() { // Test with UDS - suite.testDogstatsdContainerID(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDS) + suite.testDogstatsd(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDS) // Test with UDP + Origin detection - suite.testDogstatsdContainerID(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDPOrigin) + suite.testDogstatsd(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDPOrigin) // Test with UDP + DD_ENTITY_ID - suite.testDogstatsdPodUID(kubeNamespaceDogstatsWorkload) + suite.testDogstatsd(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDP) // Test with UDP + External Data suite.testDogstatsdExternalData(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDPExternalData) } func (suite *k8sSuite) TestDogstatsdStandalone() { // Test with UDS - suite.testDogstatsdContainerID(kubeNamespaceDogstatsStandaloneWorkload, kubeDeploymentDogstatsdUDS) + suite.testDogstatsd(kubeNamespaceDogstatsStandaloneWorkload, kubeDeploymentDogstatsdUDS) // Dogstatsd standalone does not support origin detection // Test with UDP + DD_ENTITY_ID - suite.testDogstatsdPodUID(kubeNamespaceDogstatsWorkload) + suite.testDogstatsd(kubeNamespaceDogstatsWorkload, kubeDeploymentDogstatsdUDP) } -func (suite *k8sSuite) testDogstatsdPodUID(kubeNamespace string) { - // Test dogstatsd origin detection with UDP + DD_ENTITY_ID +func (suite *k8sSuite) testDogstatsd(kubeNamespace, kubeDeployment string) { suite.testMetric(&testMetricArgs{ Filter: testMetricFilterArgs{ Name: "custom.metric", Tags: []string{ - "^kube_deployment:dogstatsd-udp$", + "^kube_deployment:" + regexp.QuoteMeta(kubeDeployment) + "$", "^kube_namespace:" + regexp.QuoteMeta(kubeNamespace) + "$", }, }, Expect: testMetricExpectArgs{ Tags: &[]string{ - `^kube_deployment:dogstatsd-udp$`, + `^container_id:`, + `^container_name:dogstatsd$`, + `^display_container_name:dogstatsd`, + `^git.commit.sha:`, // org.opencontainers.image.revision docker image label + `^git.repository_url:https://github.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label + `^image_id:ghcr.io/datadog/apps-dogstatsd@sha256:`, + `^image_name:ghcr.io/datadog/apps-dogstatsd$`, + `^image_tag:main$`, + `^kube_container_name:dogstatsd$`, + `^kube_deployment:` + regexp.QuoteMeta(kubeDeployment) + `$`, "^kube_namespace:" + regexp.QuoteMeta(kubeNamespace) + "$", `^kube_ownerref_kind:replicaset$`, - `^kube_ownerref_name:dogstatsd-udp-[[:alnum:]]+$`, + `^kube_ownerref_name:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+$`, `^kube_qos:Burstable$`, - `^kube_replica_set:dogstatsd-udp-[[:alnum:]]+$`, - `^pod_name:dogstatsd-udp-[[:alnum:]]+-[[:alnum:]]+$`, + `^kube_replica_set:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+$`, + `^pod_name:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+-[[:alnum:]]+$`, `^pod_phase:running$`, `^series:`, + `^short_image:apps-dogstatsd$`, }, }, }) @@ -911,41 +921,6 @@ func (suite *k8sSuite) testDogstatsdExternalData(kubeNamespace, kubeDeployment s }) } -func (suite *k8sSuite) testDogstatsdContainerID(kubeNamespace, kubeDeployment string) { - suite.testMetric(&testMetricArgs{ - Filter: testMetricFilterArgs{ - Name: "custom.metric", - Tags: []string{ - "^kube_deployment:" + regexp.QuoteMeta(kubeDeployment) + "$", - "^kube_namespace:" + regexp.QuoteMeta(kubeNamespace) + "$", - }, - }, - Expect: testMetricExpectArgs{ - Tags: &[]string{ - `^container_id:`, - `^container_name:dogstatsd$`, - `^display_container_name:dogstatsd`, - `^git.commit.sha:`, // org.opencontainers.image.revision docker image label - `^git.repository_url:https://github.com/DataDog/test-infra-definitions$`, // org.opencontainers.image.source docker image label - `^image_id:ghcr.io/datadog/apps-dogstatsd@sha256:`, - `^image_name:ghcr.io/datadog/apps-dogstatsd$`, - `^image_tag:main$`, - `^kube_container_name:dogstatsd$`, - `^kube_deployment:` + regexp.QuoteMeta(kubeDeployment) + `$`, - "^kube_namespace:" + regexp.QuoteMeta(kubeNamespace) + "$", - `^kube_ownerref_kind:replicaset$`, - `^kube_ownerref_name:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+$`, - `^kube_qos:Burstable$`, - `^kube_replica_set:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+$`, - `^pod_name:` + regexp.QuoteMeta(kubeDeployment) + `-[[:alnum:]]+-[[:alnum:]]+$`, - `^pod_phase:running$`, - `^series:`, - `^short_image:apps-dogstatsd$`, - }, - }, - }) -} - func (suite *k8sSuite) TestPrometheus() { // Test Prometheus check suite.testMetric(&testMetricArgs{ From 48b4521f4137351498bcc7c1679743daad4c5065 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Mon, 16 Dec 2024 18:36:32 +0100 Subject: [PATCH 37/78] [ASCII-2614] Add a task to generate a dependency graph (#32206) Co-authored-by: louis-cqrl <93274433+louis-cqrl@users.noreply.github.com> --- tasks/go_deps.py | 107 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/tasks/go_deps.py b/tasks/go_deps.py index 5310ede1384d8..b08560eaa8056 100644 --- a/tasks/go_deps.py +++ b/tasks/go_deps.py @@ -1,5 +1,7 @@ import datetime import os +import shutil +import tempfile from collections.abc import Iterable from invoke.context import Context @@ -185,3 +187,108 @@ def show(ctx: Context, build: str, flavor: str = AgentFlavor.base.name, os: str for dep in deps: print(dep) + + +@task( + help={ + 'build': f'The agent build to use, one of {", ".join(BINARIES.keys())}', + 'flavor': f'The agent flavor to use, one of {", ".join(AgentFlavor.__members__.keys())}. Defaults to base', + 'os': f'The OS to use, one of {", ".join(GOOS_MAPPING.keys())}. Defaults to host platform', + 'arch': f'The architecture to use, one of {", ".join(GOARCH_MAPPING.keys())}. Defaults to host architecture', + 'entrypoint': 'A Go package path, defaults to the entrypoint of the build.', + 'target': 'A Go package path. If specified, generate a graph from the entrypoint to the target.', + 'std': 'Whether to include the standard library in the graph', + 'cluster': 'Whether to group packages by cluster.', + 'fmt': 'The format of the generated image. Must be accepted by "dot -T". Defaults to "svg".', + 'auto_open': 'Whether to open the generated graph automatically.', + } +) +def graph( + ctx: Context, + build: str, + flavor: str = AgentFlavor.base.name, + os: str | None = None, + arch: str | None = None, + entrypoint: str | None = None, + target: str | None = None, + std: bool = False, + cluster: bool = False, + fmt: str = "svg", + auto_open: bool = True, +): + """ + Generate a dependency graph of the given build. + Requires https://github.com/loov/goda and Graphviz's `dot` tools to be in the PATH. + + Usage: + Dependency graph of the trace-agent on Linux + inv -e go-deps.graph --build trace-agent --os linux + Reachability graph of the process-agent on Linux to k8s.io/... dependencies + inv -e go-deps.graph --build process-agent --os linux --target k8s.io/... + Dependency graph of workloadmeta on Linux when using the same build tags as the core-agent + inv -e go-deps.graph --build agent --os linux --entrypoint github.com/DataDog/datadog-agent/comp/core/workloadmeta/...:all + """ + if shutil.which("goda") is None: + raise Exit( + code=1, + message=color_message( + "'goda' not found in PATH. Please install it with `go install github.com/loov/goda@latest`", "red" + ), + ) + + if os is None: + goos = ctx.run("go env GOOS", hide=True) + assert goos is not None + os = goos.stdout.strip() + assert os in GOOS_MAPPING.values() + + if arch is None: + goarch = ctx.run("go env GOARCH", hide=True) + assert goarch is not None + arch = goarch.stdout.strip() + assert arch in GOARCH_MAPPING.values() + + stdarg = "-std" if std else "" + clusterarg = "-cluster" if cluster else "" + + if entrypoint is None: + entrypoint = BINARIES[build]["entrypoint"] + entrypoint = f"github.com/DataDog/datadog-agent/{entrypoint}:all" + + build_tags = get_default_build_tags( + build=build, flavor=AgentFlavor[flavor], platform=key_for_value(GOOS_MAPPING, os) + ) + for tag in build_tags: + entrypoint = f"{tag}=1({entrypoint})" + + expr = entrypoint if target is None else f"reach({entrypoint}, {target})" + + cmd = f"goda graph {stdarg} {clusterarg} \"{expr}\"" + + env = {"GOOS": os, "GOARCH": arch} + res = ctx.run(cmd, env=env, hide='out') + assert res + + tmpfile = tempfile.mktemp(prefix="graph-") + + dotfile = tmpfile + ".dot" + print("Saving dot file in " + dotfile) + with open(dotfile, "w") as f: + f.write(res.stdout) + + if shutil.which("dot") is None: + raise Exit( + code=1, + message=color_message( + "'dot' not found in PATH. Please follow instructions on https://graphviz.org/download/ to install it.", + "red", + ), + ) + + fmtfile = tmpfile + "." + fmt + print(f"Rendering {fmt} in {fmtfile}") + ctx.run(f"dot -T{fmt} -o {fmtfile} {dotfile}") + + if auto_open: + print(f"Opening {fmt} file") + ctx.run(f"open {fmtfile}") From f9698b996a20fabee3532e54a4b2e8cfb3efced4 Mon Sep 17 00:00:00 2001 From: Pierre Gimalac Date: Mon, 16 Dec 2024 19:00:40 +0100 Subject: [PATCH 38/78] [ASCII-2613] Move SetupCoreDump to pkg/util/coredump to remove deps from trace-agent (#32174) --- cmd/agent/subcommands/run/command.go | 4 ++-- cmd/cluster-agent/subcommands/start/command.go | 4 ++-- cmd/dogstatsd/subcommands/start/command.go | 4 ++-- cmd/process-agent/command/main_common.go | 4 ++-- cmd/security-agent/subcommands/start/command.go | 4 ++-- cmd/system-probe/subcommands/run/command.go | 4 ++-- comp/trace/agent/impl/run.go | 4 ++-- pkg/util/{ => coredump}/core.go | 6 +++--- pkg/util/{ => coredump}/core_windows.go | 6 +++--- pkg/util/coredump/docs.go | 7 +++++++ 10 files changed, 27 insertions(+), 20 deletions(-) rename pkg/util/{ => coredump}/core.go (81%) rename pkg/util/{ => coredump}/core_windows.go (73%) create mode 100644 pkg/util/coredump/docs.go diff --git a/cmd/agent/subcommands/run/command.go b/cmd/agent/subcommands/run/command.go index 9c126f014aab2..76775693251e4 100644 --- a/cmd/agent/subcommands/run/command.go +++ b/cmd/agent/subcommands/run/command.go @@ -146,8 +146,8 @@ import ( jmxStatus "github.com/DataDog/datadog-agent/pkg/status/jmx" systemprobeStatus "github.com/DataDog/datadog-agent/pkg/status/systemprobe" pkgTelemetry "github.com/DataDog/datadog-agent/pkg/telemetry" - "github.com/DataDog/datadog-agent/pkg/util" pkgcommon "github.com/DataDog/datadog-agent/pkg/util/common" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/defaultpaths" "github.com/DataDog/datadog-agent/pkg/util/flavor" "github.com/DataDog/datadog-agent/pkg/util/fxutil" @@ -512,7 +512,7 @@ func startAgent( log.Infof("Starting Datadog Agent v%v", version.AgentVersion) } - if err := util.SetupCoreDump(pkgconfigsetup.Datadog()); err != nil { + if err := coredump.Setup(pkgconfigsetup.Datadog()); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } diff --git a/cmd/cluster-agent/subcommands/start/command.go b/cmd/cluster-agent/subcommands/start/command.go index f43ae03615401..aea5e4207f57c 100644 --- a/cmd/cluster-agent/subcommands/start/command.go +++ b/cmd/cluster-agent/subcommands/start/command.go @@ -81,7 +81,7 @@ import ( hostnameStatus "github.com/DataDog/datadog-agent/pkg/status/clusteragent/hostname" endpointsStatus "github.com/DataDog/datadog-agent/pkg/status/endpoints" "github.com/DataDog/datadog-agent/pkg/status/health" - "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/hostname" "github.com/DataDog/datadog-agent/pkg/util/kubernetes/apiserver" @@ -241,7 +241,7 @@ func start(log log.Component, // Starting Cluster Agent sequence // Initialization order is important for multiple reasons, see comments - if err := util.SetupCoreDump(config); err != nil { + if err := coredump.Setup(config); err != nil { pkglog.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } diff --git a/cmd/dogstatsd/subcommands/start/command.go b/cmd/dogstatsd/subcommands/start/command.go index f61ae13db837d..b6e6a63361ae8 100644 --- a/cmd/dogstatsd/subcommands/start/command.go +++ b/cmd/dogstatsd/subcommands/start/command.go @@ -57,7 +57,7 @@ import ( compressionfx "github.com/DataDog/datadog-agent/comp/serializer/compression/fx" "github.com/DataDog/datadog-agent/pkg/serializer" "github.com/DataDog/datadog-agent/pkg/status/health" - "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/fxutil" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" pkglogsetup "github.com/DataDog/datadog-agent/pkg/util/log/setup" @@ -235,7 +235,7 @@ func RunDogstatsd(_ context.Context, cliParams *CLIParams, config config.Compone } }() - if err := util.SetupCoreDump(config); err != nil { + if err := coredump.Setup(config); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } diff --git a/cmd/process-agent/command/main_common.go b/cmd/process-agent/command/main_common.go index 188c9684fd30b..dcb9287e74f04 100644 --- a/cmd/process-agent/command/main_common.go +++ b/cmd/process-agent/command/main_common.go @@ -61,7 +61,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/process/metadata/workloadmeta/collector" "github.com/DataDog/datadog-agent/pkg/process/util" proccontainers "github.com/DataDog/datadog-agent/pkg/process/util/containers" - ddutil "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/fxutil/logging" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -296,7 +296,7 @@ type miscDeps struct { // Todo: (Components) WorkloadMeta, remoteTagger // Todo: move metadata/workloadmeta/collector to workloadmeta func initMisc(deps miscDeps) error { - if err := ddutil.SetupCoreDump(deps.Config); err != nil { + if err := coredump.Setup(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/cmd/security-agent/subcommands/start/command.go b/cmd/security-agent/subcommands/start/command.go index 8386100c0031b..12a93fc4560ff 100644 --- a/cmd/security-agent/subcommands/start/command.go +++ b/cmd/security-agent/subcommands/start/command.go @@ -63,7 +63,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/security/agent" "github.com/DataDog/datadog-agent/pkg/security/utils" "github.com/DataDog/datadog-agent/pkg/status/health" - "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/fxutil" "github.com/DataDog/datadog-agent/pkg/util/optional" "github.com/DataDog/datadog-agent/pkg/util/profiling" @@ -257,7 +257,7 @@ var errNoAPIKeyConfigured = errors.New("no API key configured") // RunAgent initialized resources and starts API server func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) (err error) { - if err := util.SetupCoreDump(config); err != nil { + if err := coredump.Setup(config); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } diff --git a/cmd/system-probe/subcommands/run/command.go b/cmd/system-probe/subcommands/run/command.go index cd35036946e88..659f7e2fe0f52 100644 --- a/cmd/system-probe/subcommands/run/command.go +++ b/cmd/system-probe/subcommands/run/command.go @@ -61,7 +61,7 @@ import ( ebpftelemetry "github.com/DataDog/datadog-agent/pkg/ebpf/telemetry" processstatsd "github.com/DataDog/datadog-agent/pkg/process/statsd" ddruntime "github.com/DataDog/datadog-agent/pkg/runtime" - "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/fxutil" pkglog "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/optional" @@ -333,7 +333,7 @@ func startSystemProbe(log log.Component, statsd compstatsd.Component, telemetry return ErrNotEnabled } - if err := util.SetupCoreDump(sysprobeconfig); err != nil { + if err := coredump.Setup(sysprobeconfig); err != nil { log.Warnf("cannot setup core dumps: %s, core dumps might not be available after a crash", err) } diff --git a/comp/trace/agent/impl/run.go b/comp/trace/agent/impl/run.go index df25106b78755..20ecbc6496a1b 100644 --- a/comp/trace/agent/impl/run.go +++ b/comp/trace/agent/impl/run.go @@ -23,7 +23,7 @@ import ( "github.com/DataDog/datadog-agent/pkg/trace/info" "github.com/DataDog/datadog-agent/pkg/trace/telemetry" "github.com/DataDog/datadog-agent/pkg/trace/watchdog" - "github.com/DataDog/datadog-agent/pkg/util" + "github.com/DataDog/datadog-agent/pkg/util/coredump" "github.com/DataDog/datadog-agent/pkg/util/log" "github.com/DataDog/datadog-agent/pkg/util/profiling" "github.com/DataDog/datadog-agent/pkg/version" @@ -41,7 +41,7 @@ func runAgentSidekicks(ag component) error { defer watchdog.LogOnPanic(ag.Statsd) - if err := util.SetupCoreDump(pkgconfigsetup.Datadog()); err != nil { + if err := coredump.Setup(pkgconfigsetup.Datadog()); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } diff --git a/pkg/util/core.go b/pkg/util/coredump/core.go similarity index 81% rename from pkg/util/core.go rename to pkg/util/coredump/core.go index 77649ea1d53f3..64b4f35c3a12a 100644 --- a/pkg/util/core.go +++ b/pkg/util/coredump/core.go @@ -5,7 +5,7 @@ //go:build !windows -package util +package coredump import ( "fmt" @@ -16,8 +16,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/config/model" ) -// SetupCoreDump enables core dumps and sets the core dump size limit based on configuration -func SetupCoreDump(cfg model.Reader) error { +// Setup enables core dumps and sets the core dump size limit based on configuration +func Setup(cfg model.Reader) error { if cfg.GetBool("go_core_dump") { debug.SetTraceback("crash") diff --git a/pkg/util/core_windows.go b/pkg/util/coredump/core_windows.go similarity index 73% rename from pkg/util/core_windows.go rename to pkg/util/coredump/core_windows.go index c11422c8aff2b..3e7d3915f7636 100644 --- a/pkg/util/core_windows.go +++ b/pkg/util/coredump/core_windows.go @@ -3,7 +3,7 @@ // This product includes software developed at Datadog (https://www.datadoghq.com/). // Copyright 2016-2020 Datadog, Inc. -package util +package coredump import ( "fmt" @@ -11,8 +11,8 @@ import ( "github.com/DataDog/datadog-agent/pkg/config/model" ) -// SetupCoreDump enables core dumps and sets the core dump size limit based on configuration -func SetupCoreDump(cfg model.Reader) error { +// Setup enables core dumps and sets the core dump size limit based on configuration +func Setup(cfg model.Reader) error { if cfg.GetBool("go_core_dump") { return fmt.Errorf("Not supported on Windows") } diff --git a/pkg/util/coredump/docs.go b/pkg/util/coredump/docs.go new file mode 100644 index 0000000000000..43371b50e7dac --- /dev/null +++ b/pkg/util/coredump/docs.go @@ -0,0 +1,7 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-2020 Datadog, Inc. + +// Package coredump provides utils to enable core dumps and set core dump size limit +package coredump From b9100191842713916293d3e091c384755ac27f75 Mon Sep 17 00:00:00 2001 From: Ibraheem Aboulnaga <13734402+IbraheemA@users.noreply.github.com> Date: Mon, 16 Dec 2024 13:32:11 -0500 Subject: [PATCH 39/78] Remove SpanNameAsResourceName from default OTel agent config (#32112) --- cmd/otel-agent/config/agent_config_test.go | 4 ++-- .../impl/testdata/simple-dd/config-enhanced-result.yaml | 2 +- .../impl/testdata/simple-dd/config-provided-result.yaml | 2 +- .../otlp/components/exporter/datadogexporter/factory.go | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/otel-agent/config/agent_config_test.go b/cmd/otel-agent/config/agent_config_test.go index 2d08cfb4ac58f..01567d48d7ffe 100644 --- a/cmd/otel-agent/config/agent_config_test.go +++ b/cmd/otel-agent/config/agent_config_test.go @@ -78,7 +78,7 @@ func (suite *ConfigTestSuite) TestAgentConfigDefaults() { assert.Equal(t, 6, c.Get("logs_config.compression_level")) assert.Equal(t, "https://trace.agent.datadoghq.com", c.Get("apm_config.apm_dd_url")) assert.Equal(t, false, c.Get("apm_config.receiver_enabled")) - assert.Equal(t, true, c.Get("otlp_config.traces.span_name_as_resource_name")) + assert.Equal(t, false, c.Get("otlp_config.traces.span_name_as_resource_name")) assert.Equal(t, []string{"enable_receive_resource_spans_v2", "enable_operation_and_resource_name_logic_v2", "enable_otlp_compute_top_level_by_span_kind"}, c.Get("apm_config.features")) } @@ -104,7 +104,7 @@ func (suite *ConfigTestSuite) TestAgentConfigWithDatadogYamlDefaults() { assert.Equal(t, 6, c.Get("logs_config.compression_level")) assert.Equal(t, "https://trace.agent.datadoghq.com", c.Get("apm_config.apm_dd_url")) assert.Equal(t, false, c.Get("apm_config.receiver_enabled")) - assert.Equal(t, true, c.Get("otlp_config.traces.span_name_as_resource_name")) + assert.Equal(t, false, c.Get("otlp_config.traces.span_name_as_resource_name")) assert.Equal(t, []string{"enable_receive_resource_spans_v2", "enable_operation_and_resource_name_logic_v2", "enable_otlp_compute_top_level_by_span_kind"}, c.Get("apm_config.features")) // log_level from datadog.yaml takes precedence -> more verbose diff --git a/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-enhanced-result.yaml b/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-enhanced-result.yaml index 5914d58d690a1..055f7c67144eb 100644 --- a/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-enhanced-result.yaml +++ b/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-enhanced-result.yaml @@ -88,7 +88,7 @@ exporters: peer_service_aggregation: false peer_tags: [] peer_tags_aggregation: false - span_name_as_resource_name: true + span_name_as_resource_name: false span_name_remappings: {} trace_buffer: 0 write_buffer_size: 0 diff --git a/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-provided-result.yaml b/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-provided-result.yaml index 807d80682416a..d0b9a8b4b3aa8 100644 --- a/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-provided-result.yaml +++ b/comp/otelcol/ddflareextension/impl/testdata/simple-dd/config-provided-result.yaml @@ -88,7 +88,7 @@ exporters: peer_service_aggregation: false peer_tags: [] peer_tags_aggregation: false - span_name_as_resource_name: true + span_name_as_resource_name: false span_name_remappings: {} trace_buffer: 0 write_buffer_size: 0 diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/factory.go b/comp/otelcol/otlp/components/exporter/datadogexporter/factory.go index 35cb0957daada..b32933f255ba3 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/factory.go +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/factory.go @@ -119,7 +119,6 @@ func NewFactory( func CreateDefaultConfig() component.Config { ddcfg := datadogconfig.CreateDefaultConfig().(*datadogconfig.Config) ddcfg.Traces.TracesConfig.ComputeTopLevelBySpanKind = true - ddcfg.Traces.TracesConfig.SpanNameAsResourceName = true ddcfg.Logs.Endpoint = "https://agent-http-intake.logs.datadoghq.com" ddcfg.HostMetadata.Enabled = false return ddcfg From 8d8a340480a07fe7b6d00c71c60d2337fe5d72d3 Mon Sep 17 00:00:00 2001 From: NouemanKHAL Date: Tue, 17 Dec 2024 01:35:51 +0700 Subject: [PATCH 40/78] bump github.com/shirou/gopsutil to v4 in corechecks (#32225) --- LICENSE-3rdparty.csv | 10 ---------- go.mod | 2 -- pkg/collector/corechecks/net/network/network.go | 2 +- pkg/collector/corechecks/net/network/network_test.go | 2 +- pkg/collector/corechecks/system/cpu/cpu/cpu.go | 2 +- pkg/collector/corechecks/system/cpu/cpu/cpu_test.go | 2 +- pkg/collector/corechecks/system/cpu/load/load.go | 4 ++-- pkg/collector/corechecks/system/cpu/load/load_test.go | 4 ++-- pkg/collector/corechecks/system/disk/disk/disk_nix.go | 2 +- pkg/collector/corechecks/system/disk/disk/disk_test.go | 2 +- pkg/collector/corechecks/system/disk/io/iostats.go | 2 +- pkg/collector/corechecks/system/disk/io/iostats_nix.go | 4 ++-- .../corechecks/system/disk/io/iostats_nix_test.go | 2 +- .../corechecks/system/disk/io/iostats_test.go | 4 ++-- pkg/collector/corechecks/system/memory/memory_nix.go | 2 +- .../corechecks/system/memory/memory_nix_test.go | 2 +- pkg/collector/corechecks/system/uptime/uptime_nix.go | 2 +- 17 files changed, 19 insertions(+), 31 deletions(-) diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index d424a9ed015c7..502d348bb5ab9 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -2043,15 +2043,6 @@ core,github.com/secure-systems-lab/go-securesystemslib/dsse,MIT,Copyright (c) 20 core,github.com/secure-systems-lab/go-securesystemslib/signerverifier,MIT,Copyright (c) 2021 NYU Secure Systems Lab core,github.com/sergi/go-diff/diffmatchpatch,MIT,Copyright (c) 2012-2016 The go-diff Authors. All rights reserved | Danny Yoo | James Kolb | Jonathan Amsterdam | Markus Zimmermann | Matt Kovars | Osman Masood | Robert Carlsen | Rory Flynn | Sergi Mansilla | Shatrugna Sadhu | Shawn Smith | Stas Maksimov | Tor Arvid Lund | Zac Bergquist | Örjan Persson core,github.com/shibumi/go-pathspec,Apache-2.0,Copyright (c) 2012 The Go Authors. All rights reserved. -core,github.com/shirou/gopsutil/v3/common,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/cpu,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/disk,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/host,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/internal/common,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/load,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/mem,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/net,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" -core,github.com/shirou/gopsutil/v3/process,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" core,github.com/shirou/gopsutil/v4/common,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" core,github.com/shirou/gopsutil/v4/cpu,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" core,github.com/shirou/gopsutil/v4/disk,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" @@ -2062,7 +2053,6 @@ core,github.com/shirou/gopsutil/v4/mem,BSD-3-Clause,"Copyright (c) 2009 The Go A core,github.com/shirou/gopsutil/v4/net,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" core,github.com/shirou/gopsutil/v4/process,BSD-3-Clause,"Copyright (c) 2009 The Go Authors. All rights reserved | Copyright (c) 2014, WAKAYAMA Shirou" core,github.com/shirou/w32,BSD-3-Clause,Copyright (c) 2010-2012 The w32 Authors. All rights reserved. -core,github.com/shoenig/go-m1cpu,MPL-2.0,"copyright doctrines of fair use, fair dealing, or other" core,github.com/shopspring/decimal,MIT,"Copyright (c) 2013 Oguz Bilgic | Copyright (c) 2015 Spring, Inc" core,github.com/signalfx/sapm-proto/client,Apache-2.0,"Copyright 2019 Splunk, Inc." core,github.com/signalfx/sapm-proto/gen,Apache-2.0,"Copyright 2019 Splunk, Inc." diff --git a/go.mod b/go.mod index 0a6de46e14ef7..468318882d2f0 100644 --- a/go.mod +++ b/go.mod @@ -262,7 +262,6 @@ require ( github.com/robfig/cron/v3 v3.0.1 github.com/samber/lo v1.47.0 github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da - github.com/shirou/gopsutil/v3 v3.24.5 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 @@ -974,7 +973,6 @@ require ( github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect github.com/scaleway/scaleway-sdk-go v1.0.0-beta.29 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/signalfx/sapm-proto v0.17.0 // indirect github.com/sigstore/rekor v1.2.2 // indirect github.com/skeema/knownhosts v1.2.2 // indirect diff --git a/pkg/collector/corechecks/net/network/network.go b/pkg/collector/corechecks/net/network/network.go index c3505185203aa..d4a1e5cd6ebe7 100644 --- a/pkg/collector/corechecks/net/network/network.go +++ b/pkg/collector/corechecks/net/network/network.go @@ -17,7 +17,7 @@ import ( "strconv" "strings" - "github.com/shirou/gopsutil/v3/net" + "github.com/shirou/gopsutil/v4/net" yaml "gopkg.in/yaml.v2" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" diff --git a/pkg/collector/corechecks/net/network/network_test.go b/pkg/collector/corechecks/net/network/network_test.go index b2476ca3d734c..4a033f97dbe86 100644 --- a/pkg/collector/corechecks/net/network/network_test.go +++ b/pkg/collector/corechecks/net/network/network_test.go @@ -10,7 +10,7 @@ package network import ( "testing" - "github.com/shirou/gopsutil/v3/net" + "github.com/shirou/gopsutil/v4/net" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/pkg/collector/corechecks/system/cpu/cpu/cpu.go b/pkg/collector/corechecks/system/cpu/cpu/cpu.go index 8ca44f462bd61..a098b35a39647 100644 --- a/pkg/collector/corechecks/system/cpu/cpu/cpu.go +++ b/pkg/collector/corechecks/system/cpu/cpu/cpu.go @@ -10,7 +10,7 @@ package cpu import ( "fmt" - "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v4/cpu" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" diff --git a/pkg/collector/corechecks/system/cpu/cpu/cpu_test.go b/pkg/collector/corechecks/system/cpu/cpu/cpu_test.go index d5124637d6c4a..43a243f36d10d 100644 --- a/pkg/collector/corechecks/system/cpu/cpu/cpu_test.go +++ b/pkg/collector/corechecks/system/cpu/cpu/cpu_test.go @@ -20,7 +20,7 @@ import ( pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" "github.com/DataDog/datadog-agent/pkg/metrics" - "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v4/cpu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/pkg/collector/corechecks/system/cpu/load/load.go b/pkg/collector/corechecks/system/cpu/load/load.go index 6d6aa1833dedb..52be3ea30e08a 100644 --- a/pkg/collector/corechecks/system/cpu/load/load.go +++ b/pkg/collector/corechecks/system/cpu/load/load.go @@ -10,8 +10,8 @@ package load import ( "fmt" - "github.com/shirou/gopsutil/v3/cpu" - "github.com/shirou/gopsutil/v3/load" + "github.com/shirou/gopsutil/v4/cpu" + "github.com/shirou/gopsutil/v4/load" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" diff --git a/pkg/collector/corechecks/system/cpu/load/load_test.go b/pkg/collector/corechecks/system/cpu/load/load_test.go index af820e8fc21fa..fd140edd86ffd 100644 --- a/pkg/collector/corechecks/system/cpu/load/load_test.go +++ b/pkg/collector/corechecks/system/cpu/load/load_test.go @@ -9,9 +9,9 @@ package load import ( "testing" - "github.com/shirou/gopsutil/v3/load" + "github.com/shirou/gopsutil/v4/load" - "github.com/shirou/gopsutil/v3/cpu" + "github.com/shirou/gopsutil/v4/cpu" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" diff --git a/pkg/collector/corechecks/system/disk/disk/disk_nix.go b/pkg/collector/corechecks/system/disk/disk/disk_nix.go index 23a03601724a1..7a384f377056c 100644 --- a/pkg/collector/corechecks/system/disk/disk/disk_nix.go +++ b/pkg/collector/corechecks/system/disk/disk/disk_nix.go @@ -11,7 +11,7 @@ import ( "fmt" "path/filepath" - "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v4/disk" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" diff --git a/pkg/collector/corechecks/system/disk/disk/disk_test.go b/pkg/collector/corechecks/system/disk/disk/disk_test.go index 76aae7d0cfc36..2934b7fe9ed11 100644 --- a/pkg/collector/corechecks/system/disk/disk/disk_test.go +++ b/pkg/collector/corechecks/system/disk/disk/disk_test.go @@ -10,7 +10,7 @@ import ( "regexp" "testing" - "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v4/disk" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/mocksender" diff --git a/pkg/collector/corechecks/system/disk/io/iostats.go b/pkg/collector/corechecks/system/disk/io/iostats.go index 2cdf7647bf14a..b40315ebff0af 100644 --- a/pkg/collector/corechecks/system/disk/io/iostats.go +++ b/pkg/collector/corechecks/system/disk/io/iostats.go @@ -20,7 +20,7 @@ import ( ) const ( - // SectorSize is used here to substitute non-exporeted from github.com/shirou/gopsutil/v3/disk package constant named "sectorSize" + // SectorSize is used here to substitute non-exporeted from github.com/shirou/gopsutil/v4/disk package constant named "sectorSize" SectorSize = 512 kB = (1 << 10) CheckName = "io" diff --git a/pkg/collector/corechecks/system/disk/io/iostats_nix.go b/pkg/collector/corechecks/system/disk/io/iostats_nix.go index 8fe5b1e66da5e..e3813f162da9e 100644 --- a/pkg/collector/corechecks/system/disk/io/iostats_nix.go +++ b/pkg/collector/corechecks/system/disk/io/iostats_nix.go @@ -13,8 +13,8 @@ import ( "regexp" "time" - "github.com/shirou/gopsutil/v3/disk" - "github.com/shirou/gopsutil/v3/mem" // for system.io.block_{in,out} + "github.com/shirou/gopsutil/v4/disk" + "github.com/shirou/gopsutil/v4/mem" // for system.io.block_{in,out} "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator/sender" diff --git a/pkg/collector/corechecks/system/disk/io/iostats_nix_test.go b/pkg/collector/corechecks/system/disk/io/iostats_nix_test.go index 21bfbef4109da..61a60ccab654d 100644 --- a/pkg/collector/corechecks/system/disk/io/iostats_nix_test.go +++ b/pkg/collector/corechecks/system/disk/io/iostats_nix_test.go @@ -12,7 +12,7 @@ import ( "testing" "time" - "github.com/shirou/gopsutil/v3/disk" + "github.com/shirou/gopsutil/v4/disk" "github.com/stretchr/testify/assert" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" diff --git a/pkg/collector/corechecks/system/disk/io/iostats_test.go b/pkg/collector/corechecks/system/disk/io/iostats_test.go index 005b4aad3ebda..bb7d3eaef18b7 100644 --- a/pkg/collector/corechecks/system/disk/io/iostats_test.go +++ b/pkg/collector/corechecks/system/disk/io/iostats_test.go @@ -12,8 +12,8 @@ import ( "testing" "time" - "github.com/shirou/gopsutil/v3/disk" - "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v4/disk" + "github.com/shirou/gopsutil/v4/mem" "github.com/DataDog/datadog-agent/comp/core/autodiscovery/integration" "github.com/DataDog/datadog-agent/pkg/aggregator" diff --git a/pkg/collector/corechecks/system/memory/memory_nix.go b/pkg/collector/corechecks/system/memory/memory_nix.go index 190a4c7e02b41..577fb222bb97b 100644 --- a/pkg/collector/corechecks/system/memory/memory_nix.go +++ b/pkg/collector/corechecks/system/memory/memory_nix.go @@ -11,7 +11,7 @@ import ( "fmt" "runtime" - "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v4/mem" "github.com/DataDog/datadog-agent/pkg/util/log" diff --git a/pkg/collector/corechecks/system/memory/memory_nix_test.go b/pkg/collector/corechecks/system/memory/memory_nix_test.go index b7cfd2e55a3f9..e8be54dbb93a9 100644 --- a/pkg/collector/corechecks/system/memory/memory_nix_test.go +++ b/pkg/collector/corechecks/system/memory/memory_nix_test.go @@ -11,7 +11,7 @@ import ( "fmt" "testing" - "github.com/shirou/gopsutil/v3/mem" + "github.com/shirou/gopsutil/v4/mem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/collector/corechecks/system/uptime/uptime_nix.go b/pkg/collector/corechecks/system/uptime/uptime_nix.go index cc121ac42de61..41c8421153e3b 100644 --- a/pkg/collector/corechecks/system/uptime/uptime_nix.go +++ b/pkg/collector/corechecks/system/uptime/uptime_nix.go @@ -7,7 +7,7 @@ package uptime import ( - "github.com/shirou/gopsutil/v3/host" + "github.com/shirou/gopsutil/v4/host" ) // For testing purpose From 7b74a22d9fb07d4b2834add5db0d0a0f9446a6db Mon Sep 17 00:00:00 2001 From: Maxime Riaud <65339037+misteriaud@users.noreply.github.com> Date: Mon, 16 Dec 2024 19:47:06 +0100 Subject: [PATCH 41/78] [ASCII-2585] Migrating ProcessAgent to use IPC cert (#31844) --- cmd/process-agent/subcommands/status/status.go | 3 +-- comp/process/apiserver/apiserver.go | 4 ++++ comp/process/apiserver/apiserver_test.go | 2 ++ comp/process/bundle_test.go | 2 ++ 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/cmd/process-agent/subcommands/status/status.go b/cmd/process-agent/subcommands/status/status.go index 2660ee8fb71b4..8826c7547fb61 100644 --- a/cmd/process-agent/subcommands/status/status.go +++ b/cmd/process-agent/subcommands/status/status.go @@ -28,8 +28,6 @@ import ( "github.com/DataDog/datadog-agent/pkg/util/fxutil" ) -var httpClient = apiutil.GetClient(false) - const ( notRunning = ` ============= @@ -111,6 +109,7 @@ func writeError(log log.Component, w io.Writer, e error) { } func fetchStatus(statusURL string) ([]byte, error) { + httpClient := apiutil.GetClient(false) body, err := apiutil.DoGet(httpClient, statusURL, apiutil.LeaveConnectionOpen) if err != nil { return nil, status.NewConnectionError(err) diff --git a/comp/process/apiserver/apiserver.go b/comp/process/apiserver/apiserver.go index c216b2a93fef4..24bbe6353d06f 100644 --- a/comp/process/apiserver/apiserver.go +++ b/comp/process/apiserver/apiserver.go @@ -15,6 +15,7 @@ import ( "go.uber.org/fx" "github.com/DataDog/datadog-agent/cmd/process-agent/api" + "github.com/DataDog/datadog-agent/comp/api/authtoken" log "github.com/DataDog/datadog-agent/comp/core/log/def" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" ) @@ -32,6 +33,8 @@ type dependencies struct { Log log.Component + At authtoken.Component + APIServerDeps api.APIServerDeps } @@ -54,6 +57,7 @@ func newApiServer(deps dependencies) Component { ReadTimeout: timeout, WriteTimeout: timeout, IdleTimeout: timeout, + TLSConfig: deps.At.GetTLSServerConfig(), }, } diff --git a/comp/process/apiserver/apiserver_test.go b/comp/process/apiserver/apiserver_test.go index c9b0f4b96e92d..a0d94db4db4c2 100644 --- a/comp/process/apiserver/apiserver_test.go +++ b/comp/process/apiserver/apiserver_test.go @@ -13,6 +13,7 @@ import ( "github.com/stretchr/testify/assert" "go.uber.org/fx" + "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/settings/settingsimpl" "github.com/DataDog/datadog-agent/comp/core/status" @@ -39,6 +40,7 @@ func TestLifecycle(t *testing.T) { }), statusimpl.Module(), settingsimpl.MockModule(), + fetchonlyimpl.MockModule(), )) assert.Eventually(t, func() bool { diff --git a/comp/process/bundle_test.go b/comp/process/bundle_test.go index 591dce9f63cfb..9fb4131e013c0 100644 --- a/comp/process/bundle_test.go +++ b/comp/process/bundle_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/fx" + "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" configComp "github.com/DataDog/datadog-agent/comp/core/config" log "github.com/DataDog/datadog-agent/comp/core/log/def" @@ -60,6 +61,7 @@ func TestBundleDependencies(t *testing.T) { rdnsquerier.MockModule(), npcollectorimpl.MockModule(), statsd.MockModule(), + fetchonlyimpl.MockModule(), ) } From 452a153abe3cac69e175a08f18add4a103051036 Mon Sep 17 00:00:00 2001 From: Maxime Riaud <65339037+misteriaud@users.noreply.github.com> Date: Mon, 16 Dec 2024 20:31:04 +0100 Subject: [PATCH 42/78] [ASCII-2597] use contant-time compare for grpc AuthInterceptor callbacks (#32246) --- cmd/cluster-agent/api/server.go | 3 ++- comp/api/api/apiimpl/security.go | 3 ++- pkg/util/grpc/auth.go | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/cluster-agent/api/server.go b/cmd/cluster-agent/api/server.go index 37efad4d900dd..c4f5c0534633a 100644 --- a/cmd/cluster-agent/api/server.go +++ b/cmd/cluster-agent/api/server.go @@ -12,6 +12,7 @@ package api import ( "context" + "crypto/subtle" "crypto/tls" "crypto/x509" "encoding/pem" @@ -116,7 +117,7 @@ func StartServer(ctx context.Context, w workloadmeta.Component, taggerComp tagge logWriter, _ := pkglogsetup.NewTLSHandshakeErrorWriter(4, log.WarnLvl) authInterceptor := grpcutil.AuthInterceptor(func(token string) (interface{}, error) { - if token != util.GetDCAAuthToken() { + if subtle.ConstantTimeCompare([]byte(token), []byte(util.GetDCAAuthToken())) == 0 { return struct{}{}, errors.New("Invalid session token") } diff --git a/comp/api/api/apiimpl/security.go b/comp/api/api/apiimpl/security.go index 4587871aecec3..f7176bbfbf99c 100644 --- a/comp/api/api/apiimpl/security.go +++ b/comp/api/api/apiimpl/security.go @@ -6,6 +6,7 @@ package apiimpl import ( + "crypto/subtle" "crypto/tls" "crypto/x509" "encoding/pem" @@ -34,7 +35,7 @@ func validateToken(next http.Handler) http.Handler { // parseToken parses the token and validate it for our gRPC API, it returns an empty // struct and an error or nil func parseToken(token string) (interface{}, error) { - if token != util.GetAuthToken() { + if subtle.ConstantTimeCompare([]byte(token), []byte(util.GetAuthToken())) == 0 { return struct{}{}, errors.New("Invalid session token") } diff --git a/pkg/util/grpc/auth.go b/pkg/util/grpc/auth.go index 382c4838919a6..fae83d5c22e8d 100644 --- a/pkg/util/grpc/auth.go +++ b/pkg/util/grpc/auth.go @@ -7,6 +7,7 @@ package grpc import ( "context" + "crypto/subtle" "errors" "fmt" @@ -45,7 +46,7 @@ func AuthInterceptor(verifier verifierFunc) grpc_auth.AuthFunc { // using the given token. func StaticAuthInterceptor(token string) grpc_auth.AuthFunc { return AuthInterceptor(func(reqToken string) (interface{}, error) { - if reqToken != token { + if subtle.ConstantTimeCompare([]byte(reqToken), []byte(token)) == 0 { return struct{}{}, errors.New("invalid session token") } From 1e3d0a9e0c7e8dfa74129243154b79062f655b6a Mon Sep 17 00:00:00 2001 From: Maxime Riaud <65339037+misteriaud@users.noreply.github.com> Date: Mon, 16 Dec 2024 21:16:53 +0100 Subject: [PATCH 43/78] [ASCII-2586] Migrating SecurityAgent to use IPC cert (#31845) --- cmd/security-agent/api/server.go | 48 ++++--------------- cmd/security-agent/main_windows.go | 6 ++- .../subcommands/start/command.go | 9 ++-- 3 files changed, 19 insertions(+), 44 deletions(-) diff --git a/cmd/security-agent/api/server.go b/cmd/security-agent/api/server.go index e776628e5ccd4..33c8ac033f55a 100644 --- a/cmd/security-agent/api/server.go +++ b/cmd/security-agent/api/server.go @@ -12,9 +12,6 @@ package api import ( "crypto/tls" - "crypto/x509" - "encoding/pem" - "fmt" stdLog "log" "net" "net/http" @@ -23,10 +20,10 @@ import ( "github.com/gorilla/mux" "github.com/DataDog/datadog-agent/cmd/security-agent/api/agent" + "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/core/settings" "github.com/DataDog/datadog-agent/comp/core/status" workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def" - "github.com/DataDog/datadog-agent/pkg/api/security" "github.com/DataDog/datadog-agent/pkg/api/util" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -35,19 +32,21 @@ import ( // Server implements security agent API server type Server struct { - listener net.Listener - agent *agent.Agent + listener net.Listener + agent *agent.Agent + tlsConfig *tls.Config } // NewServer creates a new Server instance -func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) (*Server, error) { +func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (*Server, error) { listener, err := newListener() if err != nil { return nil, err } return &Server{ - listener: listener, - agent: agent.NewAgent(statusComponent, settings, wmeta), + listener: listener, + agent: agent.NewAgent(statusComponent, settings, wmeta), + tlsConfig: at.GetTLSClientConfig(), }, nil } @@ -62,43 +61,16 @@ func (s *Server) Start() error { // Validate token for every request r.Use(validateToken) - err := util.CreateAndSetAuthToken(pkgconfigsetup.Datadog()) - if err != nil { - return err - } - - hosts := []string{"127.0.0.1", "localhost"} - _, rootCertPEM, rootKey, err := security.GenerateRootCert(hosts, 2048) - if err != nil { - return fmt.Errorf("unable to start TLS server") - } - - // PEM encode the private key - rootKeyPEM := pem.EncodeToMemory(&pem.Block{ - Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootKey), - }) - - // Create a TLS cert using the private key and certificate - rootTLSCert, err := tls.X509KeyPair(rootCertPEM, rootKeyPEM) - if err != nil { - return fmt.Errorf("invalid key pair: %v", err) - } - - tlsConfig := tls.Config{ - Certificates: []tls.Certificate{rootTLSCert}, - MinVersion: tls.VersionTLS13, - } - // Use a stack depth of 4 on top of the default one to get a relevant filename in the stdlib logWriter, _ := pkglogsetup.NewLogWriter(4, log.ErrorLvl) srv := &http.Server{ Handler: r, ErrorLog: stdLog.New(logWriter, "Error from the agent http API server: ", 0), // log errors to seelog, - TLSConfig: &tlsConfig, + TLSConfig: s.tlsConfig, WriteTimeout: pkgconfigsetup.Datadog().GetDuration("server_timeout") * time.Second, } - tlsListener := tls.NewListener(s.listener, &tlsConfig) + tlsListener := tls.NewListener(s.listener, s.tlsConfig) go srv.Serve(tlsListener) //nolint:errcheck return nil diff --git a/cmd/security-agent/main_windows.go b/cmd/security-agent/main_windows.go index b81223e6aebc1..a729e7a2003f0 100644 --- a/cmd/security-agent/main_windows.go +++ b/cmd/security-agent/main_windows.go @@ -25,6 +25,7 @@ import ( "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands/start" "github.com/DataDog/datadog-agent/comp/agent/autoexit" "github.com/DataDog/datadog-agent/comp/agent/autoexit/autoexitimpl" + "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" @@ -91,10 +92,11 @@ func (s *service) Run(svcctx context.Context) error { params := &cliParams{} err := fxutil.OneShot( func(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, - telemetry telemetry.Component, _ workloadmeta.Component, _ *cliParams, statusComponent status.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component) error { + telemetry telemetry.Component, _ workloadmeta.Component, _ *cliParams, statusComponent status.Component, _ autoexit.Component, + settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error { defer start.StopAgent(log) - err := start.RunAgent(log, config, telemetry, statusComponent, settings, wmeta) + err := start.RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at) if err != nil { if errors.Is(err, start.ErrAllComponentsDisabled) { // If all components are disabled, we should exit cleanly diff --git a/cmd/security-agent/subcommands/start/command.go b/cmd/security-agent/subcommands/start/command.go index 12a93fc4560ff..2a3dd883dcd84 100644 --- a/cmd/security-agent/subcommands/start/command.go +++ b/cmd/security-agent/subcommands/start/command.go @@ -29,6 +29,7 @@ import ( "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands/runtime" "github.com/DataDog/datadog-agent/comp/agent/autoexit" "github.com/DataDog/datadog-agent/comp/agent/autoexit/autoexitimpl" + "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" @@ -201,10 +202,10 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { // TODO(components): note how workloadmeta is passed anonymously, it is still required as it is used // as a global. This should eventually be fixed and all workloadmeta interactions should be via the // injected instance. -func start(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component) error { +func start(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error { defer StopAgent(log) - err := RunAgent(log, config, telemetry, statusComponent, settings, wmeta) + err := RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at) if errors.Is(err, ErrAllComponentsDisabled) || errors.Is(err, errNoAPIKeyConfigured) { return nil } @@ -256,7 +257,7 @@ var ErrAllComponentsDisabled = errors.New("all security-agent component are disa var errNoAPIKeyConfigured = errors.New("no API key configured") // RunAgent initialized resources and starts API server -func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) (err error) { +func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (err error) { if err := coredump.Setup(config); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } @@ -299,7 +300,7 @@ func RunAgent(log log.Component, config config.Component, telemetry telemetry.Co } }() - srv, err = api.NewServer(statusComponent, settings, wmeta) + srv, err = api.NewServer(statusComponent, settings, wmeta, at) if err != nil { return log.Errorf("Error while creating api server, exiting: %v", err) } From 898efd46fa0034654361c3ed860826bcfde1fcbd Mon Sep 17 00:00:00 2001 From: Maxime Riaud <65339037+misteriaud@users.noreply.github.com> Date: Mon, 16 Dec 2024 22:03:58 +0100 Subject: [PATCH 44/78] bump github.com/hectane/go-acl (#32007) --- comp/api/authtoken/go.mod | 2 +- comp/api/authtoken/go.sum | 4 ++-- comp/core/config/go.mod | 2 +- comp/core/config/go.sum | 4 ++-- comp/core/log/impl-trace/go.mod | 2 +- comp/core/log/impl-trace/go.sum | 4 ++-- comp/core/log/impl/go.mod | 2 +- comp/core/log/impl/go.sum | 4 ++-- comp/core/log/mock/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/converter/impl/go.mod | 2 +- comp/otelcol/converter/impl/go.sum | 4 ++-- comp/otelcol/ddflareextension/impl/go.mod | 2 +- comp/otelcol/ddflareextension/impl/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/mock/go.mod | 2 +- pkg/config/mock/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/filesystem/go.mod | 2 +- pkg/util/filesystem/go.sum | 4 ++-- pkg/util/flavor/go.mod | 2 +- pkg/util/flavor/go.sum | 4 ++-- pkg/util/grpc/go.mod | 2 +- pkg/util/grpc/go.sum | 4 ++-- pkg/util/http/go.mod | 2 +- pkg/util/http/go.sum | 4 ++-- pkg/util/log/setup/go.mod | 2 +- pkg/util/log/setup/go.sum | 4 ++-- pkg/util/system/go.mod | 2 +- pkg/util/system/go.sum | 4 ++-- test/otel/go.mod | 2 +- test/otel/go.sum | 4 ++-- 87 files changed, 131 insertions(+), 131 deletions(-) diff --git a/comp/api/authtoken/go.mod b/comp/api/authtoken/go.mod index ca4e617286f9e..24081f4824780 100644 --- a/comp/api/authtoken/go.mod +++ b/comp/api/authtoken/go.mod @@ -84,7 +84,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/api/authtoken/go.sum b/comp/api/authtoken/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/comp/api/authtoken/go.sum +++ b/comp/api/authtoken/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/core/config/go.mod b/comp/core/config/go.mod index a8fc87fffecf0..00be19b3b9ba7 100644 --- a/comp/core/config/go.mod +++ b/comp/core/config/go.mod @@ -75,7 +75,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/core/config/go.sum b/comp/core/config/go.sum index 49f42d5d3e0a6..323c4fa804e83 100644 --- a/comp/core/config/go.sum +++ b/comp/core/config/go.sum @@ -88,8 +88,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/core/log/impl-trace/go.mod b/comp/core/log/impl-trace/go.mod index d78d19fde7f68..309b259ad3616 100644 --- a/comp/core/log/impl-trace/go.mod +++ b/comp/core/log/impl-trace/go.mod @@ -84,7 +84,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/core/log/impl-trace/go.sum b/comp/core/log/impl-trace/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/comp/core/log/impl-trace/go.sum +++ b/comp/core/log/impl-trace/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/core/log/impl/go.mod b/comp/core/log/impl/go.mod index 0d68073c13084..12d048a84bd4d 100644 --- a/comp/core/log/impl/go.mod +++ b/comp/core/log/impl/go.mod @@ -74,7 +74,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/core/log/impl/go.sum b/comp/core/log/impl/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/comp/core/log/impl/go.sum +++ b/comp/core/log/impl/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/core/log/mock/go.sum b/comp/core/log/mock/go.sum index 2ad11aee593c1..4b0aad4f95926 100644 --- a/comp/core/log/mock/go.sum +++ b/comp/core/log/mock/go.sum @@ -80,8 +80,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= diff --git a/comp/core/status/statusimpl/go.mod b/comp/core/status/statusimpl/go.mod index 9487310619057..c121259bfa141 100644 --- a/comp/core/status/statusimpl/go.mod +++ b/comp/core/status/statusimpl/go.mod @@ -88,7 +88,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/core/status/statusimpl/go.sum b/comp/core/status/statusimpl/go.sum index fafe0837efa7e..73fea28e04146 100644 --- a/comp/core/status/statusimpl/go.sum +++ b/comp/core/status/statusimpl/go.sum @@ -93,8 +93,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/forwarder/defaultforwarder/go.mod b/comp/forwarder/defaultforwarder/go.mod index a0891b10fda75..97eb61b033263 100644 --- a/comp/forwarder/defaultforwarder/go.mod +++ b/comp/forwarder/defaultforwarder/go.mod @@ -113,7 +113,7 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/comp/forwarder/defaultforwarder/go.sum b/comp/forwarder/defaultforwarder/go.sum index 2e4c8055f6ab0..735da3a7badda 100644 --- a/comp/forwarder/defaultforwarder/go.sum +++ b/comp/forwarder/defaultforwarder/go.sum @@ -97,8 +97,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.mod b/comp/forwarder/orchestrator/orchestratorinterface/go.mod index 3022f485e02a3..7c55fcf139cf0 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.mod +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.mod @@ -117,7 +117,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/comp/forwarder/orchestrator/orchestratorinterface/go.sum b/comp/forwarder/orchestrator/orchestratorinterface/go.sum index 68e574e5c8151..c23eb9a608463 100644 --- a/comp/forwarder/orchestrator/orchestratorinterface/go.sum +++ b/comp/forwarder/orchestrator/orchestratorinterface/go.sum @@ -100,8 +100,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/logs/agent/config/go.mod b/comp/logs/agent/config/go.mod index 5e99aa77604f0..1ae81a7be7da6 100644 --- a/comp/logs/agent/config/go.mod +++ b/comp/logs/agent/config/go.mod @@ -76,7 +76,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/logs/agent/config/go.sum b/comp/logs/agent/config/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/comp/logs/agent/config/go.sum +++ b/comp/logs/agent/config/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/converter/impl/go.mod b/comp/otelcol/converter/impl/go.mod index b3bae2ce5b62b..ec9c75f1a3c68 100644 --- a/comp/otelcol/converter/impl/go.mod +++ b/comp/otelcol/converter/impl/go.mod @@ -86,7 +86,7 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect diff --git a/comp/otelcol/converter/impl/go.sum b/comp/otelcol/converter/impl/go.sum index a98a26b75f480..4239a8ba70c9d 100644 --- a/comp/otelcol/converter/impl/go.sum +++ b/comp/otelcol/converter/impl/go.sum @@ -89,8 +89,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/ddflareextension/impl/go.mod b/comp/otelcol/ddflareextension/impl/go.mod index 0719700f9222a..7af5d796d92fb 100644 --- a/comp/otelcol/ddflareextension/impl/go.mod +++ b/comp/otelcol/ddflareextension/impl/go.mod @@ -358,7 +358,7 @@ require ( github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/hetznercloud/hcloud-go/v2 v2.10.2 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/imdario/mergo v0.3.16 // indirect diff --git a/comp/otelcol/ddflareextension/impl/go.sum b/comp/otelcol/ddflareextension/impl/go.sum index 7a6bcb0649c64..d4f8e67e79da5 100644 --- a/comp/otelcol/ddflareextension/impl/go.sum +++ b/comp/otelcol/ddflareextension/impl/go.sum @@ -443,8 +443,8 @@ github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 h1:fgVfQ4AC1av github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/hetznercloud/hcloud-go/v2 v2.10.2 h1:9gyTUPhfNbfbS40Spgij5mV5k37bOZgt8iHKCbfGs5I= github.com/hetznercloud/hcloud-go/v2 v2.10.2/go.mod h1:xQ+8KhIS62W0D78Dpi57jsufWh844gUw1az5OUvaeq8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= diff --git a/comp/otelcol/logsagentpipeline/go.mod b/comp/otelcol/logsagentpipeline/go.mod index fd1925f5a1a1d..2a575bf145e7e 100644 --- a/comp/otelcol/logsagentpipeline/go.mod +++ b/comp/otelcol/logsagentpipeline/go.mod @@ -121,7 +121,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/comp/otelcol/logsagentpipeline/go.sum b/comp/otelcol/logsagentpipeline/go.sum index 9061e9b89a5c6..b0f7d9ee40dfd 100644 --- a/comp/otelcol/logsagentpipeline/go.sum +++ b/comp/otelcol/logsagentpipeline/go.sum @@ -99,8 +99,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod index 6bce0b3c962d4..711aa095cc468 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.mod @@ -136,7 +136,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum index 9061e9b89a5c6..b0f7d9ee40dfd 100644 --- a/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum +++ b/comp/otelcol/logsagentpipeline/logsagentpipelineimpl/go.sum @@ -99,8 +99,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod index 5023166f00df5..c3e28a412d2e6 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.mod @@ -239,7 +239,7 @@ require ( github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect diff --git a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum index 5107430987dd3..e9c507fbafa8a 100644 --- a/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/datadogexporter/go.sum @@ -175,8 +175,8 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod index 23ddd3225d779..80cffa6a338c5 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.mod @@ -110,7 +110,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum index 85a19bf8e57b5..c50808e9a40cd 100644 --- a/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/logsagentexporter/go.sum @@ -121,8 +121,8 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod index 3ed3c5a02c175..1f8b9e2be8d1d 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.mod @@ -161,7 +161,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect diff --git a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum index b0e60957170e6..a33f1cc34269d 100644 --- a/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum +++ b/comp/otelcol/otlp/components/exporter/serializerexporter/go.sum @@ -139,8 +139,8 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/otelcol/otlp/testutil/go.mod b/comp/otelcol/otlp/testutil/go.mod index 58832aca7482e..d51e4d63d2b36 100644 --- a/comp/otelcol/otlp/testutil/go.mod +++ b/comp/otelcol/otlp/testutil/go.mod @@ -72,7 +72,7 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/otelcol/otlp/testutil/go.sum b/comp/otelcol/otlp/testutil/go.sum index 22f79264c82e8..0778f221b9bca 100644 --- a/comp/otelcol/otlp/testutil/go.sum +++ b/comp/otelcol/otlp/testutil/go.sum @@ -98,8 +98,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/comp/serializer/compression/go.mod b/comp/serializer/compression/go.mod index a46ee59b26ae6..f1c59cd40d645 100644 --- a/comp/serializer/compression/go.mod +++ b/comp/serializer/compression/go.mod @@ -71,7 +71,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/comp/serializer/compression/go.sum b/comp/serializer/compression/go.sum index a3f8c86e6fc2e..3b405d22ebcc8 100644 --- a/comp/serializer/compression/go.sum +++ b/comp/serializer/compression/go.sum @@ -89,8 +89,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/go.mod b/go.mod index 468318882d2f0..9f95055c7d7b6 100644 --- a/go.mod +++ b/go.mod @@ -227,7 +227,7 @@ require ( github.com/hashicorp/consul/api v1.30.0 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/golang-lru/v2 v2.0.7 - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/iceber/iouring-go v0.0.0-20230403020409-002cfd2e2a90 github.com/imdario/mergo v0.3.16 github.com/invopop/jsonschema v0.12.0 diff --git a/go.sum b/go.sum index 5fd04067f4905..038a1a011d963 100644 --- a/go.sum +++ b/go.sum @@ -971,8 +971,8 @@ github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 h1:fgVfQ4AC1av github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtxn6QnrQ69P23VvIWMR34tg3vmwLz4UdUzm1dSCgE= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/hetznercloud/hcloud-go/v2 v2.10.2 h1:9gyTUPhfNbfbS40Spgij5mV5k37bOZgt8iHKCbfGs5I= github.com/hetznercloud/hcloud-go/v2 v2.10.2/go.mod h1:xQ+8KhIS62W0D78Dpi57jsufWh844gUw1az5OUvaeq8= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= diff --git a/pkg/api/go.mod b/pkg/api/go.mod index c3499a2fb94c2..ab50796cf7a58 100644 --- a/pkg/api/go.mod +++ b/pkg/api/go.mod @@ -76,7 +76,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/pkg/api/go.sum b/pkg/api/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/pkg/api/go.sum +++ b/pkg/api/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/config/env/go.mod b/pkg/config/env/go.mod index 637c78a705df1..7b16228030c49 100644 --- a/pkg/config/env/go.mod +++ b/pkg/config/env/go.mod @@ -31,7 +31,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect diff --git a/pkg/config/env/go.sum b/pkg/config/env/go.sum index d7d4a9a3701e0..4a2c4e5bdc610 100644 --- a/pkg/config/env/go.sum +++ b/pkg/config/env/go.sum @@ -81,8 +81,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= diff --git a/pkg/config/mock/go.mod b/pkg/config/mock/go.mod index 48f1f452fa096..f4edf2bbb8c8e 100644 --- a/pkg/config/mock/go.mod +++ b/pkg/config/mock/go.mod @@ -61,7 +61,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/config/mock/go.sum b/pkg/config/mock/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/config/mock/go.sum +++ b/pkg/config/mock/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/config/remote/go.mod b/pkg/config/remote/go.mod index 282d7f34ff86f..a1c4a4ebbc561 100644 --- a/pkg/config/remote/go.mod +++ b/pkg/config/remote/go.mod @@ -100,7 +100,7 @@ require ( github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect diff --git a/pkg/config/remote/go.sum b/pkg/config/remote/go.sum index 84cc570d9ad84..ca537b0d1660e 100644 --- a/pkg/config/remote/go.sum +++ b/pkg/config/remote/go.sum @@ -149,8 +149,8 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/config/setup/go.mod b/pkg/config/setup/go.mod index b7c9ff41de81b..efed57c318b83 100644 --- a/pkg/config/setup/go.mod +++ b/pkg/config/setup/go.mod @@ -73,7 +73,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/pkg/config/setup/go.sum b/pkg/config/setup/go.sum index 7f15a6d221890..eb1a58f09f4b8 100644 --- a/pkg/config/setup/go.sum +++ b/pkg/config/setup/go.sum @@ -91,8 +91,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/config/utils/go.mod b/pkg/config/utils/go.mod index 5fc90b2da63fd..ef80f1fde725b 100644 --- a/pkg/config/utils/go.mod +++ b/pkg/config/utils/go.mod @@ -66,7 +66,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/config/utils/go.sum b/pkg/config/utils/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/config/utils/go.sum +++ b/pkg/config/utils/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/auditor/go.mod b/pkg/logs/auditor/go.mod index 3fc9bf65dfa46..f9543ab0dda08 100644 --- a/pkg/logs/auditor/go.mod +++ b/pkg/logs/auditor/go.mod @@ -80,7 +80,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/logs/auditor/go.sum b/pkg/logs/auditor/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/logs/auditor/go.sum +++ b/pkg/logs/auditor/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/client/go.mod b/pkg/logs/client/go.mod index 093922b236bf3..49dad213dc24f 100644 --- a/pkg/logs/client/go.mod +++ b/pkg/logs/client/go.mod @@ -100,7 +100,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/logs/client/go.sum b/pkg/logs/client/go.sum index 1140bccbba9c9..5f7384266b821 100644 --- a/pkg/logs/client/go.sum +++ b/pkg/logs/client/go.sum @@ -88,8 +88,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/diagnostic/go.mod b/pkg/logs/diagnostic/go.mod index 65fb4af75f769..690eb4c56105e 100644 --- a/pkg/logs/diagnostic/go.mod +++ b/pkg/logs/diagnostic/go.mod @@ -84,7 +84,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect diff --git a/pkg/logs/diagnostic/go.sum b/pkg/logs/diagnostic/go.sum index 9694f5d5e2355..dd16364891695 100644 --- a/pkg/logs/diagnostic/go.sum +++ b/pkg/logs/diagnostic/go.sum @@ -87,8 +87,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/message/go.mod b/pkg/logs/message/go.mod index 716c4a57de92f..93c9793bafaf5 100644 --- a/pkg/logs/message/go.mod +++ b/pkg/logs/message/go.mod @@ -76,7 +76,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/logs/message/go.sum b/pkg/logs/message/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/logs/message/go.sum +++ b/pkg/logs/message/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/pipeline/go.mod b/pkg/logs/pipeline/go.mod index b2cca0985481e..10ad9ab1983b5 100644 --- a/pkg/logs/pipeline/go.mod +++ b/pkg/logs/pipeline/go.mod @@ -120,7 +120,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/logs/pipeline/go.sum b/pkg/logs/pipeline/go.sum index 9061e9b89a5c6..b0f7d9ee40dfd 100644 --- a/pkg/logs/pipeline/go.sum +++ b/pkg/logs/pipeline/go.sum @@ -99,8 +99,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/processor/go.mod b/pkg/logs/processor/go.mod index cc9458ea710c0..b98eacd627e97 100644 --- a/pkg/logs/processor/go.mod +++ b/pkg/logs/processor/go.mod @@ -99,7 +99,7 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/logs/processor/go.sum b/pkg/logs/processor/go.sum index f97a65425513e..1fb7bbe76cabd 100644 --- a/pkg/logs/processor/go.sum +++ b/pkg/logs/processor/go.sum @@ -94,8 +94,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/sds/go.mod b/pkg/logs/sds/go.mod index 33c40479ed13f..f5818700af57b 100644 --- a/pkg/logs/sds/go.mod +++ b/pkg/logs/sds/go.mod @@ -93,7 +93,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/logs/sds/go.sum b/pkg/logs/sds/go.sum index a5a699a22e560..c9341f0f48498 100644 --- a/pkg/logs/sds/go.sum +++ b/pkg/logs/sds/go.sum @@ -88,8 +88,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/sender/go.mod b/pkg/logs/sender/go.mod index 5a77fb5bb761d..437b2592b6e94 100644 --- a/pkg/logs/sender/go.mod +++ b/pkg/logs/sender/go.mod @@ -100,7 +100,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/logs/sender/go.sum b/pkg/logs/sender/go.sum index 1140bccbba9c9..5f7384266b821 100644 --- a/pkg/logs/sender/go.sum +++ b/pkg/logs/sender/go.sum @@ -88,8 +88,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/sources/go.mod b/pkg/logs/sources/go.mod index a5ff5d7147d72..47c016d543c91 100644 --- a/pkg/logs/sources/go.mod +++ b/pkg/logs/sources/go.mod @@ -74,7 +74,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/logs/sources/go.sum b/pkg/logs/sources/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/logs/sources/go.sum +++ b/pkg/logs/sources/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/logs/util/testutils/go.mod b/pkg/logs/util/testutils/go.mod index ebf193c2c90a2..46e01043de4bf 100644 --- a/pkg/logs/util/testutils/go.mod +++ b/pkg/logs/util/testutils/go.mod @@ -74,7 +74,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/logs/util/testutils/go.sum b/pkg/logs/util/testutils/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/logs/util/testutils/go.sum +++ b/pkg/logs/util/testutils/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/metrics/go.mod b/pkg/metrics/go.mod index a7369cfd728f6..66e73954c2885 100644 --- a/pkg/metrics/go.mod +++ b/pkg/metrics/go.mod @@ -85,7 +85,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/metrics/go.sum b/pkg/metrics/go.sum index cc70504832885..31112092709e6 100644 --- a/pkg/metrics/go.sum +++ b/pkg/metrics/go.sum @@ -96,8 +96,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/serializer/go.mod b/pkg/serializer/go.mod index 62d3ab7c2450c..ef22edcd67b2d 100644 --- a/pkg/serializer/go.mod +++ b/pkg/serializer/go.mod @@ -140,7 +140,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/klauspost/compress v1.17.11 // indirect diff --git a/pkg/serializer/go.sum b/pkg/serializer/go.sum index 240d357b106d4..0e4568db35dfe 100644 --- a/pkg/serializer/go.sum +++ b/pkg/serializer/go.sum @@ -118,8 +118,8 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/util/filesystem/go.mod b/pkg/util/filesystem/go.mod index 1a0effc2f07e8..292bb06f164b9 100644 --- a/pkg/util/filesystem/go.mod +++ b/pkg/util/filesystem/go.mod @@ -12,7 +12,7 @@ replace ( require ( github.com/DataDog/datadog-agent/pkg/util/log v0.59.1 github.com/DataDog/datadog-agent/pkg/util/winutil v0.59.1 - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb github.com/shirou/gopsutil/v4 v4.24.11 github.com/stretchr/testify v1.10.0 golang.org/x/sys v0.28.0 diff --git a/pkg/util/filesystem/go.sum b/pkg/util/filesystem/go.sum index 97cab95a8f162..14cc56a3421e8 100644 --- a/pkg/util/filesystem/go.sum +++ b/pkg/util/filesystem/go.sum @@ -7,8 +7,8 @@ github.com/ebitengine/purego v0.8.1/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= 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= diff --git a/pkg/util/flavor/go.mod b/pkg/util/flavor/go.mod index 4b2095a3d95dd..91efb20fa615f 100644 --- a/pkg/util/flavor/go.mod +++ b/pkg/util/flavor/go.mod @@ -62,7 +62,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/util/flavor/go.sum b/pkg/util/flavor/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/util/flavor/go.sum +++ b/pkg/util/flavor/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/util/grpc/go.mod b/pkg/util/grpc/go.mod index 46b4cce771519..9598ddb031dea 100644 --- a/pkg/util/grpc/go.mod +++ b/pkg/util/grpc/go.mod @@ -75,7 +75,7 @@ require ( github.com/golang/protobuf v1.5.4 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/util/grpc/go.sum b/pkg/util/grpc/go.sum index 6b1fd57ab75ad..f5e59fbfc1ba8 100644 --- a/pkg/util/grpc/go.sum +++ b/pkg/util/grpc/go.sum @@ -105,8 +105,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/util/http/go.mod b/pkg/util/http/go.mod index fd083ad63e413..74a5f34311844 100644 --- a/pkg/util/http/go.mod +++ b/pkg/util/http/go.mod @@ -64,7 +64,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/util/http/go.sum b/pkg/util/http/go.sum index 71817661e74a3..0ba8e47583917 100644 --- a/pkg/util/http/go.sum +++ b/pkg/util/http/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/util/log/setup/go.mod b/pkg/util/log/setup/go.mod index 480a4c8b2e4cd..f2c45da509f53 100644 --- a/pkg/util/log/setup/go.mod +++ b/pkg/util/log/setup/go.mod @@ -63,7 +63,7 @@ require ( github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/magiconair/properties v1.8.7 // indirect diff --git a/pkg/util/log/setup/go.sum b/pkg/util/log/setup/go.sum index 3f00397cbc301..7fdf16db5981c 100644 --- a/pkg/util/log/setup/go.sum +++ b/pkg/util/log/setup/go.sum @@ -86,8 +86,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.13.0/go.mod h1:8XEsbTttt/W+VvjtQhLACqC github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= diff --git a/pkg/util/system/go.mod b/pkg/util/system/go.mod index b91b137b720c2..78e3380d124ed 100644 --- a/pkg/util/system/go.mod +++ b/pkg/util/system/go.mod @@ -30,7 +30,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/ebitengine/purego v0.8.1 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/lufia/plan9stats v0.0.0-20220913051719-115f729f3c8c // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20220216144756-c35f1ee13d7c // indirect diff --git a/pkg/util/system/go.sum b/pkg/util/system/go.sum index 5dc55f7f10161..47e0acb25ee1f 100644 --- a/pkg/util/system/go.sum +++ b/pkg/util/system/go.sum @@ -10,8 +10,8 @@ github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW 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/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= 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= diff --git a/test/otel/go.mod b/test/otel/go.mod index fd0d05c30abe4..a4523c0b33b0b 100644 --- a/test/otel/go.mod +++ b/test/otel/go.mod @@ -214,7 +214,7 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/hcl v1.0.1-vault-5 // indirect - github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 // indirect + github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect diff --git a/test/otel/go.sum b/test/otel/go.sum index 270aa7b812248..bce88ff15bfa8 100644 --- a/test/otel/go.sum +++ b/test/otel/go.sum @@ -168,8 +168,8 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95 h1:S4qyfL2sEm5Budr4KVMyEniCy+PbS55651I/a+Kn/NQ= -github.com/hectane/go-acl v0.0.0-20190604041725-da78bae5fc95/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb h1:PGufWXXDq9yaev6xX1YQauaO1MV90e6Mpoq1I7Lz/VM= +github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb/go.mod h1:QiyDdbZLaJ/mZP4Zwc9g2QsfaEA4o7XvvgZegSci5/E= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= From 29d90d395e944a518a438b98bfc2610bddfc26df Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 16 Dec 2024 17:27:08 -0500 Subject: [PATCH 45/78] [ebpfless] Fix the pre-existing connection tests (#31858) --- .../connection/ebpfless/tcp_processor.go | 32 ++++++++-- .../connection/ebpfless/tcp_processor_test.go | 31 +++++++++- .../tracer/connection/ebpfless/tcp_utils.go | 18 +++++- .../tracer/connection/ebpfless_tracer.go | 18 +++--- pkg/network/tracer/tracer_linux_test.go | 62 ++++++++++++------- pkg/network/tracer/tracer_test.go | 1 + 6 files changed, 126 insertions(+), 36 deletions(-) diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor.go b/pkg/network/tracer/connection/ebpfless/tcp_processor.go index b312f460269ee..8d530fc3cd70a 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor.go @@ -9,6 +9,7 @@ package ebpfless import ( "fmt" + "github.com/DataDog/datadog-agent/pkg/util/log" "syscall" "time" @@ -17,7 +18,6 @@ import ( "github.com/google/gopacket/layers" "github.com/DataDog/datadog-agent/pkg/network" - "github.com/DataDog/datadog-agent/pkg/util/log" ) type connectionState struct { @@ -51,9 +51,14 @@ type connectionState struct { // remoteFinSeq is the tcp.Seq number for the incoming FIN (including any payload length) remoteFinSeq uint32 + // rttTracker is used to track round trip times rttTracker rttTracker } +func (st *connectionState) hasMissedHandshake() bool { + return st.localSynState == synStateMissed || st.remoteSynState == synStateMissed +} + // TCPProcessor encapsulates TCP state tracking for the ebpfless tracer type TCPProcessor struct { conns map[network.ConnectionTuple]connectionState @@ -125,9 +130,13 @@ func (t *TCPProcessor) updateSynFlag(conn *network.ConnectionStats, st *connecti updateConnStatsForOpen(conn) } // if both synStates are ack'd, move to established - if st.tcpState == connStatAttempted && st.localSynState == synStateAcked && st.remoteSynState == synStateAcked { + if st.tcpState == connStatAttempted && st.localSynState.isSynAcked() && st.remoteSynState.isSynAcked() { st.tcpState = connStatEstablished - conn.Monotonic.TCPEstablished++ + if st.hasMissedHandshake() { + statsTelemetry.missedTCPConnections.Inc() + } else { + conn.Monotonic.TCPEstablished++ + } } } @@ -155,7 +164,7 @@ func (t *TCPProcessor) updateTCPStats(conn *network.ConnectionStats, st *connect ackOutdated := !st.hasLocalAck || isSeqBefore(st.lastLocalAck, tcp.Ack) if tcp.ACK && ackOutdated { // wait until data comes in via synStateAcked - if st.hasLocalAck && st.remoteSynState == synStateAcked { + if st.hasLocalAck && st.remoteSynState.isSynAcked() { ackDiff := tcp.Ack - st.lastLocalAck isFinAck := st.hasRemoteFin && tcp.Ack == st.remoteFinSeq if isFinAck { @@ -260,3 +269,18 @@ func (t *TCPProcessor) Process(conn *network.ConnectionStats, timestampNs uint64 t.conns[conn.ConnectionTuple] = st return nil } + +// HasConnEverEstablished is used to avoid a connection appearing before the three-way handshake is complete. +// This is done to mirror the behavior of ebpf tracing accept() and connect(), which both return +// after the handshake is completed. +func (t *TCPProcessor) HasConnEverEstablished(conn *network.ConnectionStats) bool { + st := t.conns[conn.ConnectionTuple] + + // conn.Monotonic.TCPEstablished can be 0 even though isEstablished is true, + // because pre-existing connections don't increment TCPEstablished. + // That's why we use tcpState instead of conn + isEstablished := st.tcpState == connStatEstablished + // if the connection has closed in any way, report that too + hasEverClosed := conn.Monotonic.TCPClosed > 0 || len(conn.TCPFailures) > 0 + return isEstablished || hasEverClosed +} diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go index 8656a73463d36..f19729579a6bc 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go @@ -181,7 +181,7 @@ func (fixture *tcpTestFixture) runPkt(pkt testCapture) { require.NoError(fixture.t, err) } -func (fixture *tcpTestFixture) runPkts(packets []testCapture) { //nolint:unused // TODO +func (fixture *tcpTestFixture) runPkts(packets []testCapture) { for _, pkt := range packets { fixture.runPkt(pkt) } @@ -775,3 +775,32 @@ func TestOpenCloseConn(t *testing.T) { f.runPkt(pb.incoming(0, 0, 0, SYN)) require.False(t, f.conn.IsClosed) } +func TestPreexistingConn(t *testing.T) { + pb := newPacketBuilder(lowerSeq, higherSeq) + + f := newTCPTestFixture(t) + + capture := []testCapture{ + // just sending data, no SYN + pb.outgoing(1, 10, 10, ACK), + pb.incoming(1, 10, 11, ACK), + // active close after sending no data + pb.outgoing(0, 11, 11, FIN|ACK), + pb.incoming(0, 11, 12, FIN|ACK), + pb.outgoing(0, 12, 12, ACK), + } + f.runPkts(capture) + + require.Empty(t, f.conn.TCPFailures) + + expectedStats := network.StatCounters{ + SentBytes: 1, + RecvBytes: 1, + SentPackets: 3, + RecvPackets: 2, + Retransmits: 0, + TCPEstablished: 0, // we missed when it established + TCPClosed: 1, + } + require.Equal(t, expectedStats, f.conn.Monotonic) +} diff --git a/pkg/network/tracer/connection/ebpfless/tcp_utils.go b/pkg/network/tracer/connection/ebpfless/tcp_utils.go index 1969fd85a5bc1..9ebe4d778969d 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_utils.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_utils.go @@ -54,9 +54,16 @@ var connStatusLabels = []string{ type synState uint8 const ( + // synStateNone - Nothing seen yet (initial state) synStateNone synState = iota + // synStateSent - We have seen the SYN but not its ACK synStateSent + // synStateAcked - SYN is ACK'd for this side of the connection. + // If both sides are synStateAcked, the connection is established. synStateAcked + // synStateMissed is effectively the same as synStateAcked but represents + // capturing a preexisting connection where we didn't get to see the SYN. + synStateMissed ) func (ss *synState) update(synFlag, ackFlag bool) { @@ -68,13 +75,20 @@ func (ss *synState) update(synFlag, ackFlag bool) { if *ss == synStateSent && ackFlag { *ss = synStateAcked } + + // this allows synStateMissed to recover via SYN in order to pass TestUnusualAckSyn + if *ss == synStateMissed && synFlag { + *ss = synStateAcked + } // if we see ACK'd traffic but missed the SYN, assume the connection started before // the datadog-agent starts. if *ss == synStateNone && ackFlag { - statsTelemetry.missedTCPConnections.Inc() - *ss = synStateAcked + *ss = synStateMissed } } +func (ss *synState) isSynAcked() bool { + return *ss == synStateAcked || *ss == synStateMissed +} func labelForState(tcpState connStatus) string { idx := int(tcpState) diff --git a/pkg/network/tracer/connection/ebpfless_tracer.go b/pkg/network/tracer/connection/ebpfless_tracer.go index 8bb0a54f170ec..e4e661c2782a3 100644 --- a/pkg/network/tracer/connection/ebpfless_tracer.go +++ b/pkg/network/tracer/connection/ebpfless_tracer.go @@ -148,20 +148,23 @@ func (t *ebpfLessTracer) processConnection( tcp *layers.TCP, decoded []gopacket.LayerType, ) error { + t.scratchConn.Source, t.scratchConn.Dest = util.Address{}, util.Address{} t.scratchConn.SPort, t.scratchConn.DPort = 0, 0 t.scratchConn.TCPFailures = make(map[uint16]uint32) - var udpPresent, tcpPresent bool + var ip4Present, ip6Present, udpPresent, tcpPresent bool for _, layerType := range decoded { switch layerType { case layers.LayerTypeIPv4: t.scratchConn.Source = util.AddressFromNetIP(ip4.SrcIP) t.scratchConn.Dest = util.AddressFromNetIP(ip4.DstIP) t.scratchConn.Family = network.AFINET + ip4Present = true case layers.LayerTypeIPv6: t.scratchConn.Source = util.AddressFromNetIP(ip6.SrcIP) t.scratchConn.Dest = util.AddressFromNetIP(ip6.DstIP) t.scratchConn.Family = network.AFINET6 + ip6Present = true case layers.LayerTypeTCP: t.scratchConn.SPort = uint16(tcp.SrcPort) t.scratchConn.DPort = uint16(tcp.DstPort) @@ -175,15 +178,15 @@ func (t *ebpfLessTracer) processConnection( } } - // check if have all the basic pieces + // check if we have all the basic pieces if !udpPresent && !tcpPresent { log.Debugf("ignoring packet since its not udp or tcp") ebpfLessTracerTelemetry.skippedPackets.Inc("not_tcp_udp") return nil } - flipSourceDest(t.scratchConn, pktType) t.determineConnectionDirection(t.scratchConn, pktType) + flipSourceDest(t.scratchConn, pktType) t.m.Lock() defer t.m.Unlock() @@ -202,17 +205,17 @@ func (t *ebpfLessTracer) processConnection( return fmt.Errorf("error getting last updated timestamp for connection: %w", err) } - if ip4 == nil && ip6 == nil { + if !ip4Present && !ip6Present { return nil } switch conn.Type { case network.UDP: - if (ip4 != nil && !t.config.CollectUDPv4Conns) || (ip6 != nil && !t.config.CollectUDPv6Conns) { + if (ip4Present && !t.config.CollectUDPv4Conns) || (ip6Present && !t.config.CollectUDPv6Conns) { return nil } err = t.udp.process(conn, pktType, udp) case network.TCP: - if (ip4 != nil && !t.config.CollectTCPv4Conns) || (ip6 != nil && !t.config.CollectTCPv6Conns) { + if (ip4Present && !t.config.CollectTCPv4Conns) || (ip6Present && !t.config.CollectTCPv6Conns) { return nil } err = t.tcp.Process(conn, uint64(ts), pktType, ip4, ip6, tcp) @@ -224,7 +227,8 @@ func (t *ebpfLessTracer) processConnection( return fmt.Errorf("error processing connection: %w", err) } - if conn.Type == network.UDP || conn.Monotonic.TCPEstablished > 0 { + // TODO probably remove HasConnEverEstablished once we handle closed connections properly + if conn.Type == network.UDP || (conn.Type == network.TCP && t.tcp.HasConnEverEstablished(conn)) { conn.LastUpdateEpoch = uint64(ts) t.conns[t.scratchConn.ConnectionTuple] = conn } diff --git a/pkg/network/tracer/tracer_linux_test.go b/pkg/network/tracer/tracer_linux_test.go index d90928d560da8..29a8b6e34965a 100644 --- a/pkg/network/tracer/tracer_linux_test.go +++ b/pkg/network/tracer/tracer_linux_test.go @@ -1979,16 +1979,16 @@ func (s *TracerSuite) TestPreexistingConnectionDirection() { t := s.T() // Start the client and server before we enable the system probe to test that the tracer picks // up the pre-existing connection - server := tracertestutil.NewTCPServer(func(c net.Conn) { r := bufio.NewReader(c) for { if _, err := r.ReadBytes(byte('\n')); err != nil { assert.ErrorIs(t, err, io.EOF, "exited server loop with error that is not EOF") - return + break } _, _ = c.Write(genPayload(serverMessageSize)) } + c.Close() }) t.Cleanup(server.Shutdown) require.NoError(t, server.Run()) @@ -2023,37 +2023,55 @@ func (s *TracerSuite) TestPreexistingConnectionDirection() { } require.NotNil(collect, outgoing) require.NotNil(collect, incoming) - }, 3*time.Second, 100*time.Millisecond, "could not find connection incoming and outgoing connections") + if !assert.True(collect, incoming != nil && outgoing != nil) { + return + } - m := outgoing.Monotonic - assert.Equal(t, clientMessageSize, int(m.SentBytes)) - assert.Equal(t, serverMessageSize, int(m.RecvBytes)) - if !tr.config.EnableEbpfless { - assert.Equal(t, os.Getpid(), int(outgoing.Pid)) - } - assert.Equal(t, addrPort(server.Address()), int(outgoing.DPort)) - assert.Equal(t, c.LocalAddr().(*net.TCPAddr).Port, int(outgoing.SPort)) - assert.Equal(t, network.OUTGOING, outgoing.Direction) + m := outgoing.Monotonic + assert.Equal(collect, clientMessageSize, int(m.SentBytes)) + // ebpfless RecvBytes is based off acknowledgements, so it can miss the first + // packet in a pre-existing connection + if !tr.config.EnableEbpfless { + assert.Equal(collect, serverMessageSize, int(m.RecvBytes)) + } + if !tr.config.EnableEbpfless { + assert.Equal(collect, os.Getpid(), int(outgoing.Pid)) + } + assert.Equal(collect, addrPort(server.Address()), int(outgoing.DPort)) + assert.Equal(collect, c.LocalAddr().(*net.TCPAddr).Port, int(outgoing.SPort)) + assert.Equal(collect, network.OUTGOING, outgoing.Direction) + + m = incoming.Monotonic + // ebpfless RecvBytes is based off acknowledgements, so it can miss the first + // packet in a pre-existing connection + if !tr.config.EnableEbpfless { + assert.Equal(collect, clientMessageSize, int(m.RecvBytes)) + } + assert.Equal(collect, serverMessageSize, int(m.SentBytes)) + if !tr.config.EnableEbpfless { + assert.Equal(collect, os.Getpid(), int(incoming.Pid)) + } + assert.Equal(collect, addrPort(server.Address()), int(incoming.SPort)) + assert.Equal(collect, c.LocalAddr().(*net.TCPAddr).Port, int(incoming.DPort)) + assert.Equal(collect, network.INCOMING, incoming.Direction) + }, 3*time.Second, 100*time.Millisecond, "could not find connection incoming and outgoing connections") - m = incoming.Monotonic - assert.Equal(t, clientMessageSize, int(m.RecvBytes)) - assert.Equal(t, serverMessageSize, int(m.SentBytes)) - if !tr.config.EnableEbpfless { - assert.Equal(t, os.Getpid(), int(incoming.Pid)) - } - assert.Equal(t, addrPort(server.Address()), int(incoming.SPort)) - assert.Equal(t, c.LocalAddr().(*net.TCPAddr).Port, int(incoming.DPort)) - assert.Equal(t, network.INCOMING, incoming.Direction) } func (s *TracerSuite) TestPreexistingEmptyIncomingConnectionDirection() { t := s.T() + + // The ebpf tracer uses this to ensure it drops pre-existing connections + // that close empty (with no data), because they are difficult to track. + // However, in ebpfless they are easy to track, so disable this test. + // For more context, see PR #31100 + skipOnEbpflessNotSupported(t, testConfig()) + t.Run("ringbuf_enabled", func(t *testing.T) { if features.HaveMapType(ebpf.RingBuf) != nil { t.Skip("skipping test as ringbuffers are not supported on this kernel") } c := testConfig() - skipOnEbpflessNotSupported(t, c) c.NPMRingbuffersEnabled = true testPreexistingEmptyIncomingConnectionDirection(t, c) }) diff --git a/pkg/network/tracer/tracer_test.go b/pkg/network/tracer/tracer_test.go index 710e5eb142253..d50f4f299690c 100644 --- a/pkg/network/tracer/tracer_test.go +++ b/pkg/network/tracer/tracer_test.go @@ -1130,6 +1130,7 @@ func (s *TracerSuite) TestTCPEstablishedPreExistingConn() { c, err := net.DialTimeout("tcp", server.Address(), 50*time.Millisecond) require.NoError(t, err) laddr, raddr := c.LocalAddr(), c.RemoteAddr() + t.Logf("laddr=%s raddr=%s", laddr, raddr) // Ensure closed connections are flushed as soon as possible cfg := testConfig() From 4ab0c77dabc38744147a5d86748f3e45bd87101d Mon Sep 17 00:00:00 2001 From: sabrina lu Date: Mon, 16 Dec 2024 18:18:27 -0500 Subject: [PATCH 46/78] add agent 6 schedule to create rc pr workflow (#32073) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/create_rc_pr.yml | 24 +++++++++++++++++------- tasks/collector.py | 19 +------------------ tasks/libs/common/git.py | 18 ++++++++++++++++++ tasks/release.py | 12 ++++++++++-- 4 files changed, 46 insertions(+), 27 deletions(-) diff --git a/.github/workflows/create_rc_pr.yml b/.github/workflows/create_rc_pr.yml index 4522ddd4100a2..2a685c3cc85df 100644 --- a/.github/workflows/create_rc_pr.yml +++ b/.github/workflows/create_rc_pr.yml @@ -5,10 +5,12 @@ on: schedule: - cron: '0 14 * * 1,3,5' # Run on Monday, Wednesday, and Friday at 14:00 UTC - cron: '0 8 * * 1,3,5' # Same as above but at 08:00 UTC, to warn agent-integrations team about releasing + - cron: '0 9 * * 1' # Run Agent 6 workflow on Monday at 09:00 UTC env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + AGENT6_RELEASE_BRANCH: '6.53.x' + IS_AGENT6_RELEASE: ${{ github.event.schedule == '0 9 * * 1' }} permissions: {} jobs: @@ -19,18 +21,21 @@ jobs: warning: ${{ steps.warning.outputs.value }} steps: - name: Checkout repository + if: ${{ env.IS_AGENT6_RELEASE == 'false' }} uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 with: sparse-checkout: 'tasks' persist-credentials: false - name: Install python + if: ${{ env.IS_AGENT6_RELEASE == 'false' }} uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 with: python-version: 3.11 cache: "pip" - name: Install Python dependencies + if: ${{ env.IS_AGENT6_RELEASE == 'false' }} run: | python -m pip install --upgrade pip pip install -r requirements.txt @@ -40,7 +45,11 @@ jobs: - name: Determine the release active branches id: branches run: | - echo "value=$(inv release.get-unreleased-release-branches)" >> $GITHUB_OUTPUT + if ${{ env.IS_AGENT6_RELEASE == 'true' }}; then + echo "value=[\"$AGENT6_RELEASE_BRANCH\"]" >> $GITHUB_OUTPUT + else + echo "value=$(inv release.get-unreleased-release-branches)" >> $GITHUB_OUTPUT + fi - name: Set the warning option id: warning @@ -93,11 +102,12 @@ jobs: fi - name: Create RC PR - if: ${{ steps.check_for_changes.outputs.CHANGES == 'true'}} + if: ${{ steps.check_for_changes.outputs.CHANGES == 'true' || env.IS_AGENT6_RELEASE == 'true' }} env: MATRIX: ${{ matrix.value }} run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git fetch - inv -e release.create-rc -r "$MATRIX" --slack-webhook=${{ secrets.AGENT_RELEASE_SYNC_SLACK_WEBHOOK }} + if ${{ env.IS_AGENT6_RELEASE == 'true' }}; then + inv -e release.create-rc -r "$MATRIX" --slack-webhook=${{ secrets.AGENT_RELEASE_SYNC_SLACK_WEBHOOK }} --patch-version + else + inv -e release.create-rc -r "$MATRIX" --slack-webhook=${{ secrets.AGENT_RELEASE_SYNC_SLACK_WEBHOOK }} + fi diff --git a/tasks/collector.py b/tasks/collector.py index e4dbd4ae4d68d..74f3a439bd9fd 100644 --- a/tasks/collector.py +++ b/tasks/collector.py @@ -14,7 +14,7 @@ from tasks.go import tidy from tasks.libs.ciproviders.github_api import GithubAPI from tasks.libs.common.color import Color, color_message -from tasks.libs.common.git import check_uncommitted_changes +from tasks.libs.common.git import check_uncommitted_changes, get_git_config, revert_git_config, set_git_config LICENSE_HEADER = """// Unless explicitly stated otherwise all files in this repository are licensed // under the Apache License Version 2.0. @@ -502,23 +502,6 @@ def update(ctx): print("Update complete.") -def get_git_config(key): - result = subprocess.run(['git', 'config', '--get', key], capture_output=True, text=True) - return result.stdout.strip() if result.returncode == 0 else None - - -def set_git_config(key, value): - subprocess.run(['git', 'config', key, value]) - - -def revert_git_config(original_config): - for key, value in original_config.items(): - if value is None: - subprocess.run(['git', 'config', '--unset', key]) - else: - subprocess.run(['git', 'config', key, value]) - - @task() def pull_request(ctx): # Save current Git configuration diff --git a/tasks/libs/common/git.py b/tasks/libs/common/git.py index b50184c3af411..b1dc1b1cd7683 100644 --- a/tasks/libs/common/git.py +++ b/tasks/libs/common/git.py @@ -1,6 +1,7 @@ from __future__ import annotations import os +import subprocess import sys import tempfile from contextlib import contextmanager @@ -292,3 +293,20 @@ def get_last_release_tag(ctx, repo, pattern): last_tag_name = last_tag_name_with_suffix.removesuffix("^{}") last_tag_name = last_tag_name.removeprefix("refs/tags/") return last_tag_commit, last_tag_name + + +def get_git_config(key): + result = subprocess.run(['git', 'config', '--get', key], capture_output=True, text=True) + return result.stdout.strip() if result.returncode == 0 else None + + +def set_git_config(key, value): + subprocess.run(['git', 'config', key, value]) + + +def revert_git_config(original_config): + for key, value in original_config.items(): + if value is None: + subprocess.run(['git', 'config', '--unset', key]) + else: + subprocess.run(['git', 'config', key, value]) diff --git a/tasks/release.py b/tasks/release.py index aa16b7fd7045f..d7acec73f8e05 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -34,6 +34,7 @@ get_last_commit, get_last_release_tag, is_agent6, + set_git_config, try_git_command, ) from tasks.libs.common.gomodules import get_default_modules @@ -432,11 +433,16 @@ def create_rc(ctx, release_branch, patch_version=False, upstream="origin", slack This also requires that there are no local uncommitted changes, that the current branch is 'main' or the release branch, and that no branch named 'release/' already exists locally or upstream. """ - major_version = get_version_major(release_branch) with agent_context(ctx, release_branch): github = GithubAPI(repository=GITHUB_REPO_NAME) + github_action = os.environ.get("GITHUB_ACTIONS") + + if github_action: + set_git_config('user.name', 'github-actions[bot]') + set_git_config('user.email', 'github-actions[bot]@users.noreply.github.com') + upstream = f"https://x-access-token:{os.environ.get('GITHUB_TOKEN')}@github.com/{GITHUB_REPO_NAME}.git" # Get the version of the highest major: useful for some logging & to get # the version to use for Go submodules updates @@ -491,7 +497,9 @@ def create_rc(ctx, release_branch, patch_version=False, upstream="origin", slack ctx.run("git ls-files . | grep 'go.mod$' | xargs git add") ok = try_git_command( - ctx, f"git commit --no-verify -m 'Update release.json and Go modules for {new_highest_version}'" + ctx, + f"git commit --no-verify -m 'Update release.json and Go modules for {new_highest_version}'", + github_action, ) if not ok: raise Exit( From 03265530dcf3b87f814d51f4b2a1cd34616d7e2e Mon Sep 17 00:00:00 2001 From: Julien Lebot Date: Tue, 17 Dec 2024 00:47:22 +0100 Subject: [PATCH 47/78] [Fleet Automation][Windows] Improve error handling (#31282) --- .../packages/datadog_agent_windows.go | 40 +- .../packages/datadog_installer_windows.go | 102 +++-- .../internal/bootstrap/bootstrap_windows.go | 25 +- pkg/fleet/internal/msi/file_in_use.log | Bin 0 -> 1895134 bytes .../internal/msi/invalid_credentials.log | Bin 0 -> 1397784 bytes .../internal/msi/missing_password_for_dc.log | Bin 0 -> 1346326 bytes pkg/fleet/internal/msi/msiexec.go | 289 +++++++++++++ pkg/fleet/internal/msi/msilog.go | 130 ++++++ pkg/fleet/internal/msi/msilog_test.go | 393 ++++++++++++++++++ .../msiexec.go => internal/msi/product.go} | 85 ++-- .../msi/service_marked_for_deletion.log | Bin 0 -> 108018 bytes .../internal/msi/wixfailwhendeferred.log | Bin 0 -> 128276 bytes 12 files changed, 936 insertions(+), 128 deletions(-) create mode 100644 pkg/fleet/internal/msi/file_in_use.log create mode 100644 pkg/fleet/internal/msi/invalid_credentials.log create mode 100644 pkg/fleet/internal/msi/missing_password_for_dc.log create mode 100644 pkg/fleet/internal/msi/msiexec.go create mode 100644 pkg/fleet/internal/msi/msilog.go create mode 100644 pkg/fleet/internal/msi/msilog_test.go rename pkg/fleet/{installer/packages/msiexec.go => internal/msi/product.go} (59%) create mode 100644 pkg/fleet/internal/msi/service_marked_for_deletion.log create mode 100644 pkg/fleet/internal/msi/wixfailwhendeferred.log diff --git a/pkg/fleet/installer/packages/datadog_agent_windows.go b/pkg/fleet/installer/packages/datadog_agent_windows.go index 69935fdee41d8..48e3e1e00148c 100644 --- a/pkg/fleet/installer/packages/datadog_agent_windows.go +++ b/pkg/fleet/installer/packages/datadog_agent_windows.go @@ -10,10 +10,13 @@ package packages import ( "context" "fmt" - + "github.com/DataDog/datadog-agent/pkg/fleet/internal/msi" + "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" "github.com/DataDog/datadog-agent/pkg/fleet/internal/winregistry" "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" "github.com/DataDog/datadog-agent/pkg/util/log" + "os" + "path" ) const ( @@ -29,9 +32,8 @@ func PrepareAgent(_ context.Context) error { func SetupAgent(ctx context.Context, args []string) (err error) { span, _ := telemetry.StartSpanFromContext(ctx, "setup_agent") defer func() { - if err != nil { - log.Errorf("Failed to setup agent: %s", err) - } + // Don't log error here, or it will appear twice in the output + // since installerImpl.Install will also print the error. span.Finish(err) }() // Make sure there are no Agent already installed @@ -108,20 +110,38 @@ func installAgentPackage(target string, args []string) error { if err != nil { return fmt.Errorf("failed to get Agent user: %w", err) } - args = append(args, fmt.Sprintf("DDAGENTUSER_NAME=%s", agentUser)) - cmd, err := msiexec(target, datadogAgent, "/i", args) + rootPath := "" + _, err = os.Stat(paths.RootTmpDir) + // If bootstrap has not been called before, `paths.RootTmpDir` might not exist + if os.IsExist(err) { + rootPath = paths.RootTmpDir + } + tempDir, err := os.MkdirTemp(rootPath, "datadog-agent") + if err != nil { + return err + } + logFile := path.Join(tempDir, "msi.log") + + cmd, err := msi.Cmd( + msi.Install(), + msi.WithMsiFromPackagePath(target, datadogAgent), + msi.WithDdAgentUserName(agentUser), + msi.WithAdditionalArgs(args), + msi.WithLogFile(path.Join(tempDir, "msi.log")), + ) + var output []byte if err == nil { - err = cmd.Run() + output, err = cmd.Run() } if err != nil { - return fmt.Errorf("failed to install Agent %s: %w", target, err) + return fmt.Errorf("failed to install Agent %s: %w\nLog file located at: %s\n%s", target, err, logFile, string(output)) } return nil } func removeAgentIfInstalled(ctx context.Context) (err error) { - if isProductInstalled("Datadog Agent") { + if msi.IsProductInstalled("Datadog Agent") { span, _ := telemetry.StartSpanFromContext(ctx, "remove_agent") defer func() { if err != nil { @@ -131,7 +151,7 @@ func removeAgentIfInstalled(ctx context.Context) (err error) { } span.Finish(err) }() - err := removeProduct("Datadog Agent") + err := msi.RemoveProduct("Datadog Agent") if err != nil { return err } diff --git a/pkg/fleet/installer/packages/datadog_installer_windows.go b/pkg/fleet/installer/packages/datadog_installer_windows.go index d7714ddb257cc..6a4b313120840 100644 --- a/pkg/fleet/installer/packages/datadog_installer_windows.go +++ b/pkg/fleet/installer/packages/datadog_installer_windows.go @@ -10,9 +10,11 @@ package packages import ( "context" - - "github.com/DataDog/datadog-agent/pkg/fleet/telemetry" - "github.com/DataDog/datadog-agent/pkg/util/log" + "fmt" + "github.com/DataDog/datadog-agent/pkg/fleet/internal/msi" + "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" + "os" + "path" ) const ( @@ -20,68 +22,62 @@ const ( ) // SetupInstaller installs and starts the installer -func SetupInstaller(ctx context.Context) (err error) { - span, _ := telemetry.StartSpanFromContext(ctx, "setup_installer") - defer func() { - if err != nil { - log.Errorf("Failed to setup installer: %s", err) - } - span.Finish(err) - }() - cmd, err := msiexec("stable", datadogInstaller, "/i", nil) - if err == nil { - // This is the first time that we are installing the installer, - // so we can run it synchronously. - err = cmd.Run() +func SetupInstaller(_ context.Context) error { + rootPath := "" + _, err := os.Stat(paths.RootTmpDir) + // If bootstrap has not been called before, `paths.RootTmpDir` might not exist + if os.IsExist(err) { + rootPath = paths.RootTmpDir + } + tempDir, err := os.MkdirTemp(rootPath, "datadog-installer") + if err != nil { + return err + } + + cmd, err := msi.Cmd(msi.Install(), msi.WithMsiFromPackagePath("stable", datadogInstaller), msi.WithLogFile(path.Join(tempDir, "setup_installer.log"))) + if err != nil { + return fmt.Errorf("failed to setup installer: %w", err) + } + output, err := cmd.Run() + if err != nil { + return fmt.Errorf("failed to setup installer: %w\n%s", err, string(output)) } - return err + return nil } // RemoveInstaller removes the installer -func RemoveInstaller(ctx context.Context) (err error) { - span, _ := telemetry.StartSpanFromContext(ctx, "remove_installer") - defer func() { - if err != nil { - log.Errorf("Failed to remove installer: %s", err) - } - span.Finish(err) - }() - err = removeProduct("Datadog Installer") - return err +func RemoveInstaller(_ context.Context) error { + return msi.RemoveProduct("Datadog Installer") } // StartInstallerExperiment starts the installer experiment -func StartInstallerExperiment(ctx context.Context) (err error) { - span, _ := telemetry.StartSpanFromContext(ctx, "start_installer_experiment") - defer func() { - if err != nil { - log.Errorf("Failed to start installer experiment: %s", err) - } - span.Finish(err) - }() - cmd, err := msiexec("experiment", datadogInstaller, "/i", nil) - if err == nil { - // Launch the msiexec process asynchronously. - err = cmd.Start() +func StartInstallerExperiment(_ context.Context) error { + tempDir, err := os.MkdirTemp(paths.RootTmpDir, "datadog-installer") + if err != nil { + return err + } + + cmd, err := msi.Cmd(msi.Install(), msi.WithMsiFromPackagePath("experiment", datadogInstaller), msi.WithLogFile(path.Join(tempDir, "start_installer_experiment.log"))) + if err != nil { + return fmt.Errorf("failed to start installer experiment: %w", err) } - return err + // Launch the msiexec process asynchronously. + return cmd.FireAndForget() } // StopInstallerExperiment stops the installer experiment -func StopInstallerExperiment(ctx context.Context) (err error) { - span, _ := telemetry.StartSpanFromContext(ctx, "stop_installer_experiment") - defer func() { - if err != nil { - log.Errorf("Failed to stop installer experiment: %s", err) - } - span.Finish(err) - }() - cmd, err := msiexec("stable", datadogInstaller, "/i", nil) - if err == nil { - // Launch the msiexec process asynchronously. - err = cmd.Start() +func StopInstallerExperiment(_ context.Context) error { + tempDir, err := os.MkdirTemp(paths.RootTmpDir, "datadog-installer") + if err != nil { + return err + } + + cmd, err := msi.Cmd(msi.Install(), msi.WithMsiFromPackagePath("stable", datadogInstaller), msi.WithLogFile(path.Join(tempDir, "stop_installer_experiment.log"))) + if err != nil { + return fmt.Errorf("failed to stop installer experiment: %w", err) } - return err + // Launch the msiexec process asynchronously. + return cmd.FireAndForget() } // PromoteInstallerExperiment promotes the installer experiment diff --git a/pkg/fleet/internal/bootstrap/bootstrap_windows.go b/pkg/fleet/internal/bootstrap/bootstrap_windows.go index 6990ec77cf042..7172d02a1b6a9 100644 --- a/pkg/fleet/internal/bootstrap/bootstrap_windows.go +++ b/pkg/fleet/internal/bootstrap/bootstrap_windows.go @@ -11,8 +11,8 @@ package bootstrap import ( "context" "fmt" + "github.com/DataDog/datadog-agent/pkg/fleet/internal/msi" "os" - "os/exec" "path/filepath" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" @@ -81,20 +81,19 @@ func downloadInstaller(ctx context.Context, env *env.Env, url string, tmpDir str } else if len(msis) == 0 { return nil, fmt.Errorf("no MSIs in package") } - msiArgs := []string{ - "/i", - msis[0], - "/qn", - "MSIFASTINSTALL=7", - } - if env.AgentUserName != "" { - msiArgs = append(msiArgs, fmt.Sprintf("DDAGENTUSER_NAME=%s", env.AgentUserName)) - // don't need to look at the registry here since the installer will read it if the command line - // parameter is not provided + + cmd, err := msi.Cmd( + msi.Install(), + msi.WithMsi(msis[0]), + msi.WithDdAgentUserName(env.AgentUserName), + ) + var output []byte + if err == nil { + output, err = cmd.Run() } - err = exec.Command("msiexec", msiArgs...).Run() + if err != nil { - return nil, fmt.Errorf("failed to install the Datadog Installer") + return nil, fmt.Errorf("failed to install the Datadog Installer: %w\n%s", err, string(output)) } return iexec.NewInstallerExec(env, paths.StableInstallerPath), nil } diff --git a/pkg/fleet/internal/msi/file_in_use.log b/pkg/fleet/internal/msi/file_in_use.log new file mode 100644 index 0000000000000000000000000000000000000000..eb0ad60b3ddd666d06868c7827a30dea71754a3e GIT binary patch literal 1895134 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

BJ-X?E@D^6Y_WD)%a(W}h2JF!(Ju?r%;L^8N1 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 literal 0 HcmV?d00001 diff --git a/pkg/fleet/internal/msi/msiexec.go b/pkg/fleet/internal/msi/msiexec.go new file mode 100644 index 0000000000000..f2ca387fb3aa2 --- /dev/null +++ b/pkg/fleet/internal/msi/msiexec.go @@ -0,0 +1,289 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build windows + +// Package msi contains helper functions to work with msi packages +package msi + +import ( + "errors" + "fmt" + "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" + "io/fs" + "os" + "os/exec" + "path" + "path/filepath" + "regexp" +) + +type msiexecArgs struct { + // target should be either a full path to a MSI, an URL to a MSI or a product code. + target string + + // msiAction should be "/i" for installation, "/x" for uninstallation etc... + msiAction string + + // logFile should be a full local path where msiexec will write the installation logs. + // If nothing is specified, a random, temporary file is used. + logFile string + ddagentUserName string + + // additionalArgs are further args that can be passed to msiexec + additionalArgs []string +} + +// MsiexecOption is an option type for creating msiexec command lines +type MsiexecOption func(*msiexecArgs) error + +// Install specifies that msiexec will be invoked to install a product +func Install() MsiexecOption { + return func(a *msiexecArgs) error { + a.msiAction = "/i" + return nil + } +} + +// Uninstall specifies that msiexec will be invoked to uninstall a product +func Uninstall() MsiexecOption { + return func(a *msiexecArgs) error { + a.msiAction = "/x" + return nil + } +} + +// WithMsi specifies the MSI target for msiexec +func WithMsi(target string) MsiexecOption { + return func(a *msiexecArgs) error { + a.target = target + return nil + } +} + +// WithMsiFromPackagePath finds an MSI from the packages folder +func WithMsiFromPackagePath(target, product string) MsiexecOption { + return func(a *msiexecArgs) error { + updaterPath := filepath.Join(paths.PackagesPath, product, target) + msis, err := filepath.Glob(filepath.Join(updaterPath, fmt.Sprintf("%s-*-1-x86_64.msi", product))) + if err != nil { + return err + } + if len(msis) > 1 { + return fmt.Errorf("too many MSIs in package") + } else if len(msis) == 0 { + return fmt.Errorf("no MSIs in package") + } + a.target = msis[0] + return nil + } +} + +// WithProduct specifies the product name to target for msiexec +func WithProduct(productName string) MsiexecOption { + return func(a *msiexecArgs) error { + product, err := FindProductCode(productName) + if err != nil { + return fmt.Errorf("error trying to find product %s: %w", productName, err) + } + a.target = product.Code + return nil + } +} + +// WithLogFile specifies the log file for msiexec +func WithLogFile(logFile string) MsiexecOption { + return func(a *msiexecArgs) error { + a.logFile = logFile + return nil + } +} + +// WithAdditionalArgs specifies additional arguments for msiexec +func WithAdditionalArgs(additionalArgs []string) MsiexecOption { + return func(a *msiexecArgs) error { + a.additionalArgs = additionalArgs + return nil + } +} + +// WithDdAgentUserName specifies the DDAGENTUSER_NAME to use +func WithDdAgentUserName(ddagentUserName string) MsiexecOption { + return func(a *msiexecArgs) error { + a.ddagentUserName = ddagentUserName + return nil + } +} + +// Msiexec is a type wrapping msiexec +type Msiexec struct { + *exec.Cmd + + // logFile is the path to the MSI log file + logFile string + + // postExecActions is a list of actions to be executed after msiexec has run + postExecActions []func() +} + +func (m *Msiexec) openAndProcessLogFile() ([]byte, error) { + logfile, err := os.Open(m.logFile) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + // File does not exist is not necessarily an error + return nil, nil + } + return nil, err + } + result, err := m.processLogFile(logfile) + _ = logfile.Close() + return result, err +} + +// processLogFile takes an open file and processes it with a series of processors to obtain +// a condensed version of the log file with only the relevant information. +func (m *Msiexec) processLogFile(logFile fs.File) ([]byte, error) { + // Compile a list of regular expressions we are interested in extracting from the logs + return processLogFile(logFile, + func(bytes []byte) []TextRange { + // Only need one TextRange of context before and after since other regexes will combine + return FindAllIndexWithContext(regexp.MustCompile("Datadog[.]CustomActions.*"), bytes, 1, 1) + }, + func(bytes []byte) []TextRange { + // Only need one TextRange of context before and after since other regexes will combine + return FindAllIndexWithContext(regexp.MustCompile("System[.]Exception"), bytes, 1, 1) + }, + func(bytes []byte) []TextRange { + // typically looks like this: + // Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.StartDDServices + // CA: 01:50:49: StartDDServices. Failed to start services: System.InvalidOperationException: Cannot start service datadogagent on computer '.'. ---> System.ComponentModel.Win32Exception: The service did not start due to a logon failure + // --- End of inner exception stack trace --- + // at System.ServiceProcess.ServiceController.Start(String args) + // at Datadog.CustomActions.Native.ServiceController.StartService(String serviceName, TimeSpan timeout) + // at Datadog.CustomActions.ServiceCustomAction.StartDDServices() + // Other regexes will pick up on the stack trace, but there's not much information to get before the error + return FindAllIndexWithContext(regexp.MustCompile("Cannot start service"), bytes, 1, 2) + }, + func(bytes []byte) []TextRange { + // Typically looks like this: + // CA(ddnpm): DriverInstall: serviceDef::create() + // CA(ddnpm): DriverInstall: Failed to CreateService 1073 + // CA(ddnpm): DriverInstall: Service exists, verifying + // CA(ddnpm): DriverInstall: Updated path for existing service + // So include a bit of context before and after + return FindAllIndexWithContext(regexp.MustCompile("Failed to CreateService"), bytes, 5, 5) + }, + func(bytes []byte) []TextRange { + // Typically looks like this: + // Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.ProcessDdAgentUserCredentials + // CA: 01:49:43: LookupAccountWithExtendedDomainSyntax. User not found, trying again with fixed domain part: \toto + // CA: 01:49:43: ProcessDdAgentUserCredentials. User toto doesn't exist. + // CA: 01:49:43: ProcessDdAgentUserCredentials. domain part is empty, using default + // CA: 01:49:43: ProcessDdAgentUserCredentials. Installing with DDAGENTUSER_PROCESSED_NAME=toto and DDAGENTUSER_PROCESSED_DOMAIN=datadoghq-qa-labs.local + // CA: 01:49:43: HandleProcessDdAgentUserCredentialsException. Error processing ddAgentUser credentials: Datadog.CustomActions.InvalidAgentUserConfigurationException: A password was not provided. A password is a required when installing on Domain Controllers. + // at Datadog.CustomActions.ProcessUserCustomActions.ProcessDdAgentUserCredentials(Boolean calledFromUIControl) + // MSI (s) (C8!50) [01:49:43:906]: Product: Datadog Agent -- A password was not provided. A password is a required when installing on Domain Controllers. + // + // A password was not provided. A password is a required when installing on Domain Controllers. + // CustomAction ProcessDdAgentUserCredentials returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) + // Action ended 1:49:43: ProcessDdAgentUserCredentials. Return value 3. + // So include lots of context to ensure we get the full picture + return FindAllIndexWithContext(regexp.MustCompile("A password was not provided"), bytes, 6, 6) + }, + func(bytes []byte) []TextRange { + // Typically looks like this: + // Info 1603. The file C:\Program Files\Datadog\Datadog Agent\bin\agent\process-agent.exe is being held in use by the following process: Name: process-agent, Id: 4704, Window Title: '(not determined yet)'. Close that application and retry. + // Not much context to be had before and after + return FindAllIndexWithContext(regexp.MustCompile("is being held in use by the following process"), bytes, 1, 1) + }, + func(bytes []byte) []TextRange { + // Typically looks like this: + // Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.StartDDServices + // CustomAction WixFailWhenDeferred returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) + // Action ended 2:11:49: InstallFinalize. Return value 3. + // The important context is the TextRange after the error ("Return value 3") but the previous lines can include some useful information too + return FindAllIndexWithContext(regexp.MustCompile("returned actual error"), bytes, 5, 1) + }, + func(bytes []byte) []TextRange { + // Typically looks like this: + // Action 12:24:00: InstallServices. Installing new services + // InstallServices: Service: + // Error 1923. Service 'Datadog Agent' (datadogagent) could not be installed. Verify that you have sufficient privileges to install system services. + // MSI (s) (54:EC) [12:25:53:886]: Product: Datadog Agent -- Error 1923. Service 'Datadog Agent' (datadogagent) could not be installed. Verify that you have sufficient privileges to install system services. + return FindAllIndexWithContext(regexp.MustCompile("Verify that you have sufficient privileges to install system services"), bytes, 2, 1) + }) +} + +// Run runs msiexec synchronously +func (m *Msiexec) Run() ([]byte, error) { + err := m.Cmd.Run() + // The log file *should not* be too big. Avoid verbose log files. + logFileBytes, err2 := m.openAndProcessLogFile() + err = errors.Join(err, err2) + for _, p := range m.postExecActions { + p() + } + + return logFileBytes, err +} + +// RunAsync runs msiexec asynchronously +func (m *Msiexec) RunAsync(done func([]byte, error)) error { + err := m.Cmd.Start() + if err != nil { + return err + } + go func() { + err := m.Cmd.Wait() + // The log file *should not* be too big. Avoid verbose log files. + logFileBytes, err2 := m.openAndProcessLogFile() + err = errors.Join(err, err2) + for _, p := range m.postExecActions { + p() + } + done(logFileBytes, err) + }() + return nil +} + +// FireAndForget starts msiexec and doesn't wait for it to finish. +// The log file won't be read at the end and post execution actions will not be executed. +func (m *Msiexec) FireAndForget() error { + return m.Cmd.Start() +} + +// Cmd creates a new Msiexec wrapper around cmd.Exec that will call msiexec +func Cmd(options ...MsiexecOption) (*Msiexec, error) { + a := &msiexecArgs{} + for _, opt := range options { + if err := opt(a); err != nil { + return nil, err + } + } + if a.msiAction == "" || a.target == "" { + return nil, fmt.Errorf("argument error") + } + + cmd := &Msiexec{} + if len(a.logFile) == 0 { + tempDir, err := os.MkdirTemp("", "datadog-installer") + if err != nil { + return nil, err + } + a.logFile = path.Join(tempDir, "msi.log") + cmd.postExecActions = append(cmd.postExecActions, func() { + _ = os.RemoveAll(tempDir) + }) + } + args := append(a.additionalArgs, a.msiAction, a.target, "/qn", "MSIFASTINSTALL=7", "/log", a.logFile) + if a.ddagentUserName != "" { + args = append(args, fmt.Sprintf("DDAGENTUSER_NAME=%s", a.ddagentUserName)) + } + + cmd.Cmd = exec.Command("msiexec", args...) + cmd.logFile = a.logFile + + return cmd, nil +} diff --git a/pkg/fleet/internal/msi/msilog.go b/pkg/fleet/internal/msi/msilog.go new file mode 100644 index 0000000000000..47f8ef5f30641 --- /dev/null +++ b/pkg/fleet/internal/msi/msilog.go @@ -0,0 +1,130 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build windows + +// Package msi contains helper functions to work with msi packages +package msi + +import ( + "bytes" + "fmt" + "golang.org/x/text/encoding/unicode" + "io" + "io/fs" + "regexp" + "sort" +) + +// TextRange is a simple struct to represent a range of text in a file. +type TextRange struct { + start int + end int +} + +// FindAllIndexWithContext is similar to FindAllIndex but expands the matched range for a number of lines +// before and after the TextRange (called contextBefore and contextAfter). +func FindAllIndexWithContext(r *regexp.Regexp, input []byte, contextBefore, contextAfter int) []TextRange { + contextBefore = max(contextBefore, 0) + contextAfter = max(contextAfter, 0) + var extractedRanges []TextRange + results := r.FindAllIndex(input, -1) + for _, result := range results { + lineCounter := 0 + charCounter := result[0] + for ; charCounter >= 0; charCounter-- { + if input[charCounter] == '\n' { + lineCounter++ + } + if lineCounter > contextBefore { + break + } + } + lineStart := charCounter + 1 + + lineCounter = 0 + charCounter = result[1] + for ; charCounter < len(input); charCounter++ { + if input[charCounter] == '\n' { + lineCounter++ + } + if lineCounter > contextAfter { + break + } + } + lineEnd := charCounter + + extractedRanges = append(extractedRanges, TextRange{lineStart, lineEnd}) + } + + return extractedRanges +} + +// insert merges newRanges into existingRanges by combining overlapping or adjacent ranges. +func insert(existingRanges, newRanges []TextRange) []TextRange { + // Combine all ranges into a single slice for sorting + allRanges := append(existingRanges, newRanges...) + + // Sort ranges by start value (and end value if starts are equal) + sort.Slice(allRanges, func(i, j int) bool { + if allRanges[i].start == allRanges[j].start { + return allRanges[i].end < allRanges[j].end + } + return allRanges[i].start < allRanges[j].start + }) + + // Merge ranges + var merged []TextRange + for _, current := range allRanges { + // If merged is empty or the current range does not overlap with the last merged range + if len(merged) == 0 || merged[len(merged)-1].end < current.start { + merged = append(merged, current) // Add the current range + } else { + // Overlapping or adjacent: Extend the end of the last merged range + merged[len(merged)-1].end = max(merged[len(merged)-1].end, current.end) + } + } + + return merged +} + +// Combine processes input using multiple logFileProcessors and merges their output ranges. +func Combine(input []byte, processors ...logFileProcessor) []TextRange { + var allRanges []TextRange + + // Collect all ranges from each processor + for _, processor := range processors { + allRanges = append(allRanges, processor(input)...) + } + + // Use the improved insert function to merge all collected ranges + return insert(nil, allRanges) +} + +type logFileProcessor func([]byte) []TextRange + +// processLogFile reads a UTF-16 MSI log file and applies various processors on it +// to retain only the relevant log lines. It combines the various outputs from the processors and +// decorate each range of log lines with a marker ('---') to distinguish them. +func processLogFile(logFile fs.File, processors ...logFileProcessor) ([]byte, error) { + logFileBuffer := bytes.NewBuffer(nil) + _, err := io.Copy(logFileBuffer, logFile) + if err != nil { + return nil, err + } + decodedLogsBytes, err := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder().Bytes(logFileBuffer.Bytes()) + if err != nil { + return nil, err + } + + var output []byte + rangesToSave := Combine(decodedLogsBytes, processors...) + for _, ranges := range rangesToSave { + output = append(output, []byte(fmt.Sprintf("--- %d:%d\r\n", ranges.start, ranges.end))...) + output = append(output, decodedLogsBytes[ranges.start:ranges.end]...) + output = append(output, '\r', '\n', '\r', '\n') + } + return output, nil +} diff --git a/pkg/fleet/internal/msi/msilog_test.go b/pkg/fleet/internal/msi/msilog_test.go new file mode 100644 index 0000000000000..f52ff24f2a65f --- /dev/null +++ b/pkg/fleet/internal/msi/msilog_test.go @@ -0,0 +1,393 @@ +// Unless explicitly stated otherwise all files in this repository are licensed +// under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2016-present Datadog, Inc. + +//go:build windows + +package msi + +import ( + "embed" + "github.com/stretchr/testify/require" + "golang.org/x/text/encoding/unicode" + "regexp" + "strings" + "testing" +) + +//go:embed wixfailwhendeferred.log +//go:embed missing_password_for_dc.log +//go:embed file_in_use.log +//go:embed invalid_credentials.log +//go:embed service_marked_for_deletion.log +var logFilesFS embed.FS + +func TestFindAllIndexWithContext(t *testing.T) { + data, err := logFilesFS.ReadFile("wixfailwhendeferred.log") + require.NoError(t, err) + + r := regexp.MustCompile("returned actual error") + decodedLogsBytes, err := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder().Bytes(data) + require.NoError(t, err) + + matches := FindAllIndexWithContext(r, decodedLogsBytes, 2, 1) + + expectedMatches := []string{ + `Action start 2:10:53: WixRemoveFoldersEx. +WixRemoveFoldersEx: Error 0x80070057: Missing folder property: dd_PROJECTLOCATION_0 for row: RemoveFolderEx +CustomAction WixRemoveFoldersEx returned actual error code 1603 but will be translated to success due to continue marking +Action ended 2:10:53: WixRemoveFoldersEx. Return value 1.`, + `Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.PrepareDecompressPythonDistributions +CA: 02:10:56: PrepareDecompressPythonDistributions. Could not set the progress bar size +CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking +Action ended 2:10:56: PrepareDecompressPythonDistributions. Return value 1.`, + `SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.StartDDServices +CustomAction WixFailWhenDeferred returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) +Action ended 2:11:49: InstallFinalize. Return value 3.`, + } + for i, expectedMatch := range expectedMatches { + text := strings.ReplaceAll(string(decodedLogsBytes[matches[i].start:matches[i].end]), "\r", "") + require.Equal(t, expectedMatch, text) + } +} + +func TestFindAllIndexWithContextOutOfRangeDoesntFail(t *testing.T) { + data, err := logFilesFS.ReadFile("wixfailwhendeferred.log") + require.NoError(t, err) + + r := regexp.MustCompile("returned actual error") + decodedLogsBytes, err := unicode.UTF16(unicode.LittleEndian, unicode.UseBOM).NewDecoder().Bytes(data) + require.NoError(t, err) + + matches := FindAllIndexWithContext(r, decodedLogsBytes, -21, -1435) + expectedMatches := []string{ + `CustomAction WixRemoveFoldersEx returned actual error code 1603 but will be translated to success due to continue marking`, + `CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking`, + `CustomAction WixFailWhenDeferred returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox)`, + } + for i, expectedMatch := range expectedMatches { + text := strings.ReplaceAll(string(decodedLogsBytes[matches[i].start:matches[i].end]), "\r", "") + require.Equal(t, expectedMatch, text) + } +} + +func TestCombineRanges(t *testing.T) { + tests := map[string]struct { + input []logFileProcessor + expected []TextRange + }{ + "overlap left": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{1, 5}} + }, + func(_ []byte) []TextRange { + return []TextRange{{4, 7}} + }}, + expected: []TextRange{{1, 7}}, + }, + "overlap right": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{2, 4}} + }, + func(_ []byte) []TextRange { + return []TextRange{{1, 3}} + }}, + expected: []TextRange{{1, 4}}, + }, + "no overlap": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{2, 4}} + }, + func(_ []byte) []TextRange { + return []TextRange{{5, 10}} + }}, + expected: []TextRange{{2, 4}, {5, 10}}, + }, + "full overlap": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{2, 4}} + }, + func(_ []byte) []TextRange { + return []TextRange{{1, 10}} + }}, + expected: []TextRange{{1, 10}}, + }, + "full overlap inverted": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{1, 10}} + }, + func(_ []byte) []TextRange { + return []TextRange{{2, 4}} + }}, + expected: []TextRange{{1, 10}}, + }, + "test many ranges": { + input: []logFileProcessor{ + func(_ []byte) []TextRange { + return []TextRange{{16067, 16421}} + }, + func(_ []byte) []TextRange { + return []TextRange{{19659, 20140}} + }, + func(_ []byte) []TextRange { + return []TextRange{{16002, 16359}} + }, + func(_ []byte) []TextRange { + return []TextRange{{19559, 19951}} + }, + func(_ []byte) []TextRange { + return []TextRange{{59421, 59556}} + }}, + expected: []TextRange{{16002, 16421}, {19559, 20140}, {59421, 59556}}, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + require.Equal(t, test.expected, Combine(nil, test.input...)) + }) + } +} + +func TestReadLogFile(t *testing.T) { + m := &Msiexec{} + + tests := map[string]struct { + input string + expected string + }{ + "Wix built-in failure mode": { + input: "wixfailwhendeferred.log", + expected: `--- 1547:2038 +Action ended 2:10:53: LaunchConditions. Return value 1. +Action start 2:10:53: ValidateProductID. +Action ended 2:10:53: ValidateProductID. Return value 1. +Action start 2:10:53: WixRemoveFoldersEx. +WixRemoveFoldersEx: Error 0x80070057: Missing folder property: dd_PROJECTLOCATION_0 for row: RemoveFolderEx +CustomAction WixRemoveFoldersEx returned actual error code 1603 but will be translated to success due to continue marking +Action ended 2:10:53: WixRemoveFoldersEx. Return value 1. + +--- 6770:7391 +Action start 2:10:55: PrepareDecompressPythonDistributions. +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSIB32B.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.PrepareDecompressPythonDistributions +CA: 02:10:56: PrepareDecompressPythonDistributions. Could not set the progress bar size +CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking +Action ended 2:10:56: PrepareDecompressPythonDistributions. Return value 1. + +--- 16002:16421 +CA: 02:11:30: AddUser. ddagentuser already exists, not creating +CA: 02:11:30: GetPreviousAgentUser. Could not find previous agent user: System.Exception: Agent user information is not in registry + at Datadog.CustomActions.InstallStateCustomActions.GetPreviousAgentUser(ISession session, IRegistryServices registryServices, INativeMethods nativeMethods) +CA: 02:11:30: ConfigureUser. Resetting ddagentuser password. + +--- 19559:20140 +CA: 02:11:36: ConfigureServiceUsers. Configuring services with account WIN-ST17FJ32SOG\ddagentuser +CA: 02:11:36: GetPreviousAgentUser. Could not find previous agent user: System.Exception: Agent user information is not in registry + at Datadog.CustomActions.InstallStateCustomActions.GetPreviousAgentUser(ISession session, IRegistryServices registryServices, INativeMethods nativeMethods) +CA: 02:11:36: UpdateAndLogAccessControl. datadog-process-agent current ACLs: D:(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA) + +--- 24843:25383 +CA(ddprocmon): DriverInstall: Done with create() 0 +CA(ddprocmon): DriverInstall: done installing services +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI593F.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.StartDDServices +CustomAction WixFailWhenDeferred returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) +Action ended 2:11:49: InstallFinalize. Return value 3. + +`, + }, + "Missing password for DC": { + input: "missing_password_for_dc.log", + expected: `--- 3625:5242 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.ProcessDdAgentUserCredentials +CA: 01:49:43: LookupAccountWithExtendedDomainSyntax. User not found, trying again with fixed domain part: \toto +CA: 01:49:43: ProcessDdAgentUserCredentials. User toto doesn't exist. +CA: 01:49:43: ProcessDdAgentUserCredentials. domain part is empty, using default +CA: 01:49:43: ProcessDdAgentUserCredentials. Installing with DDAGENTUSER_PROCESSED_NAME=toto and DDAGENTUSER_PROCESSED_DOMAIN=datadoghq-qa-labs.local +CA: 01:49:43: HandleProcessDdAgentUserCredentialsException. Error processing ddAgentUser credentials: Datadog.CustomActions.InvalidAgentUserConfigurationException: A password was not provided. A password is a required when installing on Domain Controllers. + at Datadog.CustomActions.ProcessUserCustomActions.ProcessDdAgentUserCredentials(Boolean calledFromUIControl) +MSI (s) (C8!50) [01:49:43:906]: Product: Datadog Agent -- A password was not provided. A password is a required when installing on Domain Controllers. + +A password was not provided. A password is a required when installing on Domain Controllers. +CustomAction ProcessDdAgentUserCredentials returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) +Action ended 1:49:43: ProcessDdAgentUserCredentials. Return value 3. +Action ended 1:49:43: INSTALL. Return value 3. +Property(S): UpgradeCode = {0C50421B-AEFB-4F15-A809-7AF256D608A5} +Property(S): NETFRAMEWORK45 = #528449 +Property(S): WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 + +`, + }, + "File in use": { + input: "file_in_use.log", + expected: `--- 3557:3890 +Action start 1:45:18: InstallValidate. +Info 1603. The file C:\Program Files\Datadog\Datadog Agent\bin\agent\process-agent.exe is being held in use by the following process: Name: process-agent, Id: 4704, Window Title: '(not determined yet)'. Close that application and retry. +Action ended 1:45:21: InstallValidate. Return value 1. + +--- 5653:5849 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.PrerequisitesCustomActions.EnsureAdminCaller +Action ended 1:45:22: RunAsAdmin. Return value 1. + +--- 5983:6218 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.InstallStateCustomActions.ReadInstallState +CA: 01:45:23: RegistryProperty. Found DDAGENTUSER_NAME in registry DDOG-HQ-QA-LABS\ddGmsa$ + +--- 7338:7520 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.ConfigCustomActions.ReadConfig +Action ended 1:45:25: ReadConfig. Return value 1. + +--- 7627:7960 +Action start 1:45:25: InstallValidate. +Info 1603. The file C:\Program Files\Datadog\Datadog Agent\bin\agent\process-agent.exe is being held in use by the following process: Name: process-agent, Id: 4704, Window Title: '(not determined yet)'. Close that application and retry. +Action ended 1:45:27: InstallValidate. Return value 1. + +--- 12929:13180 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.Rollback.RestoreDaclRollbackCustomAction.DoRollback +CA: 01:45:35: DoRollback. Resetting inheritance flag on "C:\Program Files\Datadog\Datadog Agent\" + +--- 13273:13484 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.ServiceCustomAction.StopDDServices +CA: 01:45:39: StopDDServices. Service datadog-system-probe status: Stopped + +--- 14824:15093 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action CustomActions!Datadog.CustomActions.ConfigureUserCustomActions.UninstallUser +CA: 01:45:42: UninstallUser. Removing file access for DDOG-HQ-QA-LABS\ddGmsa$ (S-1-5-21-3647231507-2031390810-2876811253-1605) + +--- 464565:465186 +Action start 1:46:16: PrepareDecompressPythonDistributions. +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI58A1.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.PrepareDecompressPythonDistributions +CA: 01:46:17: PrepareDecompressPythonDistributions. Could not set the progress bar size +CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking +Action ended 1:46:17: PrepareDecompressPythonDistributions. Return value 1. + +`, + }, + "Invalid credentials": { + input: "invalid_credentials.log", + expected: `--- 7263:7883 +Action start 1:50:19: PrepareDecompressPythonDistributions. +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSID56.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.PrepareDecompressPythonDistributions +CA: 01:50:19: PrepareDecompressPythonDistributions. Could not set the progress bar size +CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking +Action ended 1:50:19: PrepareDecompressPythonDistributions. Return value 1. + +--- 25259:26729 +CA: 01:50:48: StoreAgentUserInRegistry. Storing installedUser=ddagentuser +CA(ddnpm): DriverInstall: Initialized +CA(ddnpm): DriverInstall: Installing services +CA(ddnpm): DriverInstall: installing service +CA(ddnpm): DriverInstall: serviceDef::create() +CA(ddnpm): DriverInstall: Failed to CreateService 1073 +CA(ddnpm): DriverInstall: Service exists, verifying +CA(ddnpm): DriverInstall: Updated path for existing service +CA(ddnpm): DriverInstall: done installing services +CA(ddapm): DriverInstall: Initialized +CA(ddapm): DriverInstall: Installing services +CA(ddapm): DriverInstall: installing service +CA(ddapm): DriverInstall: serviceDef::create() +CA(ddapm): DriverInstall: Failed to CreateService 1073 +CA(ddapm): DriverInstall: Service exists, verifying +CA(ddapm): DriverInstall: Updated path for existing service +CA(ddapm): DriverInstall: done installing services +CA(ddprocmon): DriverInstall: Initialized +CA(ddprocmon): DriverInstall: Installing services +CA(ddprocmon): DriverInstall: installing service +CA(ddprocmon): DriverInstall: serviceDef::create() +CA(ddprocmon): DriverInstall: Failed to CreateService 1073 +CA(ddprocmon): DriverInstall: Service exists, verifying +CA(ddprocmon): DriverInstall: Updated path for existing service +CA(ddprocmon): DriverInstall: done installing services +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI7FD3.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 + +--- 26730:27404 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.StartDDServices +CA: 01:50:49: StartDDServices. Failed to start services: System.InvalidOperationException: Cannot start service datadogagent on computer '.'. ---> System.ComponentModel.Win32Exception: The service did not start due to a logon failure + --- End of inner exception stack trace --- + at System.ServiceProcess.ServiceController.Start(String args) + at Datadog.CustomActions.Native.ServiceController.StartService(String serviceName, TimeSpan timeout) + at Datadog.CustomActions.ServiceCustomAction.StartDDServices() +Action ended 1:50:49: InstallFinalize. Return value 1. + +`, + }, + "Service marked for deletion": { + input: "service_marked_for_deletion.log", + expected: `--- 1546:2037 +Action ended 6:11:36: LaunchConditions. Return value 1. +Action start 6:11:36: ValidateProductID. +Action ended 6:11:36: ValidateProductID. Return value 1. +Action start 6:11:36: WixRemoveFoldersEx. +WixRemoveFoldersEx: Error 0x80070057: Missing folder property: dd_PROJECTLOCATION_0 for row: RemoveFolderEx +CustomAction WixRemoveFoldersEx returned actual error code 1603 but will be translated to success due to continue marking +Action ended 6:11:36: WixRemoveFoldersEx. Return value 1. + +--- 6876:7497 +Action start 6:11:38: PrepareDecompressPythonDistributions. +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI7CB7.tmp-\ +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.PrepareDecompressPythonDistributions +CA: 06:11:39: PrepareDecompressPythonDistributions. Could not set the progress bar size +CustomAction PrepareDecompressPythonDistributions returned actual error code 1603 but will be translated to success due to continue marking +Action ended 6:11:39: PrepareDecompressPythonDistributions. Return value 1. + +--- 16762:17181 +CA: 06:12:16: AddUser. ddagentuser already exists, not creating +CA: 06:12:16: GetPreviousAgentUser. Could not find previous agent user: System.Exception: Agent user information is not in registry + at Datadog.CustomActions.InstallStateCustomActions.GetPreviousAgentUser(ISession session, IRegistryServices registryServices, INativeMethods nativeMethods) +CA: 06:12:16: ConfigureUser. Resetting ddagentuser password. + +--- 19832:20285 + } +] +MSI (s) (B4:24) [06:12:21:764]: Product: Datadog Agent -- Error 1923. Service 'Datadog Agent' (datadogagent) could not be installed. Verify that you have sufficient privileges to install system services. + +Error 1923. Service 'Datadog Agent' (datadogagent) could not be installed. Verify that you have sufficient privileges to install system services. +SFXCA: Extracting custom action to temporary directory: C:\Windows\Installer\MSI2787.tmp-\ + +--- 20286:20851 +SFXCA: Binding to CLR version v4.0.30319 +Calling custom action AgentCustomActions!Datadog.AgentCustomActions.CustomActions.EnsureNpmServiceDependency +ExecServiceConfig: Error 0x80070430: Cannot change service configuration. Error: The specified service has been marked for deletion. + +ExecServiceConfig: Error 0x80070430: Failed to configure service: datadogagent +CustomAction ExecServiceConfig returned actual error code 1603 (note this may not be 100% accurate if translation happened inside sandbox) +Action ended 6:12:22: InstallFinalize. Return value 3. + +`, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + file, err := logFilesFS.Open(test.input) + require.NoError(t, err) + got, err := m.processLogFile(file) + require.NoError(t, err) + resultString := strings.ReplaceAll(string(got), "\r", "") + require.Equal(t, test.expected, resultString) + }) + } +} diff --git a/pkg/fleet/installer/packages/msiexec.go b/pkg/fleet/internal/msi/product.go similarity index 59% rename from pkg/fleet/installer/packages/msiexec.go rename to pkg/fleet/internal/msi/product.go index e65692bb38c8f..e87144d748f9d 100644 --- a/pkg/fleet/installer/packages/msiexec.go +++ b/pkg/fleet/internal/msi/product.go @@ -5,34 +5,14 @@ //go:build windows -package packages +package msi import ( "fmt" - "os/exec" - "path/filepath" - "github.com/DataDog/datadog-agent/pkg/fleet/internal/paths" - "github.com/DataDog/datadog-agent/pkg/util/log" "golang.org/x/sys/windows/registry" ) -func msiexec(target, product, operation string, args []string) (*exec.Cmd, error) { - updaterPath := filepath.Join(paths.PackagesPath, product, target) - msis, err := filepath.Glob(filepath.Join(updaterPath, fmt.Sprintf("%s-*-1-x86_64.msi", product))) - if err != nil { - return nil, err - } - if len(msis) > 1 { - return nil, fmt.Errorf("too many MSIs in package") - } else if len(msis) == 0 { - return nil, fmt.Errorf("no MSIs in package") - } - - cmd := exec.Command("msiexec", append([]string{operation, msis[0], "/qn", "MSIFASTINSTALL=7"}, args...)...) - return cmd, nil -} - // Product represents a software from the Windows Registry type Product struct { // Code is the software product code @@ -41,7 +21,8 @@ type Product struct { UninstallString string } -func findProductCode(name string) (*Product, error) { +// FindProductCode looks for the productName in the registry and returns information about it +func FindProductCode(productName string) (*Product, error) { rootPath := "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall" reg, err := registry.OpenKey(registry.LOCAL_MACHINE, rootPath, registry.ENUMERATE_SUB_KEYS) if err != nil { @@ -53,55 +34,55 @@ func findProductCode(name string) (*Product, error) { return nil, err } for _, key := range keys { - product, err := processKey(rootPath, key, name) + product, err := processKey(rootPath, key, productName) if err == nil && product != nil { - return product, err + return product, nil } } - return nil, nil + return nil, fmt.Errorf("product not found") } -func processKey(rootPath, key, name string) (*Product, error) { - subkey, err := registry.OpenKey(registry.LOCAL_MACHINE, rootPath+"\\"+key, registry.QUERY_VALUE) +// IsProductInstalled returns true if the given productName is installed +func IsProductInstalled(productName string) bool { + product, err := FindProductCode(productName) if err != nil { - return nil, err - } - defer subkey.Close() - - displayName, _, err := subkey.GetStringValue("DisplayName") - if err == nil && displayName == name { - product := &Product{} - product.UninstallString, _, _ = subkey.GetStringValue("UninstallString") - product.Code = key - return product, nil + return false } - - return nil, nil + return product != nil } -// removeProduct uses the registry to try and find a product and use msiexec to remove it. +// RemoveProduct uses the registry to try and find a product and use msiexec to remove it. // It is different from msiexec in that it uses the registry and not the stable/experiment path on disk to // uninstall the product. // This is needed because in certain circumstances the installer database stored in the stable/experiment paths does not // reflect the installed version, and using those installers can lead to undefined behavior (either failure to uninstall, // or weird bugs from uninstalling a product with an installer from a different version). -func removeProduct(productName string) error { - log.Debugf("Removing product %s", productName) - product, err := findProductCode(productName) +func RemoveProduct(productName string) error { + cmd, err := Cmd(Uninstall(), WithProduct(productName)) if err != nil { - return fmt.Errorf("error trying to find product %s: %w", productName, err) + return fmt.Errorf("failed to remove product: %w", err) } - if product != nil { - cmd := exec.Command("msiexec", "/x", product.Code, "/qn", "MSIFASTINSTALL=7") - return cmd.Run() + output, err := cmd.Run() + if err != nil { + return fmt.Errorf("failed to remove product: %w\n%s", err, string(output)) } - return fmt.Errorf("product %s not found", productName) + return nil } -func isProductInstalled(productName string) bool { - product, err := findProductCode(productName) +func processKey(rootPath, key, name string) (*Product, error) { + subkey, err := registry.OpenKey(registry.LOCAL_MACHINE, rootPath+"\\"+key, registry.QUERY_VALUE) if err != nil { - return false + return nil, err } - return product != nil + defer subkey.Close() + + displayName, _, err := subkey.GetStringValue("DisplayName") + if err == nil && displayName == name { + product := &Product{} + product.UninstallString, _, _ = subkey.GetStringValue("UninstallString") + product.Code = key + return product, nil + } + + return nil, nil } diff --git a/pkg/fleet/internal/msi/service_marked_for_deletion.log b/pkg/fleet/internal/msi/service_marked_for_deletion.log new file mode 100644 index 0000000000000000000000000000000000000000..7844b7a546931e3104ddd6eb35d7b6c32e2942e3 GIT binary patch literal 108018 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 LiOev*kdgln9pZqz literal 0 HcmV?d00001 diff --git a/pkg/fleet/internal/msi/wixfailwhendeferred.log b/pkg/fleet/internal/msi/wixfailwhendeferred.log new file mode 100644 index 0000000000000000000000000000000000000000..fb1982434be9b3a068f8fd1f90ae5ecff8f5ba70 GIT binary patch literal 128276 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 Y6Hi!{yJz2R#%QMW&1%$#J;u-f2c?K}MF0Q* literal 0 HcmV?d00001 From f215538d98687c4ca0d5620cd7750873446aeb2c Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 16 Dec 2024 19:20:16 -0500 Subject: [PATCH 48/78] [ebpfless] Separate pending connection map, handle closed connections (#32158) --- .../tracer/connection/ebpfless/map_utils.go | 19 +++ .../connection/ebpfless/map_utils_test.go | 38 ++++++ .../connection/ebpfless/tcp_processor.go | 111 +++++++++++++----- .../connection/ebpfless/tcp_processor_test.go | 60 ++++------ .../tracer/connection/ebpfless/tcp_utils.go | 42 +++++-- .../tracer/connection/ebpfless_tracer.go | 48 ++++++-- 6 files changed, 232 insertions(+), 86 deletions(-) create mode 100644 pkg/network/tracer/connection/ebpfless/map_utils.go create mode 100644 pkg/network/tracer/connection/ebpfless/map_utils_test.go diff --git a/pkg/network/tracer/connection/ebpfless/map_utils.go b/pkg/network/tracer/connection/ebpfless/map_utils.go new file mode 100644 index 0000000000000..3e41f4832707d --- /dev/null +++ b/pkg/network/tracer/connection/ebpfless/map_utils.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. + +//go:build linux + +package ebpfless + +// WriteMapWithSizeLimit updates a map via m[key] = val. +// However, if the map would overflow sizeLimit, it returns false instead. +func WriteMapWithSizeLimit[Key comparable, Val any](m map[Key]Val, key Key, val Val, sizeLimit int) bool { + _, exists := m[key] + if !exists && len(m) >= sizeLimit { + return false + } + m[key] = val + return true +} diff --git a/pkg/network/tracer/connection/ebpfless/map_utils_test.go b/pkg/network/tracer/connection/ebpfless/map_utils_test.go new file mode 100644 index 0000000000000..e387328d00610 --- /dev/null +++ b/pkg/network/tracer/connection/ebpfless/map_utils_test.go @@ -0,0 +1,38 @@ +// 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. + +//go:build linux + +package ebpfless + +import ( + "github.com/stretchr/testify/require" + "testing" +) + +func TestWriteMapWithSizeLimit(t *testing.T) { + m := map[string]int{} + + // not full: any write should work + ok := WriteMapWithSizeLimit(m, "foo", 123, 1) + require.True(t, ok) + + expectedFoo := map[string]int{ + "foo": 123, + } + require.Equal(t, expectedFoo, m) + + // full: shouldn't write a new key + ok = WriteMapWithSizeLimit(m, "bar", 456, 1) + require.False(t, ok) + require.Equal(t, expectedFoo, m) + + // full: replacing key should still work + ok = WriteMapWithSizeLimit(m, "foo", 789, 1) + require.True(t, ok) + require.Equal(t, map[string]int{ + "foo": 789, + }, m) +} diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor.go b/pkg/network/tracer/connection/ebpfless/tcp_processor.go index 8d530fc3cd70a..c83a5430b96f1 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor.go @@ -18,6 +18,7 @@ import ( "github.com/google/gopacket/layers" "github.com/DataDog/datadog-agent/pkg/network" + "github.com/DataDog/datadog-agent/pkg/network/config" ) type connectionState struct { @@ -61,13 +62,22 @@ func (st *connectionState) hasMissedHandshake() bool { // TCPProcessor encapsulates TCP state tracking for the ebpfless tracer type TCPProcessor struct { - conns map[network.ConnectionTuple]connectionState + cfg *config.Config + // pendingConns contains connections with tcpState == connStatAttempted + pendingConns map[network.ConnectionTuple]*connectionState + // establishedConns contains connections with tcpState == connStatEstablished + establishedConns map[network.ConnectionTuple]*connectionState } +// TODO make this into a config value +const maxPendingConns = 4096 + // NewTCPProcessor constructs an empty TCPProcessor -func NewTCPProcessor() *TCPProcessor { +func NewTCPProcessor(cfg *config.Config) *TCPProcessor { return &TCPProcessor{ - conns: map[network.ConnectionTuple]connectionState{}, + cfg: cfg, + pendingConns: make(map[network.ConnectionTuple]*connectionState, maxPendingConns), + establishedConns: make(map[network.ConnectionTuple]*connectionState, cfg.MaxTrackedConnections), } } @@ -133,7 +143,7 @@ func (t *TCPProcessor) updateSynFlag(conn *network.ConnectionStats, st *connecti if st.tcpState == connStatAttempted && st.localSynState.isSynAcked() && st.remoteSynState.isSynAcked() { st.tcpState = connStatEstablished if st.hasMissedHandshake() { - statsTelemetry.missedTCPConnections.Inc() + statsTelemetry.missedTCPHandshakes.Inc() } else { conn.Monotonic.TCPEstablished++ } @@ -241,13 +251,13 @@ func (t *TCPProcessor) updateRstFlag(conn *network.ConnectionStats, st *connecti // Process handles a TCP packet, calculating stats and keeping track of its state according to the // TCP state machine. -func (t *TCPProcessor) Process(conn *network.ConnectionStats, timestampNs uint64, pktType uint8, ip4 *layers.IPv4, ip6 *layers.IPv6, tcp *layers.TCP) error { +func (t *TCPProcessor) Process(conn *network.ConnectionStats, timestampNs uint64, pktType uint8, ip4 *layers.IPv4, ip6 *layers.IPv6, tcp *layers.TCP) (ProcessResult, error) { if pktType != unix.PACKET_OUTGOING && pktType != unix.PACKET_HOST { - return fmt.Errorf("TCPProcessor saw invalid pktType: %d", pktType) + return ProcessResultNone, fmt.Errorf("TCPProcessor saw invalid pktType: %d", pktType) } payloadLen, err := TCPPayloadLen(conn.Family, ip4, ip6, tcp) if err != nil { - return err + return ProcessResultNone, err } log.TraceFunc(func() string { @@ -256,31 +266,76 @@ func (t *TCPProcessor) Process(conn *network.ConnectionStats, timestampNs uint64 // skip invalid packets we don't recognize: if checkInvalidTCP(tcp) { - return nil + return ProcessResultNone, nil + } + + st := t.getConn(conn.ConnectionTuple) + origState := st.tcpState + + t.updateSynFlag(conn, st, pktType, tcp, payloadLen) + t.updateTCPStats(conn, st, pktType, tcp, payloadLen, timestampNs) + t.updateFinFlag(conn, st, pktType, tcp, payloadLen) + t.updateRstFlag(conn, st, pktType, tcp, payloadLen) + + stateChanged := st.tcpState != origState + if stateChanged { + ok := t.moveConn(conn.ConnectionTuple, st) + // if the map is full then we are unable to move the connection, report that + if !ok { + return ProcessResultMapFull, nil + } } - st := t.conns[conn.ConnectionTuple] + // if the connection is still established, we should update the connection map + if st.tcpState == connStatEstablished { + return ProcessResultStoreConn, nil + } + // if the connection just closed, store it in the tracer's closeCallback + if st.tcpState == connStatClosed && stateChanged { + return ProcessResultCloseConn, nil + } + return ProcessResultNone, nil +} - t.updateSynFlag(conn, &st, pktType, tcp, payloadLen) - t.updateTCPStats(conn, &st, pktType, tcp, payloadLen, timestampNs) - t.updateFinFlag(conn, &st, pktType, tcp, payloadLen) - t.updateRstFlag(conn, &st, pktType, tcp, payloadLen) +func (t *TCPProcessor) getConn(tuple network.ConnectionTuple) *connectionState { + if st, ok := t.establishedConns[tuple]; ok { + return st + } + if st, ok := t.pendingConns[tuple]; ok { + return st + } + // otherwise, create a fresh state object that will be stored by moveConn later + return &connectionState{} +} - t.conns[conn.ConnectionTuple] = st - return nil +// RemoveConn clears a ConnectionTuple from its internal state. +func (t *TCPProcessor) RemoveConn(tuple network.ConnectionTuple) { + delete(t.pendingConns, tuple) + delete(t.establishedConns, tuple) } -// HasConnEverEstablished is used to avoid a connection appearing before the three-way handshake is complete. -// This is done to mirror the behavior of ebpf tracing accept() and connect(), which both return -// after the handshake is completed. -func (t *TCPProcessor) HasConnEverEstablished(conn *network.ConnectionStats) bool { - st := t.conns[conn.ConnectionTuple] - - // conn.Monotonic.TCPEstablished can be 0 even though isEstablished is true, - // because pre-existing connections don't increment TCPEstablished. - // That's why we use tcpState instead of conn - isEstablished := st.tcpState == connStatEstablished - // if the connection has closed in any way, report that too - hasEverClosed := conn.Monotonic.TCPClosed > 0 || len(conn.TCPFailures) > 0 - return isEstablished || hasEverClosed +// moveConn moves a connection to the correct map based on its tcpState. +// If it had to drop the connection because the target map was full, it returns false. +func (t *TCPProcessor) moveConn(tuple network.ConnectionTuple, st *connectionState) bool { + t.RemoveConn(tuple) + + switch st.tcpState { + // For this case, simply let closed connections disappear. Process() will return + // ProcessResultCloseConn letting the ebpfless tracer know the connection has closed. + case connStatClosed: + case connStatAttempted: + ok := WriteMapWithSizeLimit(t.pendingConns, tuple, st, maxPendingConns) + if !ok { + statsTelemetry.droppedPendingConns.Inc() + } + return ok + case connStatEstablished: + maxTrackedConns := int(t.cfg.MaxTrackedConnections) + ok := WriteMapWithSizeLimit(t.establishedConns, tuple, st, maxTrackedConns) + if !ok { + statsTelemetry.droppedEstablishedConns.Inc() + } + return ok + } + return true } diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go index f19729579a6bc..dea6090eb121d 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go @@ -8,6 +8,7 @@ package ebpfless import ( + "github.com/DataDog/datadog-agent/pkg/network/config" "net" "syscall" "testing" @@ -166,19 +167,21 @@ func (pb packetBuilder) outgoing(payloadLen uint16, relSeq, relAck uint32, flags } func newTCPTestFixture(t *testing.T) *tcpTestFixture { + cfg := config.New() return &tcpTestFixture{ t: t, - tcp: NewTCPProcessor(), + tcp: NewTCPProcessor(cfg), conn: nil, } } -func (fixture *tcpTestFixture) runPkt(pkt testCapture) { +func (fixture *tcpTestFixture) runPkt(pkt testCapture) ProcessResult { if fixture.conn == nil { fixture.conn = makeTCPStates(pkt) } - err := fixture.tcp.Process(fixture.conn, pkt.timestampNs, pkt.pktType, pkt.ipv4, pkt.ipv6, pkt.tcp) + result, err := fixture.tcp.Process(fixture.conn, pkt.timestampNs, pkt.pktType, pkt.ipv4, pkt.ipv6, pkt.tcp) require.NoError(fixture.t, err) + return result } func (fixture *tcpTestFixture) runPkts(packets []testCapture) { @@ -197,7 +200,7 @@ func (fixture *tcpTestFixture) runAgainstState(packets []testCapture, expected [ fixture.runPkt(pkt) connTuple := fixture.conn.ConnectionTuple - actual := fixture.tcp.conns[connTuple].tcpState + actual := fixture.tcp.getConn(connTuple).tcpState actualStrs = append(actualStrs, labelForState(actual)) } require.Equal(fixture.t, expectedStrs, actualStrs) @@ -255,6 +258,9 @@ func testBasicHandshake(t *testing.T, pb packetBuilder) { } require.Equal(t, expectedStats, f.conn.Monotonic) + + require.Empty(t, f.tcp.pendingConns) + require.Empty(t, f.tcp.establishedConns) } var lowerSeq uint32 = 2134452051 @@ -322,6 +328,9 @@ func testReversedBasicHandshake(t *testing.T, pb packetBuilder) { TCPClosed: 1, } require.Equal(t, expectedStats, f.conn.Monotonic) + + require.Empty(t, f.tcp.pendingConns) + require.Empty(t, f.tcp.establishedConns) } func TestReversedBasicHandshake(t *testing.T) { @@ -614,9 +623,8 @@ func TestConnReset(t *testing.T) { require.Equal(t, expectedStats, f.conn.Monotonic) } -func TestConnectTwice(t *testing.T) { - // same as TestImmediateFin but everything happens twice - +func TestProcessResult(t *testing.T) { + // same as TestImmediateFin but checks ProcessResult pb := newPacketBuilder(lowerSeq, higherSeq) basicHandshake := []testCapture{ pb.incoming(0, 0, 0, SYN), @@ -628,40 +636,20 @@ func TestConnectTwice(t *testing.T) { pb.outgoing(0, 2, 2, ACK), } - expectedClientStates := []connStatus{ - connStatAttempted, - connStatAttempted, - connStatEstablished, - // active close begins here - connStatEstablished, - connStatEstablished, - connStatClosed, + processResults := []ProcessResult{ + ProcessResultNone, + ProcessResultNone, + ProcessResultStoreConn, + ProcessResultStoreConn, + ProcessResultStoreConn, + ProcessResultCloseConn, } f := newTCPTestFixture(t) - f.runAgainstState(basicHandshake, expectedClientStates) - - state := f.tcp.conns[f.conn.ConnectionTuple] - // make sure the TCP state was erased after the connection was closed - require.Equal(t, connectionState{ - tcpState: connStatClosed, - }, state) - - // second connection here - f.runAgainstState(basicHandshake, expectedClientStates) - - require.Empty(t, f.conn.TCPFailures) - expectedStats := network.StatCounters{ - SentBytes: 0, - RecvBytes: 0, - SentPackets: 3 * 2, - RecvPackets: 3 * 2, - Retransmits: 0, - TCPEstablished: 1 * 2, - TCPClosed: 1 * 2, + for i, pkt := range basicHandshake { + require.Equal(t, processResults[i], f.runPkt(pkt), "packet #%d has the wrong ProcessResult", i) } - require.Equal(t, expectedStats, f.conn.Monotonic) } func TestSimultaneousClose(t *testing.T) { diff --git a/pkg/network/tracer/connection/ebpfless/tcp_utils.go b/pkg/network/tracer/connection/ebpfless/tcp_utils.go index 9ebe4d778969d..4abe627a995d5 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_utils.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_utils.go @@ -21,18 +21,40 @@ import ( const ebpflessModuleName = "ebpfless_network_tracer" +// ProcessResult represents what the ebpfless tracer should do with ConnectionStats after processing a packet +type ProcessResult uint8 + +const ( + // ProcessResultNone - the updated ConnectionStats should NOT be stored in the connection map. + // Usually, this is because the connection is not established yet. + ProcessResultNone ProcessResult = iota + // ProcessResultStoreConn - the updated ConnectionStats should be stored in the connection map. + // This happens when the connection is established. + ProcessResultStoreConn + // ProcessResultCloseConn - this connection is done and its ConnectionStats should be passed + // to the ebpfless tracer's closed connection handler. + ProcessResultCloseConn + // ProcessResultMapFull - this connection can't be tracked because the TCPProcessor's connection + // map is full. This connection should be removed from the tracer as well. + ProcessResultMapFull +) + var statsTelemetry = struct { - missedTCPConnections telemetry.Counter - missingTCPFlags telemetry.Counter - tcpSynAndFin telemetry.Counter - tcpRstAndSyn telemetry.Counter - tcpRstAndFin telemetry.Counter + droppedPendingConns telemetry.Counter + droppedEstablishedConns telemetry.Counter + missedTCPHandshakes telemetry.Counter + missingTCPFlags telemetry.Counter + tcpSynAndFin telemetry.Counter + tcpRstAndSyn telemetry.Counter + tcpRstAndFin telemetry.Counter }{ - telemetry.NewCounter(ebpflessModuleName, "missed_tcp_connections", []string{}, "Counter measuring the number of TCP connections where we missed the SYN handshake"), - telemetry.NewCounter(ebpflessModuleName, "missing_tcp_flags", []string{}, "Counter measuring packets encountered with none of SYN, FIN, ACK, RST set"), - telemetry.NewCounter(ebpflessModuleName, "tcp_syn_and_fin", []string{}, "Counter measuring packets encountered with SYN+FIN together"), - telemetry.NewCounter(ebpflessModuleName, "tcp_rst_and_syn", []string{}, "Counter measuring packets encountered with RST+SYN together"), - telemetry.NewCounter(ebpflessModuleName, "tcp_rst_and_fin", []string{}, "Counter measuring packets encountered with RST+FIN together"), + droppedPendingConns: telemetry.NewCounter(ebpflessModuleName, "dropped_pending_conns", nil, "Counter measuring the number of TCP connections which were dropped during the handshake (because the map was full)"), + droppedEstablishedConns: telemetry.NewCounter(ebpflessModuleName, "dropped_established_conns", nil, "Counter measuring the number of TCP connections which were dropped while established (because the map was full)"), + missedTCPHandshakes: telemetry.NewCounter(ebpflessModuleName, "missed_tcp_handshakes", nil, "Counter measuring the number of TCP connections where we missed the SYN handshake"), + missingTCPFlags: telemetry.NewCounter(ebpflessModuleName, "missing_tcp_flags", nil, "Counter measuring packets encountered with none of SYN, FIN, ACK, RST set"), + tcpSynAndFin: telemetry.NewCounter(ebpflessModuleName, "tcp_syn_and_fin", nil, "Counter measuring packets encountered with SYN+FIN together"), + tcpRstAndSyn: telemetry.NewCounter(ebpflessModuleName, "tcp_rst_and_syn", nil, "Counter measuring packets encountered with RST+SYN together"), + tcpRstAndFin: telemetry.NewCounter(ebpflessModuleName, "tcp_rst_and_fin", nil, "Counter measuring packets encountered with RST+FIN together"), } const tcpSeqMidpoint = 0x80000000 diff --git a/pkg/network/tracer/connection/ebpfless_tracer.go b/pkg/network/tracer/connection/ebpfless_tracer.go index e4e661c2782a3..88c744239fd39 100644 --- a/pkg/network/tracer/connection/ebpfless_tracer.go +++ b/pkg/network/tracer/connection/ebpfless_tracer.go @@ -40,9 +40,11 @@ const ( var ( ebpfLessTracerTelemetry = struct { - skippedPackets telemetry.Counter + skippedPackets telemetry.Counter + droppedConnections telemetry.Counter }{ telemetry.NewCounter(ebpfLessTelemetryPrefix, "skipped_packets", []string{"reason"}, "Counter measuring skipped packets"), + telemetry.NewCounter(ebpfLessTelemetryPrefix, "dropped_connections", nil, "Counter measuring dropped connections"), } ) @@ -81,7 +83,7 @@ func newEbpfLessTracer(cfg *config.Config) (*ebpfLessTracer, error) { exit: make(chan struct{}), scratchConn: &network.ConnectionStats{}, udp: &udpProcessor{}, - tcp: ebpfless.NewTCPProcessor(), + tcp: ebpfless.NewTCPProcessor(cfg), conns: make(map[network.ConnectionTuple]*network.ConnectionStats, cfg.MaxTrackedConnections), boundPorts: ebpfless.NewBoundPorts(cfg), cookieHasher: newCookieHasher(), @@ -96,7 +98,7 @@ func newEbpfLessTracer(cfg *config.Config) (*ebpfLessTracer, error) { } // Start begins collecting network connection data. -func (t *ebpfLessTracer) Start(_ func(*network.ConnectionStats)) error { +func (t *ebpfLessTracer) Start(closeCallback func(*network.ConnectionStats)) error { if err := t.boundPorts.Start(); err != nil { return fmt.Errorf("could not update bound ports: %w", err) } @@ -123,7 +125,7 @@ func (t *ebpfLessTracer) Start(_ func(*network.ConnectionStats)) error { return nil } - if err := t.processConnection(pktType, &ip4, &ip6, &udp, &tcp, decoded); err != nil { + if err := t.processConnection(pktType, &ip4, &ip6, &udp, &tcp, decoded, closeCallback); err != nil { log.Warnf("could not process packet: %s", err) } @@ -147,8 +149,8 @@ func (t *ebpfLessTracer) processConnection( udp *layers.UDP, tcp *layers.TCP, decoded []gopacket.LayerType, + closeCallback func(*network.ConnectionStats), ) error { - t.scratchConn.Source, t.scratchConn.Dest = util.Address{}, util.Address{} t.scratchConn.SPort, t.scratchConn.DPort = 0, 0 t.scratchConn.TCPFailures = make(map[uint16]uint32) @@ -204,21 +206,25 @@ func (t *ebpfLessTracer) processConnection( if ts, err = ddebpf.NowNanoseconds(); err != nil { return fmt.Errorf("error getting last updated timestamp for connection: %w", err) } + conn.LastUpdateEpoch = uint64(ts) if !ip4Present && !ip6Present { return nil } + + var result ebpfless.ProcessResult switch conn.Type { case network.UDP: if (ip4Present && !t.config.CollectUDPv4Conns) || (ip6Present && !t.config.CollectUDPv6Conns) { return nil } + result = ebpfless.ProcessResultStoreConn err = t.udp.process(conn, pktType, udp) case network.TCP: if (ip4Present && !t.config.CollectTCPv4Conns) || (ip6Present && !t.config.CollectTCPv6Conns) { return nil } - err = t.tcp.Process(conn, uint64(ts), pktType, ip4, ip6, tcp) + result, err = t.tcp.Process(conn, uint64(ts), pktType, ip4, ip6, tcp) default: err = fmt.Errorf("unsupported connection type %d", conn.Type) } @@ -227,15 +233,30 @@ func (t *ebpfLessTracer) processConnection( return fmt.Errorf("error processing connection: %w", err) } - // TODO probably remove HasConnEverEstablished once we handle closed connections properly - if conn.Type == network.UDP || (conn.Type == network.TCP && t.tcp.HasConnEverEstablished(conn)) { - conn.LastUpdateEpoch = uint64(ts) - t.conns[t.scratchConn.ConnectionTuple] = conn - } - log.TraceFunc(func() string { return fmt.Sprintf("connection: %s", conn) }) + + switch result { + case ebpfless.ProcessResultNone: + case ebpfless.ProcessResultStoreConn: + maxTrackedConns := int(t.config.MaxTrackedConnections) + ok := ebpfless.WriteMapWithSizeLimit(t.conns, conn.ConnectionTuple, conn, maxTrackedConns) + if !ok { + // we don't have enough space to add this connection, remove its TCP state tracking + if conn.Type == network.TCP { + t.tcp.RemoveConn(conn.ConnectionTuple) + } + ebpfLessTracerTelemetry.droppedConnections.Inc() + } + case ebpfless.ProcessResultCloseConn: + delete(t.conns, conn.ConnectionTuple) + closeCallback(conn) + case ebpfless.ProcessResultMapFull: + delete(t.conns, conn.ConnectionTuple) + ebpfLessTracerTelemetry.droppedConnections.Inc() + } + return nil } @@ -310,6 +331,9 @@ func (t *ebpfLessTracer) Remove(conn *network.ConnectionStats) error { defer t.m.Unlock() delete(t.conns, conn.ConnectionTuple) + if conn.Type == network.TCP { + t.tcp.RemoveConn(conn.ConnectionTuple) + } return nil } From 51ec10ca20b382377f93528aa913a7028cd2b67a Mon Sep 17 00:00:00 2001 From: Stuart Geipel Date: Mon, 16 Dec 2024 22:05:06 -0500 Subject: [PATCH 49/78] [ebpfless] Add 5 second expiry to pending connections (#32187) --- .../connection/ebpfless/tcp_processor.go | 26 ++++++++++++++++ .../connection/ebpfless/tcp_processor_test.go | 23 ++++++++++++++ .../tracer/connection/ebpfless/tcp_utils.go | 2 ++ .../tracer/connection/ebpfless_tracer.go | 31 ++++++++++++++++--- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor.go b/pkg/network/tracer/connection/ebpfless/tcp_processor.go index c83a5430b96f1..73ea6a1152aa3 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor.go @@ -54,6 +54,13 @@ type connectionState struct { // rttTracker is used to track round trip times rttTracker rttTracker + + // lastUpdateEpoch contains the last timestamp this connection sent/received a packet + // TODO find a way to combine this with ConnectionStats.lastUpdateEpoch + // This exists because connections in pendingConns don't have a ConnectionStats object yet. + // Can we make all connections in TCPProcessor have a ConnectionStats no matter what, and + // filter them out in GetConnections? + lastUpdateEpoch uint64 } func (st *connectionState) hasMissedHandshake() bool { @@ -71,6 +78,7 @@ type TCPProcessor struct { // TODO make this into a config value const maxPendingConns = 4096 +const pendingConnTimeoutNs = uint64(5 * time.Second) // NewTCPProcessor constructs an empty TCPProcessor func NewTCPProcessor(cfg *config.Config) *TCPProcessor { @@ -155,6 +163,7 @@ func (t *TCPProcessor) updateSynFlag(conn *network.ConnectionStats, st *connecti func (t *TCPProcessor) updateTCPStats(conn *network.ConnectionStats, st *connectionState, pktType uint8, tcp *layers.TCP, payloadLen uint16, timestampNs uint64) { nextSeq := calcNextSeq(tcp, payloadLen) + st.lastUpdateEpoch = timestampNs if pktType == unix.PACKET_OUTGOING { conn.Monotonic.SentPackets++ // packetCanRetransmit filters out packets that look like retransmits but aren't, like TCP keepalives @@ -339,3 +348,20 @@ func (t *TCPProcessor) moveConn(tuple network.ConnectionTuple, st *connectionSta } return true } + +// CleanupExpiredPendingConns iterates through pendingConns and removes those that +// have existed too long - in normal TCP, they should become established right away. +// +// This is only required for pendingConns because the tracer already has logic to remove +// established connections (connections that have ConnectionStats) +func (t *TCPProcessor) CleanupExpiredPendingConns(timestampNs uint64) { + for tuple, st := range t.pendingConns { + timeoutTime := st.lastUpdateEpoch + pendingConnTimeoutNs + + if timeoutTime <= timestampNs { + delete(t.pendingConns, tuple) + + statsTelemetry.expiredPendingConns.Inc() + } + } +} diff --git a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go index dea6090eb121d..b7d525bce1531 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_processor_test.go @@ -12,6 +12,7 @@ import ( "net" "syscall" "testing" + "time" "golang.org/x/sys/unix" @@ -792,3 +793,25 @@ func TestPreexistingConn(t *testing.T) { } require.Equal(t, expectedStats, f.conn.Monotonic) } + +func TestPendingConnExpiry(t *testing.T) { + now := uint64(time.Now().UnixNano()) + + pb := newPacketBuilder(lowerSeq, higherSeq) + pkt := pb.outgoing(0, 0, 0, SYN) + pkt.timestampNs = now + + f := newTCPTestFixture(t) + + f.runPkt(pkt) + require.Len(t, f.tcp.pendingConns, 1) + + // if no time has passed, should not remove the connection + f.tcp.CleanupExpiredPendingConns(now) + require.Len(t, f.tcp.pendingConns, 1) + + // if too much time has passed, should remove the connection + tenSecNs := uint64((10 * time.Second).Nanoseconds()) + f.tcp.CleanupExpiredPendingConns(now + tenSecNs) + require.Empty(t, f.tcp.pendingConns) +} diff --git a/pkg/network/tracer/connection/ebpfless/tcp_utils.go b/pkg/network/tracer/connection/ebpfless/tcp_utils.go index 4abe627a995d5..7a30737e734ae 100644 --- a/pkg/network/tracer/connection/ebpfless/tcp_utils.go +++ b/pkg/network/tracer/connection/ebpfless/tcp_utils.go @@ -40,6 +40,7 @@ const ( ) var statsTelemetry = struct { + expiredPendingConns telemetry.Counter droppedPendingConns telemetry.Counter droppedEstablishedConns telemetry.Counter missedTCPHandshakes telemetry.Counter @@ -48,6 +49,7 @@ var statsTelemetry = struct { tcpRstAndSyn telemetry.Counter tcpRstAndFin telemetry.Counter }{ + expiredPendingConns: telemetry.NewCounter(ebpflessModuleName, "expired_pending_conns", nil, "Counter measuring the number of TCP connections which expired because it took too long to complete the handshake"), droppedPendingConns: telemetry.NewCounter(ebpflessModuleName, "dropped_pending_conns", nil, "Counter measuring the number of TCP connections which were dropped during the handshake (because the map was full)"), droppedEstablishedConns: telemetry.NewCounter(ebpflessModuleName, "dropped_established_conns", nil, "Counter measuring the number of TCP connections which were dropped while established (because the map was full)"), missedTCPHandshakes: telemetry.NewCounter(ebpflessModuleName, "missed_tcp_handshakes", nil, "Counter measuring the number of TCP connections where we missed the SYN handshake"), diff --git a/pkg/network/tracer/connection/ebpfless_tracer.go b/pkg/network/tracer/connection/ebpfless_tracer.go index 88c744239fd39..3eb5a03344b2a 100644 --- a/pkg/network/tracer/connection/ebpfless_tracer.go +++ b/pkg/network/tracer/connection/ebpfless_tracer.go @@ -303,6 +303,12 @@ func (t *ebpfLessTracer) GetConnections(buffer *network.ConnectionBuffer, filter t.m.Lock() defer t.m.Unlock() + // use GetConnections to periodically cleanup pending connections + err := t.cleanupPendingConns() + if err != nil { + return err + } + if len(t.conns) == 0 { return nil } @@ -321,20 +327,35 @@ func (t *ebpfLessTracer) GetConnections(buffer *network.ConnectionBuffer, filter return nil } +// cleanupPendingConns removes pending connections from the TCP tracer. +// For more information, refer to CleanupExpiredPendingConns +func (t *ebpfLessTracer) cleanupPendingConns() error { + ts, err := ddebpf.NowNanoseconds() + if err != nil { + return fmt.Errorf("error getting last updated timestamp for connection: %w", err) + } + t.tcp.CleanupExpiredPendingConns(uint64(ts)) + return nil +} + // FlushPending forces any closed connections waiting for batching to be processed immediately. func (t *ebpfLessTracer) FlushPending() {} +func (t *ebpfLessTracer) remove(conn *network.ConnectionStats) error { + delete(t.conns, conn.ConnectionTuple) + if conn.Type == network.TCP { + t.tcp.RemoveConn(conn.ConnectionTuple) + } + return nil +} + // Remove deletes the connection from tracking state. // It does not prevent the connection from re-appearing later, if additional traffic occurs. func (t *ebpfLessTracer) Remove(conn *network.ConnectionStats) error { t.m.Lock() defer t.m.Unlock() - delete(t.conns, conn.ConnectionTuple) - if conn.Type == network.TCP { - t.tcp.RemoveConn(conn.ConnectionTuple) - } - return nil + return t.remove(conn) } // GetMap returns the underlying named map. This is useful if any maps are shared with other eBPF components. From 6a7a2be4e760b1bbf5918648649f18f99a916fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Tue, 17 Dec 2024 08:44:20 +0100 Subject: [PATCH 50/78] omnibus: python: don't attempt to substitute an unset value (#32230) --- omnibus/config/software/python3.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/omnibus/config/software/python3.rb b/omnibus/config/software/python3.rb index 7f8fd8e8f4219..6e5636064bad2 100644 --- a/omnibus/config/software/python3.rb +++ b/omnibus/config/software/python3.rb @@ -54,10 +54,12 @@ # Don't forward CC and CXX to python extensions Makefile, it's quite unlikely that any non default # compiler we use would end up being available in the system/docker image used by customers - if linux_target? + if linux_target? && ENV["CC"] command "sed -i \"s/^CC=[[:space:]]*${CC}/CC=gcc/\" #{install_dir}/embedded/lib/python#{major}.#{minor}/config-3.12-*-linux-gnu/Makefile", :env => env - command "sed -i \"s/^CXX=[[:space:]]*${CXX}/CC=g++/\" #{install_dir}/embedded/lib/python#{major}.#{minor}/config-3.12-*-linux-gnu/Makefile", :env => env command "sed -i \"s/${CC}/gcc/g\" #{install_dir}/embedded/lib/python#{major}.#{minor}/_sysconfigdata__linux_*-linux-gnu.py", :env => env + end + if linux_target? && ENV["CXX"] + command "sed -i \"s/^CXX=[[:space:]]*${CXX}/CC=g++/\" #{install_dir}/embedded/lib/python#{major}.#{minor}/config-3.12-*-linux-gnu/Makefile", :env => env command "sed -i \"s/${CXX}/g++/g\" #{install_dir}/embedded/lib/python#{major}.#{minor}/_sysconfigdata__linux_*-linux-gnu.py", :env => env end delete "#{install_dir}/embedded/lib/python#{major}.#{minor}/test" From 219e2f0bb6e88a678c0e7e4debfdb88137b8f7bb Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 17 Dec 2024 10:06:52 +0100 Subject: [PATCH 51/78] discovery: e2e: Avoid fail on temporary network errors (#32215) --- test/new-e2e/tests/discovery/linux_test.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/new-e2e/tests/discovery/linux_test.go b/test/new-e2e/tests/discovery/linux_test.go index ec7d0aa339be3..0df272712dedd 100644 --- a/test/new-e2e/tests/discovery/linux_test.go +++ b/test/new-e2e/tests/discovery/linux_test.go @@ -170,7 +170,19 @@ func assertRunningCheck(t *assert.CollectT, remoteHost *components.RemoteHost, c func (s *linuxTestSuite) provisionServer() { err := s.Env().RemoteHost.CopyFolder("testdata/provision", "/home/ubuntu/e2e-test") require.NoError(s.T(), err) - s.Env().RemoteHost.MustExecute("sudo bash /home/ubuntu/e2e-test/provision.sh") + + cmd := "sudo bash /home/ubuntu/e2e-test/provision.sh" + _, err = s.Env().RemoteHost.Execute(cmd) + if err != nil { + // Sometimes temporary network errors are seen which cause the provision + // script to fail. + s.T().Log("Retrying provision due to failure", err) + time.Sleep(30 * time.Second) + _, err := s.Env().RemoteHost.Execute(cmd) + if err != nil { + s.T().Skip("Unable to provision server") + } + } } func (s *linuxTestSuite) startServices() { From 89985fb58f415ee49a88a63c3c4cf3d8c6684926 Mon Sep 17 00:00:00 2001 From: "agent-platform-auto-pr[bot]" <153269286+agent-platform-auto-pr[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:08:23 +0000 Subject: [PATCH 52/78] [Backport main] Changelog update for 7.60.0 (#32202) Co-authored-by: chouquette --- CHANGELOG-DCA.rst | 27 ++++++++++ CHANGELOG.rst | 132 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) diff --git a/CHANGELOG-DCA.rst b/CHANGELOG-DCA.rst index 5ed8fdc3430f3..569bda03c6a0d 100644 --- a/CHANGELOG-DCA.rst +++ b/CHANGELOG-DCA.rst @@ -2,6 +2,33 @@ Release Notes ============= +.. _Release Notes_7.60.0: + +7.60.0 +====== + +.. _Release Notes_7.60.0_Prelude: + +Prelude +------- + +Released on: 2024-12-16 +Pinned to datadog-agent v7.60.0: `CHANGELOG `_. + + +.. _Release Notes_7.60.0_Bug Fixes: + +Bug Fixes +--------- + +- Fixes bug where incorrect timestamp would be used for unbundled Kubernetes events. + +- Fixed an issue in the KSM check when it's configured with the option + ``pod_collection_mode`` set to ``node_kubelet``. Previously, the check could + fail to start if there was a timeout while contacting the API server. This + issue has now been resolved. + + .. _Release Notes_7.59.1: 7.59.1 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9dcb70ac5e1e1..ba9f97c930ee8 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,138 @@ Release Notes ============= +.. _Release Notes_7.60.0: + +7.60.0 +====== + +.. _Release Notes_7.60.0_Prelude: + +Prelude +------- + +Release on: 2024-12-16 + +- Please refer to the `7.60.0 tag on integrations-core `_ for the list of changes on the Core Checks + + +.. _Release Notes_7.60.0_Upgrade Notes: + +Upgrade Notes +------------- + +- * Parameter ``peer_tags_aggregation`` (a.k.a. environment variable ``DD_APM_PEER_TAGS_AGGREGATION``) is now enabled by default. This means that aggregation of peer related tags (e.g., `peer.service`, `db.instance`, etc.) now happens in the Agent, which enables statistics for Inferred Entities. If you want to disable this feature, set `peer_tags_aggregation` to `false` in your Agent configuration. + + * Parameter ``compute_stats_by_span_kind`` (a.k.a. environment variable ``DD_APM_COMPUTE_STATS_BY_SPAN_KIND``) is now enabled by default. This means spans with an eligible `span.kind` will have stats computed. If disabled, only top-level and measured spans will have stats computed. If you want to disable this feature, set `compute_stats_by_span_kind` to `false` in your Agent configuration. + + Note: When using ``peer_tags_aggregation`` and ``compute_stats_by_span_kind``, a high cardinality of peer tags or APM resources can contribute to higher CPU and memory consumption. If enabling both causes the Agent to consume too many resources, try disabling `compute_stats_by_span_kind` first. + + It is recommended that you update your tracing libraries according to the instructions `here `_ and set ``DD_TRACE_REMOVE_INTEGRATION_SERVICE_NAMES_ENABLED`` (or ``dd.trace.remove.integration-service-names.enabled``) to ``true``. + +- Upgraded JMXFetch to `0.49.5 `_ which adds support for ``UnloadedClassCount`` metric + and IBM J9 gc metrics. See `0.49.5 `_ for more details. + + +.. _Release Notes_7.60.0_New Features: + +New Features +------------ + +- `Inferred Service dependencies `_ are now Generally Available (exiting Beta) and enabled by default. Inferred Services of all kinds now have trace metrics and are available in dependency maps. `apm_config.peer_tags_aggregation` and `apm_config.compute_stats_by_span_kind` both now default to `true` unless explicitly set to `false`. + +- Add `check_tag_cardinality` parameter config check. + + By default `check_tag_cardinality` is not set which doesn't change the behavior of the checks. + Once it is set in pod annotaions, it overrides the cardinality value provided in the base agent configuration. + Example of usage: + ```yaml + ad.datadoghq.com/redis.checks: | + { + "redisdb": { + "check_tag_cardinality": "high", + "instances": [ + { + "host": "%%host%%", + "port": "6379" + } + ] + } + } + ``` + +- Added a new feature flag `enable_receive_resource_spans_v2` in DD_APM_FEATURES that gates a refactored implementation of ReceiveResourceSpans for OTLP. + + +.. _Release Notes_7.60.0_Enhancement Notes: + +Enhancement Notes +----------------- + +- Added information about where the Agent sourced BTF data for eBPF to the Agent flare. When applicable, this will appear in ``system-probe/ebpf_btf_loader.log``. + +- The Agent flare now returns NAT debug information from conntrack in the ``system-probe`` directory. + +- The ``flare`` subcommand includes a ``--provider-timeout`` option to set a timeout for each file collection (default is 10s), useful for unblocking slow flare creation. + +- This change reduces the number of DNS queries made by Network Traffic + based paths in Network Path. + A cache of reverse DNS lookups is used to reduce the number of DNS + queries. Additionally, reverse DNS lookups are now performed only + for private IPs and not for public IPs. + +- Agent flare now includes system-probe telemetry data via ``system-probe/system_probe_telemetry.log``. + +- The MSI installer uses 7zr.exe to decompress the embedded Python. + +- On Windows, the endpoint /windows_crash_detection/check has been modified to report crashes in + an asynchronous manner, to allow processing of large crash dumps without blocking or timing out. + The first check will return a busy status and continue to do so until the processing is completed. + + +.. _Release Notes_7.60.0_Deprecation Notes: + +Deprecation Notes +----------------- + +- Prebuilt eBPF for the network tracer system-probe module has been + deprecated in favor of CO-RE and runtime compilation variants on Linux + kernel versions 6+ and RHEL kernel versions 5.14+. To continue to use + the prebuilt eBPF network tracer, set + `system_probe_config.allow_prebuilt_fallback` in the + system-probe config file, or set the environment variable + `DD_ALLOW_PREBUILT_FALLBACK`, to `true` on these platforms. + +- The feature flag `service_monitoring_config.enable_http_stats_by_status_code` was deprecated and removed. + No impact on USM's behavior. + + +.. _Release Notes_7.60.0_Bug Fixes: + +Bug Fixes +--------- + +- Fixes an issue added in 7.50 that causes the Windows event log tailer to drop + events if it cannot open their publisher metadata. + +- Fix a bug in the config parser that broke ignored_ip_addresses from working in NDM Autodiscovery. + +- Fixes host tags with a configurable duration so the metric's context hash doesn't change, preventing the aggregator from mistaking it as a new metric. + +- Fix `could not parse voltage fields` error in Nvidia Jetson integration when tegrastats output contains mW units. + +- Fix building of Python extension containing native code. + +- [oracle] Fix broken activity sampling with an external Oracle client. + +- Fix nil pointer error on Oracle DBM query when the check's connection is lost before SELECT statement executes. + +- Fix a regression that caused the Agent to not be able to run if its + capabilities had been modified with the `setcap` command. + +- Fix bug wherein single line truncated logs ended with whitespace characters were not being tagged as truncated. + Fix issue with the truncation message occasionally causing subsequent logs to think they were truncated when they were not (single line logs only). + + .. _Release Notes_7.59.1: 7.59.1 From fb6bdd392ef5ac747c00e5a61404f2a6e8d777ef Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Tue, 17 Dec 2024 10:28:17 +0100 Subject: [PATCH 53/78] pkg/sbom: force disable the license file analyzer (#32184) --- LICENSE-3rdparty.csv | 2 -- go.mod | 3 +-- go.sum | 7 ++----- pkg/util/trivy/trivy.go | 8 +++----- 4 files changed, 6 insertions(+), 14 deletions(-) diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 502d348bb5ab9..92e5c072e3c8e 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -1209,8 +1209,6 @@ core,github.com/google/gopacket/afpacket,BSD-3-Clause,"Copyright (c) 2009-2011 A core,github.com/google/gopacket/layers,BSD-3-Clause,"Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved. | Copyright (c) 2012 Google, Inc. All rights reserved." core,github.com/google/gopacket/pcap,BSD-3-Clause,"Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved. | Copyright (c) 2012 Google, Inc. All rights reserved." core,github.com/google/gopacket/pcapgo,BSD-3-Clause,"Copyright (c) 2009-2011 Andreas Krennmair. All rights reserved. | Copyright (c) 2012 Google, Inc. All rights reserved." -core,github.com/google/licenseclassifier/v2,Apache-2.0,Copyright 2017 Google LLC All Rights Reserved. | Copyright 2020 Google LLC All Rights Reserved. -core,github.com/google/licenseclassifier/v2/assets,Apache-2.0,Copyright 2017 Google LLC All Rights Reserved. | Copyright 2020 Google LLC All Rights Reserved. core,github.com/google/pprof/profile,Apache-2.0,Andrew Hunter | Google Inc. | Hyoun Kyu Cho | Martin Spier | Raul Silvera | Taco de Wolff | Tipp Moseley core,github.com/google/s2a-go,Apache-2.0,Copyright (c) 2020 Google core,github.com/google/s2a-go/fallback,Apache-2.0,Copyright (c) 2020 Google diff --git a/go.mod b/go.mod index 9f95055c7d7b6..e44ee3eddac45 100644 --- a/go.mod +++ b/go.mod @@ -425,7 +425,6 @@ require ( github.com/godbus/dbus/v5 v5.1.0 github.com/golang/glog v1.2.2 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect - github.com/google/licenseclassifier/v2 v2.0.0 // indirect github.com/google/uuid v1.6.0 github.com/google/wire v0.6.0 // indirect github.com/googleapis/gax-go/v2 v2.13.0 // indirect @@ -1061,7 +1060,7 @@ replace github.com/vishvananda/netlink => github.com/DataDog/netlink v1.0.1-0.20 // Pull in replacements needed by upstream Trivy replace ( // Maps to Trivy fork https://github.com/DataDog/trivy/commits/use-fs-main-dd/ - github.com/aquasecurity/trivy => github.com/DataDog/trivy v0.0.0-20241126101205-8517f9b946f4 + github.com/aquasecurity/trivy => github.com/DataDog/trivy v0.0.0-20241216135157-95e0e96002ee github.com/saracen/walker => github.com/DataDog/walker v0.0.0-20230418153152-7f29bb2dc950 // testcontainers-go has a bug with versions v0.25.0 and v0.26.0 // ref: https://github.com/testcontainers/testcontainers-go/issues/1782 diff --git a/go.sum b/go.sum index 038a1a011d963..44b70bde70c36 100644 --- a/go.sum +++ b/go.sum @@ -174,8 +174,8 @@ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.22.0 h1:63SzQz9Ab8XJ github.com/DataDog/opentelemetry-mapping-go/pkg/quantile v0.22.0/go.mod h1:E/PY/aQ6S/N5hBPHXZRGmovs5b1BSi4RHGNcB4yP/Z0= github.com/DataDog/sketches-go v1.4.6 h1:acd5fb+QdUzGrosfNLwrIhqyrbMORpvBy7mE+vHlT3I= github.com/DataDog/sketches-go v1.4.6/go.mod h1:7Y8GN8Jf66DLyDhc94zuWA3uHEt/7ttt8jHOBWWrSOg= -github.com/DataDog/trivy v0.0.0-20241126101205-8517f9b946f4 h1:UVL5oU/8o0JhEv8Js6qxJgiqeV+PzPw/aldojyexS/U= -github.com/DataDog/trivy v0.0.0-20241126101205-8517f9b946f4/go.mod h1:hLiUAm3v175M5jWbq34TdGmX6mvHIJY7FMuZ3wBugtw= +github.com/DataDog/trivy v0.0.0-20241216135157-95e0e96002ee h1:taj22FDHhWs9QkVJypOYMhdgJTnTb5y4SHiW7YcJJms= +github.com/DataDog/trivy v0.0.0-20241216135157-95e0e96002ee/go.mod h1:hv4bqUzcHXSfVft8E9DUlu9d+szHrxYSh+WykZU70Dk= github.com/DataDog/viper v1.14.0 h1:dIjTe/uJiah+QFqFZ+MXeqgmUvWhg37l37ZxFWxr3is= github.com/DataDog/viper v1.14.0/go.mod h1:wDdUVJ2SHaMaPrCZrlRCObwkubsX8j5sme3LaR/SGTc= github.com/DataDog/walker v0.0.0-20230418153152-7f29bb2dc950 h1:2imDajw3V85w1iqHsuXN+hUBZQVF+r9eME8tsPq/HpA= @@ -842,8 +842,6 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA= -github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= @@ -1606,7 +1604,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/secure-systems-lab/go-securesystemslib v0.8.0 h1:mr5An6X45Kb2nddcFlbmfHkLguCE9laoZCUzEEpIZXA= github.com/secure-systems-lab/go-securesystemslib v0.8.0/go.mod h1:UH2VZVuJfCYR8WgMlCU1uFsOUU+KeyrTWcSS73NBOzU= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= diff --git a/pkg/util/trivy/trivy.go b/pkg/util/trivy/trivy.go index e78690f98ab6b..63163ff3ae307 100644 --- a/pkg/util/trivy/trivy.go +++ b/pkg/util/trivy/trivy.go @@ -56,7 +56,6 @@ const ( LanguagesAnalyzers = "languages" // LanguagesAnalyzers defines a language analyzer SecretAnalyzers = "secret" // SecretAnalyzers defines a secret analyzer ConfigFileAnalyzers = "config" // ConfigFileAnalyzers defines a configuration file analyzer - LicenseAnalyzers = "license" // LicenseAnalyzers defines a license analyzer TypeApkCommand = "apk-command" // TypeApkCommand defines a apk-command analyzer HistoryDockerfile = "history-dockerfile" // HistoryDockerfile defines a history-dockerfile analyzer TypeImageConfigSecret = "image-config-secret" // TypeImageConfigSecret defines a history-dockerfile analyzer @@ -149,9 +148,6 @@ func DefaultDisabledCollectors(enabledAnalyzers []string) []analyzer.Type { if analyzersDisabled(ConfigFileAnalyzers) { disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeConfigFiles...) } - if analyzersDisabled(LicenseAnalyzers) { - disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeLicenseFile) - } if analyzersDisabled(TypeApkCommand) { disabledAnalyzers = append(disabledAnalyzers, analyzer.TypeApkCommand) } @@ -166,7 +162,9 @@ func DefaultDisabledCollectors(enabledAnalyzers []string) []analyzer.Type { analyzer.TypeRedHatContentManifestType, analyzer.TypeRedHatDockerfileType, analyzer.TypeSBOM, - analyzer.TypeUbuntuESM) + analyzer.TypeUbuntuESM, + analyzer.TypeLicenseFile, + ) return disabledAnalyzers } From 49e0feffd2c5b12d7dad55c0c297f0942a147f5a Mon Sep 17 00:00:00 2001 From: Alex Lopez Date: Tue, 17 Dec 2024 11:06:43 +0100 Subject: [PATCH 54/78] Revert "[ASCII-2586] Migrating SecurityAgent to use IPC cert" (#32278) --- cmd/security-agent/api/server.go | 48 +++++++++++++++---- cmd/security-agent/main_windows.go | 6 +-- .../subcommands/start/command.go | 9 ++-- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/cmd/security-agent/api/server.go b/cmd/security-agent/api/server.go index 33c8ac033f55a..e776628e5ccd4 100644 --- a/cmd/security-agent/api/server.go +++ b/cmd/security-agent/api/server.go @@ -12,6 +12,9 @@ package api import ( "crypto/tls" + "crypto/x509" + "encoding/pem" + "fmt" stdLog "log" "net" "net/http" @@ -20,10 +23,10 @@ import ( "github.com/gorilla/mux" "github.com/DataDog/datadog-agent/cmd/security-agent/api/agent" - "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/core/settings" "github.com/DataDog/datadog-agent/comp/core/status" workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def" + "github.com/DataDog/datadog-agent/pkg/api/security" "github.com/DataDog/datadog-agent/pkg/api/util" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" "github.com/DataDog/datadog-agent/pkg/util/log" @@ -32,21 +35,19 @@ import ( // Server implements security agent API server type Server struct { - listener net.Listener - agent *agent.Agent - tlsConfig *tls.Config + listener net.Listener + agent *agent.Agent } // NewServer creates a new Server instance -func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (*Server, error) { +func NewServer(statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) (*Server, error) { listener, err := newListener() if err != nil { return nil, err } return &Server{ - listener: listener, - agent: agent.NewAgent(statusComponent, settings, wmeta), - tlsConfig: at.GetTLSClientConfig(), + listener: listener, + agent: agent.NewAgent(statusComponent, settings, wmeta), }, nil } @@ -61,16 +62,43 @@ func (s *Server) Start() error { // Validate token for every request r.Use(validateToken) + err := util.CreateAndSetAuthToken(pkgconfigsetup.Datadog()) + if err != nil { + return err + } + + hosts := []string{"127.0.0.1", "localhost"} + _, rootCertPEM, rootKey, err := security.GenerateRootCert(hosts, 2048) + if err != nil { + return fmt.Errorf("unable to start TLS server") + } + + // PEM encode the private key + rootKeyPEM := pem.EncodeToMemory(&pem.Block{ + Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(rootKey), + }) + + // Create a TLS cert using the private key and certificate + rootTLSCert, err := tls.X509KeyPair(rootCertPEM, rootKeyPEM) + if err != nil { + return fmt.Errorf("invalid key pair: %v", err) + } + + tlsConfig := tls.Config{ + Certificates: []tls.Certificate{rootTLSCert}, + MinVersion: tls.VersionTLS13, + } + // Use a stack depth of 4 on top of the default one to get a relevant filename in the stdlib logWriter, _ := pkglogsetup.NewLogWriter(4, log.ErrorLvl) srv := &http.Server{ Handler: r, ErrorLog: stdLog.New(logWriter, "Error from the agent http API server: ", 0), // log errors to seelog, - TLSConfig: s.tlsConfig, + TLSConfig: &tlsConfig, WriteTimeout: pkgconfigsetup.Datadog().GetDuration("server_timeout") * time.Second, } - tlsListener := tls.NewListener(s.listener, s.tlsConfig) + tlsListener := tls.NewListener(s.listener, &tlsConfig) go srv.Serve(tlsListener) //nolint:errcheck return nil diff --git a/cmd/security-agent/main_windows.go b/cmd/security-agent/main_windows.go index a729e7a2003f0..b81223e6aebc1 100644 --- a/cmd/security-agent/main_windows.go +++ b/cmd/security-agent/main_windows.go @@ -25,7 +25,6 @@ import ( "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands/start" "github.com/DataDog/datadog-agent/comp/agent/autoexit" "github.com/DataDog/datadog-agent/comp/agent/autoexit/autoexitimpl" - "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" @@ -92,11 +91,10 @@ func (s *service) Run(svcctx context.Context) error { params := &cliParams{} err := fxutil.OneShot( func(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, - telemetry telemetry.Component, _ workloadmeta.Component, _ *cliParams, statusComponent status.Component, _ autoexit.Component, - settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error { + telemetry telemetry.Component, _ workloadmeta.Component, _ *cliParams, statusComponent status.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component) error { defer start.StopAgent(log) - err := start.RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at) + err := start.RunAgent(log, config, telemetry, statusComponent, settings, wmeta) if err != nil { if errors.Is(err, start.ErrAllComponentsDisabled) { // If all components are disabled, we should exit cleanly diff --git a/cmd/security-agent/subcommands/start/command.go b/cmd/security-agent/subcommands/start/command.go index 2a3dd883dcd84..12a93fc4560ff 100644 --- a/cmd/security-agent/subcommands/start/command.go +++ b/cmd/security-agent/subcommands/start/command.go @@ -29,7 +29,6 @@ import ( "github.com/DataDog/datadog-agent/cmd/security-agent/subcommands/runtime" "github.com/DataDog/datadog-agent/comp/agent/autoexit" "github.com/DataDog/datadog-agent/comp/agent/autoexit/autoexitimpl" - "github.com/DataDog/datadog-agent/comp/api/authtoken" "github.com/DataDog/datadog-agent/comp/api/authtoken/fetchonlyimpl" "github.com/DataDog/datadog-agent/comp/core" "github.com/DataDog/datadog-agent/comp/core/config" @@ -202,10 +201,10 @@ func Commands(globalParams *command.GlobalParams) []*cobra.Command { // TODO(components): note how workloadmeta is passed anonymously, it is still required as it is used // as a global. This should eventually be fixed and all workloadmeta interactions should be via the // injected instance. -func start(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) error { +func start(log log.Component, config config.Component, _ secrets.Component, _ statsd.Component, _ sysprobeconfig.Component, telemetry telemetry.Component, statusComponent status.Component, _ pid.Component, _ autoexit.Component, settings settings.Component, wmeta workloadmeta.Component) error { defer StopAgent(log) - err := RunAgent(log, config, telemetry, statusComponent, settings, wmeta, at) + err := RunAgent(log, config, telemetry, statusComponent, settings, wmeta) if errors.Is(err, ErrAllComponentsDisabled) || errors.Is(err, errNoAPIKeyConfigured) { return nil } @@ -257,7 +256,7 @@ var ErrAllComponentsDisabled = errors.New("all security-agent component are disa var errNoAPIKeyConfigured = errors.New("no API key configured") // RunAgent initialized resources and starts API server -func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component, at authtoken.Component) (err error) { +func RunAgent(log log.Component, config config.Component, telemetry telemetry.Component, statusComponent status.Component, settings settings.Component, wmeta workloadmeta.Component) (err error) { if err := coredump.Setup(config); err != nil { log.Warnf("Can't setup core dumps: %v, core dumps might not be available after a crash", err) } @@ -300,7 +299,7 @@ func RunAgent(log log.Component, config config.Component, telemetry telemetry.Co } }() - srv, err = api.NewServer(statusComponent, settings, wmeta, at) + srv, err = api.NewServer(statusComponent, settings, wmeta) if err != nil { return log.Errorf("Error while creating api server, exiting: %v", err) } From b743e35030e0a6ebc402264185196efcf9b2d6cb Mon Sep 17 00:00:00 2001 From: Florent Clarret Date: Tue, 17 Dec 2024 11:06:52 +0100 Subject: [PATCH 55/78] Create a dedicated task to bump the `current_milestone` (#32275) --- tasks/libs/ciproviders/github_api.py | 3 + tasks/release.py | 100 ++++++++++++++++----------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/tasks/libs/ciproviders/github_api.py b/tasks/libs/ciproviders/github_api.py index a32546d23ee38..8a4d5e69027d1 100644 --- a/tasks/libs/ciproviders/github_api.py +++ b/tasks/libs/ciproviders/github_api.py @@ -523,6 +523,9 @@ def create_label(self, name, color, description=""): """ return self._repository.create_label(name, color, description) + def create_milestone(self, title): + self._repository.create_milestone(title) + def create_release(self, tag, message, draft=True): return self._repository.create_git_release( tag=tag, diff --git a/tasks/release.py b/tasks/release.py index d7acec73f8e05..077834b279923 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -731,10 +731,8 @@ def create_release_branches(ctx, base_directory="~/dd", major_version: int = 7, github = GithubAPI(repository=GITHUB_REPO_NAME) current = current_version(ctx, major_version) - next = current.next_version(bump_minor=True) current.rc = False current.devel = False - next.devel = False # Strings with proper branch/tag names release_branch = current.branch() @@ -772,43 +770,7 @@ def create_release_branches(ctx, base_directory="~/dd", major_version: int = 7, ) # Step 2 - Create PRs with new settings in datadog-agent repository - # Step 2.0 - Create milestone update - milestone_branch = f"release_milestone-{int(time.time())}" - ctx.run(f"git switch -c {milestone_branch}") - rj = load_release_json() - rj["current_milestone"] = f"{next}" - _save_release_json(rj) - # Commit release.json - ctx.run("git add release.json") - ok = try_git_command(ctx, f"git commit -m 'Update release.json with current milestone to {next}'") - - if not ok: - raise Exit( - color_message( - f"Could not create commit. Please commit manually and push the commit to the {milestone_branch} branch.", - "red", - ), - code=1, - ) - - res = ctx.run(f"git push --set-upstream {upstream} {milestone_branch}", warn=True) - if res.exited is None or res.exited > 0: - raise Exit( - color_message( - f"Could not push branch {milestone_branch} to the upstream '{upstream}'. Please push it manually and then open a PR against {release_branch}.", - "red", - ), - code=1, - ) - - create_release_pr( - f"[release] Update current milestone to {next}", - get_default_branch(), - milestone_branch, - next, - ) - - # Step 2.1 - Update release.json + # Step 2.0 - Update release.json update_branch = f"{release_branch}-updates" ctx.run(f"git checkout {release_branch}") @@ -1303,3 +1265,63 @@ def create_github_release(ctx, release_branch, draft=True): ) print(f"Link to the release note: {release.html_url}") + + +@task +def update_current_milestone(ctx, major_version: int = 7, upstream="origin"): + """ + Create a PR to bump the current_milestone in the release.json file + """ + import github + + gh = GithubAPI() + + current = current_version(ctx, major_version) + next = current.next_version(bump_minor=True) + next.devel = False + + print(f"Creating the {next} milestone...") + + try: + gh.create_milestone(str(next)) + except github.GithubException as e: + if e.status == 422: + print(f"Milestone {next} already exists") + else: + raise e + + with agent_context(ctx, get_default_branch(major=major_version)): + milestone_branch = f"release_milestone-{int(time.time())}" + ctx.run(f"git switch -c {milestone_branch}") + rj = load_release_json() + rj["current_milestone"] = f"{next}" + _save_release_json(rj) + # Commit release.json + ctx.run("git add release.json") + ok = try_git_command(ctx, f"git commit -m 'Update release.json with current milestone to {next}'") + + if not ok: + raise Exit( + color_message( + f"Could not create commit. Please commit manually and push the commit to the {milestone_branch} branch.", + Color.RED, + ), + code=1, + ) + + res = ctx.run(f"git push --set-upstream {upstream} {milestone_branch}", warn=True) + if res.exited is None or res.exited > 0: + raise Exit( + color_message( + f"Could not push branch {milestone_branch} to the upstream '{upstream}'. Please push it manually and then open a PR against main.", + Color.RED, + ), + code=1, + ) + + create_release_pr( + f"[release] Update current milestone to {next}", + get_default_branch(), + milestone_branch, + next, + ) From 9f49879720e7586fb4ae35c19f62095f9229d002 Mon Sep 17 00:00:00 2001 From: David du Colombier Date: Tue, 17 Dec 2024 11:20:10 +0100 Subject: [PATCH 56/78] [omnibus] Upgrade OpenSCAP to version 1.4.0 (#32276) --- omnibus/config/software/openscap.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/omnibus/config/software/openscap.rb b/omnibus/config/software/openscap.rb index e97250467e2a2..3e8e08d71990d 100644 --- a/omnibus/config/software/openscap.rb +++ b/omnibus/config/software/openscap.rb @@ -4,12 +4,12 @@ # Copyright 2016-present Datadog, Inc. name 'openscap' -default_version '1.3.10' +default_version '1.4.0' license "LGPL-3.0-or-later" license_file "COPYING" -version("1.3.10") { source sha256: "0d023ff3fbdec617768ea5977fd3bb6702dfef4ae595da9a5bbc6ecc6ac9e575" } +version("1.4.0") { source sha256: "b64f3709f0e072262708212d56897c419abdd2dc618273a363bc0497c6b4ba0b" } ship_source_offer true From 2c6de378d89139ecc2ca1a6201c1cd465016c631 Mon Sep 17 00:00:00 2001 From: Sylvain Afchain Date: Tue, 17 Dec 2024 11:20:52 +0100 Subject: [PATCH 57/78] [CWS] fix secl weight (#32162) --- pkg/security/secl/compiler/eval/eval_test.go | 8 +++++--- pkg/security/secl/compiler/eval/operators.go | 3 +-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/security/secl/compiler/eval/eval_test.go b/pkg/security/secl/compiler/eval/eval_test.go index 0674e4ef744b1..6a217e8877444 100644 --- a/pkg/security/secl/compiler/eval/eval_test.go +++ b/pkg/security/secl/compiler/eval/eval_test.go @@ -1006,8 +1006,9 @@ func TestRegisterPartial(t *testing.T) { func TestOptimizer(t *testing.T) { event := &testEvent{ process: testProcess{ - uid: 44, - gid: 44, + uid: 44, + gid: 44, + name: "aaa", }, } @@ -1018,10 +1019,11 @@ func TestOptimizer(t *testing.T) { Expr string Evaluated func() bool }{ - {Expr: `process.list[A].key == 44 && process.gid == 55`, Evaluated: func() bool { return event.listEvaluated }}, + {Expr: `process.list.key == 44 && process.gid == 55`, Evaluated: func() bool { return event.listEvaluated }}, {Expr: `process.gid == 55 && process.list[A].key == 44`, Evaluated: func() bool { return event.listEvaluated }}, {Expr: `process.uid in [66, 77, 88] && process.gid == 55`, Evaluated: func() bool { return event.uidEvaluated }}, {Expr: `process.gid == 55 && process.uid in [66, 77, 88]`, Evaluated: func() bool { return event.uidEvaluated }}, + {Expr: `process.list.value == "AA" && process.name == "zzz"`, Evaluated: func() bool { return event.listEvaluated }}, } for _, test := range tests { diff --git a/pkg/security/secl/compiler/eval/operators.go b/pkg/security/secl/compiler/eval/operators.go index 9f39ff3e3550e..9fd97b9a98177 100644 --- a/pkg/security/secl/compiler/eval/operators.go +++ b/pkg/security/secl/compiler/eval/operators.go @@ -352,7 +352,6 @@ func StringEquals(a *StringEvaluator, b *StringEvaluator, state *State) (*BoolEv return &BoolEvaluator{ Value: op(ea, eb), - Weight: a.Weight + InArrayWeight*len(eb), isDeterministic: isDc, }, nil } @@ -366,7 +365,7 @@ func StringEquals(a *StringEvaluator, b *StringEvaluator, state *State) (*BoolEv return &BoolEvaluator{ EvalFnc: evalFnc, - Weight: a.Weight + InArrayWeight*len(eb), + Weight: a.Weight, isDeterministic: isDc, }, nil } From 98d6070e42c86b7bf88507ee9e85ffe7ce68a7a6 Mon Sep 17 00:00:00 2001 From: Paul Cacheux Date: Tue, 17 Dec 2024 11:32:04 +0100 Subject: [PATCH 58/78] remove unused build tags from system-probe build (#32188) --- tasks/build_tags.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tasks/build_tags.py b/tasks/build_tags.py index a3c13f088eb8b..c400d2560aa94 100644 --- a/tasks/build_tags.py +++ b/tasks/build_tags.py @@ -159,7 +159,18 @@ SERVERLESS_TAGS = {"serverless", "otlp"} # SYSTEM_PROBE_TAGS lists the tags necessary to build system-probe -SYSTEM_PROBE_TAGS = AGENT_TAGS.union({"linux_bpf", "npm", "pcap"}).difference({"python", "systemd"}) +SYSTEM_PROBE_TAGS = { + "datadog.no_waf", + "ec2", + "linux_bpf", + "netcgo", + "npm", + "pcap", + "process", + "trivy", + "zlib", + "zstd", +} # TRACE_AGENT_TAGS lists the tags that have to be added when the trace-agent TRACE_AGENT_TAGS = {"docker", "containerd", "datadog.no_waf", "kubeapiserver", "kubelet", "otlp", "netcgo", "podman"} From abb0743733b1713b582a9c9b0f259b8f3d93c479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9na=C3=AFc=20Huard?= Date: Tue, 17 Dec 2024 11:46:57 +0100 Subject: [PATCH 59/78] [incident-33304] Remove the flaky mark on fixed tests (#32266) --- flakes.yaml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/flakes.yaml b/flakes.yaml index bde9554d2f5f7..ab2af6b55f3ea 100644 --- a/flakes.yaml +++ b/flakes.yaml @@ -11,8 +11,3 @@ test/new-e2e/tests/containers: - TestECSSuite/TestCPU/metric___container.cpu.usage{^ecs_container_name:stress-ng$} - TestEKSSuite/TestCPU/metric___container.cpu.usage{^kube_deployment:stress-ng$,^kube_namespace:workload-cpustress$} - TestKindSuite/TestCPU/metric___container.cpu.usage{^kube_deployment:stress-ng$,^kube_namespace:workload-cpustress$} - - - TestEKSSuite/TestDogstatsdInAgent/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} - - TestEKSSuite/TestDogstatsdStandalone/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} - - TestKindSuite/TestDogstatsdInAgent/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} - - TestKindSuite/TestDogstatsdStandalone/metric___custom.metric{^kube_deployment:dogstatsd-udp$,^kube_namespace:workload-dogstatsd$} From 326b13e4e5add1756013e69ac8b486d871360c37 Mon Sep 17 00:00:00 2001 From: Florian Veaux Date: Tue, 17 Dec 2024 11:51:30 +0100 Subject: [PATCH 60/78] [VULN-9579] Traps: Validate community string in constant time (#32280) --- comp/snmptraps/listener/listenerimpl/listener.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/comp/snmptraps/listener/listenerimpl/listener.go b/comp/snmptraps/listener/listenerimpl/listener.go index 7571896edc30d..ab3d5c3aa3dea 100644 --- a/comp/snmptraps/listener/listenerimpl/listener.go +++ b/comp/snmptraps/listener/listenerimpl/listener.go @@ -8,6 +8,7 @@ package listenerimpl import ( "context" + "crypto/subtle" "errors" "fmt" "net" @@ -167,7 +168,8 @@ func validatePacket(p *gosnmp.SnmpPacket, c *config.TrapsConfig) error { // At least one of the known community strings must match. for _, community := range c.CommunityStrings { - if community == p.Community { + // Simple string equality check, but in constant time to avoid timing attacks + if subtle.ConstantTimeCompare([]byte(community), []byte(p.Community)) == 1 { return nil } } From a89d19f18c7fb151515383e8137fb0dcacc1c388 Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Tue, 17 Dec 2024 12:06:00 +0100 Subject: [PATCH 61/78] [CWS] Use config defaults rather than CLI defaults to generate dumps (#32224) --- .../subcommands/runtime/activity_dump.go | 11 +++++++++-- pkg/security/security_profile/dump/manager.go | 15 ++++++++++++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/cmd/security-agent/subcommands/runtime/activity_dump.go b/cmd/security-agent/subcommands/runtime/activity_dump.go index 35907553045fb..0f15351d72e02 100644 --- a/cmd/security-agent/subcommands/runtime/activity_dump.go +++ b/cmd/security-agent/subcommands/runtime/activity_dump.go @@ -12,6 +12,7 @@ import ( "encoding/json" "fmt" "os" + "time" "github.com/spf13/cobra" "go.uber.org/fx" @@ -173,7 +174,7 @@ func generateDumpCommands(globalParams *command.GlobalParams) []*cobra.Command { activityDumpGenerateDumpCmd.Flags().StringVar( &cliParams.timeout, "timeout", - "1m", + "", "timeout for the activity dump", ) activityDumpGenerateDumpCmd.Flags().BoolVar( @@ -185,7 +186,7 @@ func generateDumpCommands(globalParams *command.GlobalParams) []*cobra.Command { activityDumpGenerateDumpCmd.Flags().StringVar( &cliParams.localStorageDirectory, "output", - "/tmp/activity_dumps/", + "", "local storage output directory", ) activityDumpGenerateDumpCmd.Flags().BoolVar( @@ -472,6 +473,12 @@ func generateActivityDump(_ log.Component, _ config.Component, _ secrets.Compone return err } + if activityDumpArgs.timeout != "" { + if _, err = time.ParseDuration(activityDumpArgs.timeout); err != nil { + return err + } + } + output, err := client.GenerateActivityDump(&api.ActivityDumpParams{ ContainerID: activityDumpArgs.containerID, CGroupID: activityDumpArgs.cgroupID, diff --git a/pkg/security/security_profile/dump/manager.go b/pkg/security/security_profile/dump/manager.go index d06600ed81bca..079f45c13bc21 100644 --- a/pkg/security/security_profile/dump/manager.go +++ b/pkg/security/security_profile/dump/manager.go @@ -548,11 +548,24 @@ func (adm *ActivityDumpManager) DumpActivity(params *api.ActivityDumpParams) (*a adm.Lock() defer adm.Unlock() + var dumpDuration time.Duration + var err error + if params.Timeout != "" { + if dumpDuration, err = time.ParseDuration(params.Timeout); err != nil { + return nil, err + } + } else { + dumpDuration = adm.config.RuntimeSecurity.ActivityDumpCgroupDumpTimeout + } + + if params.Storage.LocalStorageDirectory == "" { + params.Storage.LocalStorageDirectory = adm.config.RuntimeSecurity.ActivityDumpLocalStorageDirectory + } + newDump := NewActivityDump(adm, func(ad *ActivityDump) { ad.Metadata.ContainerID = containerutils.ContainerID(params.GetContainerID()) ad.Metadata.CGroupContext.CGroupID = containerutils.CGroupID(params.GetCGroupID()) - dumpDuration, _ := time.ParseDuration(params.Timeout) ad.SetTimeout(dumpDuration) if params.GetDifferentiateArgs() { From 8ef1fd9a7dc9bbe049b53e3b92bfad81cab9c77c Mon Sep 17 00:00:00 2001 From: Sylvain Baubeau Date: Tue, 17 Dec 2024 12:06:10 +0100 Subject: [PATCH 62/78] [CWS] Reduce fallbacks to cgroup file read (#32199) --- pkg/security/resolvers/process/resolver_ebpf.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/security/resolvers/process/resolver_ebpf.go b/pkg/security/resolvers/process/resolver_ebpf.go index dc5694dd55647..5bcccdb52c540 100644 --- a/pkg/security/resolvers/process/resolver_ebpf.go +++ b/pkg/security/resolvers/process/resolver_ebpf.go @@ -852,7 +852,7 @@ func (p *EBPFResolver) resolveFromKernelMaps(pid, tid uint32, inode uint64, newE // is no insurance that the parent of this process is still running, we can't use our user space cache to check if // the parent is in a container. In other words, we have to fall back to /proc to query the container ID of the // process. - if entry.ContainerID == "" || entry.CGroup.CGroupFile.Inode == 0 { + if entry.CGroup.CGroupFile.Inode == 0 { if containerID, cgroup, err := p.containerResolver.GetContainerContext(pid); err == nil { entry.CGroup.Merge(&cgroup) entry.ContainerID = containerID From 9c7198c00df845570cb75a5238fe19d443cdae00 Mon Sep 17 00:00:00 2001 From: Wassim Dhif Date: Tue, 17 Dec 2024 12:13:50 +0100 Subject: [PATCH 63/78] feat(tagger): implement remote OriginInfo resolution (#31836) Signed-off-by: Wassim DHIF --- comp/api/api/apiimpl/grpc.go | 6 + comp/core/tagger/def/component.go | 2 + comp/core/tagger/impl-noop/tagger.go | 7 + comp/core/tagger/impl-remote/remote.go | 80 +++ comp/core/tagger/impl/local_tagger.go | 10 + comp/core/tagger/impl/replay_tagger.go | 7 + comp/core/tagger/impl/tagger.go | 8 +- comp/core/tagger/mock/fake_tagger.go | 6 + comp/core/tagger/origindetection/go.mod | 14 + comp/core/tagger/origindetection/go.sum | 23 + .../tagger/origindetection/origindetection.go | 84 +++ .../origindetection/origindetection_test.go | 81 +++ comp/core/tagger/server/server.go | 20 + comp/core/tagger/telemetry/telemetry.go | 10 + go.mod | 2 + go.work | 1 + modules.yml | 1 + pkg/proto/datadog/api/v1/api.proto | 16 + pkg/proto/datadog/model/v1/model.proto | 24 + pkg/proto/pbgo/core/api.pb.go | 402 ++++++++------ pkg/proto/pbgo/core/api.pb.gw.go | 81 +++ pkg/proto/pbgo/core/model.pb.go | 514 ++++++++++++++---- pkg/proto/pbgo/mocks/core/api_mockgen.pb.go | 35 ++ 23 files changed, 1154 insertions(+), 280 deletions(-) create mode 100644 comp/core/tagger/origindetection/go.mod create mode 100644 comp/core/tagger/origindetection/go.sum create mode 100644 comp/core/tagger/origindetection/origindetection.go create mode 100644 comp/core/tagger/origindetection/origindetection_test.go diff --git a/comp/api/api/apiimpl/grpc.go b/comp/api/api/apiimpl/grpc.go index 5f88cdb722163..6272a7f00fe05 100644 --- a/comp/api/api/apiimpl/grpc.go +++ b/comp/api/api/apiimpl/grpc.go @@ -74,6 +74,12 @@ func (s *serverSecure) TaggerStreamEntities(req *pb.StreamTagsRequest, srv pb.Ag return s.taggerServer.TaggerStreamEntities(req, srv) } +// TaggerGenerateContainerIDFromOriginInfo generates a container ID from the Origin Info. +// This function takes an Origin Info but only uses the ExternalData part of it, this is done for backward compatibility. +func (s *serverSecure) TaggerGenerateContainerIDFromOriginInfo(ctx context.Context, req *pb.GenerateContainerIDFromOriginInfoRequest) (*pb.GenerateContainerIDFromOriginInfoResponse, error) { + return s.taggerServer.TaggerGenerateContainerIDFromOriginInfo(ctx, req) +} + func (s *serverSecure) TaggerFetchEntity(ctx context.Context, req *pb.FetchEntityRequest) (*pb.FetchEntityResponse, error) { return s.taggerServer.TaggerFetchEntity(ctx, req) } diff --git a/comp/core/tagger/def/component.go b/comp/core/tagger/def/component.go index c580325c3bf81..619196f41de95 100644 --- a/comp/core/tagger/def/component.go +++ b/comp/core/tagger/def/component.go @@ -9,6 +9,7 @@ package tagger import ( "context" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" taggertypes "github.com/DataDog/datadog-agent/pkg/tagger/types" @@ -37,6 +38,7 @@ type Component interface { // integrations using the tagger LegacyTag(entity string, cardinality types.TagCardinality) ([]string, error) Tag(entityID types.EntityID, cardinality types.TagCardinality) ([]string, error) + GenerateContainerIDFromOriginInfo(originInfo origindetection.OriginInfo) (string, error) AccumulateTagsFor(entityID types.EntityID, cardinality types.TagCardinality, tb tagset.TagsAccumulator) error Standard(entityID types.EntityID) ([]string, error) List() types.TaggerListResponse diff --git a/comp/core/tagger/impl-noop/tagger.go b/comp/core/tagger/impl-noop/tagger.go index 811a60e905923..986e113df2c1e 100644 --- a/comp/core/tagger/impl-noop/tagger.go +++ b/comp/core/tagger/impl-noop/tagger.go @@ -17,6 +17,7 @@ import ( "context" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" taggertypes "github.com/DataDog/datadog-agent/pkg/tagger/types" @@ -49,6 +50,12 @@ func (n *noopTagger) LegacyTag(string, types.TagCardinality) ([]string, error) { return nil, nil } +// GenerateContainerIDFromOriginInfo generates a container ID from Origin Info. +// This is a no-op for the noop tagger +func (n *noopTagger) GenerateContainerIDFromOriginInfo(origindetection.OriginInfo) (string, error) { + return "", nil +} + func (n *noopTagger) AccumulateTagsFor(types.EntityID, types.TagCardinality, tagset.TagsAccumulator) error { return nil } diff --git a/comp/core/tagger/impl-remote/remote.go b/comp/core/tagger/impl-remote/remote.go index b1a77165f18a9..b602facbb824b 100644 --- a/comp/core/tagger/impl-remote/remote.go +++ b/comp/core/tagger/impl-remote/remote.go @@ -27,6 +27,7 @@ import ( "github.com/DataDog/datadog-agent/comp/core/config" log "github.com/DataDog/datadog-agent/comp/core/log/def" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" "github.com/DataDog/datadog-agent/comp/core/tagger/utils" @@ -47,6 +48,8 @@ const ( var errTaggerStreamNotStarted = errors.New("tagger stream not started") +var errTaggerFailedGenerateContainerIDFromOriginInfo = errors.New("tagger failed to generate container ID from origin info") + // Requires defines the dependencies for the remote tagger. type Requires struct { compdef.In @@ -75,6 +78,7 @@ type remoteTagger struct { log log.Component conn *grpc.ClientConn + token string client pb.AgentSecureClient stream pb.AgentSecure_TaggerStreamEntitiesClient @@ -82,6 +86,9 @@ type remoteTagger struct { streamCancel context.CancelFunc filter *types.Filter + queryCtx context.Context + queryCancel context.CancelFunc + ctx context.Context cancel context.CancelFunc @@ -250,6 +257,79 @@ func (t *remoteTagger) LegacyTag(entity string, cardinality types.TagCardinality return t.Tag(entityID, cardinality) } +// GenerateContainerIDFromOriginInfo returns a container ID for the given Origin Info. +// This function currently only uses the External Data from the Origin Info to generate the container ID. +func (t *remoteTagger) GenerateContainerIDFromOriginInfo(originInfo origindetection.OriginInfo) (string, error) { + fail := true + defer func() { + if fail { + t.telemetryStore.OriginInfoRequests.Inc("failed") + } else { + t.telemetryStore.OriginInfoRequests.Inc("success") + } + }() + + expBackoff := backoff.NewExponentialBackOff() + expBackoff.InitialInterval = 500 * time.Millisecond + expBackoff.MaxInterval = 1 * time.Second + expBackoff.MaxElapsedTime = 15 * time.Second + + var containerID string + + err := backoff.Retry(func() error { + select { + case <-t.ctx.Done(): + return &backoff.PermanentError{Err: errTaggerFailedGenerateContainerIDFromOriginInfo} + default: + } + + // Fetch the auth token + if t.token == "" { + var authError error + t.token, authError = t.options.TokenFetcher() + if authError != nil { + _ = t.log.Errorf("unable to fetch auth token, will possibly retry: %s", authError) + return authError + } + } + + // Create the context with the auth token + t.queryCtx, t.queryCancel = context.WithCancel( + metadata.NewOutgoingContext(t.ctx, metadata.MD{ + "authorization": []string{fmt.Sprintf("Bearer %s", t.token)}, + }), + ) + + // Call the gRPC method to get the container ID from the origin info + containerIDResponse, err := t.client.TaggerGenerateContainerIDFromOriginInfo(t.queryCtx, &pb.GenerateContainerIDFromOriginInfoRequest{ + ExternalData: &pb.GenerateContainerIDFromOriginInfoRequest_ExternalData{ + Init: &originInfo.ExternalData.Init, + ContainerName: &originInfo.ExternalData.ContainerName, + PodUID: &originInfo.ExternalData.PodUID, + }, + }) + if err != nil { + _ = t.log.Errorf("unable to generate container ID from origin info, will retry: %s", err) + return err + } + + if containerIDResponse == nil { + _ = t.log.Warnf("unable to generate container ID from origin info, will retry: %s", err) + return errors.New("containerIDResponse is nil") + } + containerID = containerIDResponse.ContainerID + + fail = false + t.log.Debugf("Container ID generated successfully from origin info %+v: %s", originInfo, containerID) + return nil + }, expBackoff) + + if err != nil { + return "", err + } + return containerID, nil +} + // AccumulateTagsFor returns tags for a given entity at the desired cardinality. func (t *remoteTagger) AccumulateTagsFor(entityID types.EntityID, cardinality types.TagCardinality, tb tagset.TagsAccumulator) error { tags, err := t.Tag(entityID, cardinality) diff --git a/comp/core/tagger/impl/local_tagger.go b/comp/core/tagger/impl/local_tagger.go index ac2259342eb25..85c8741c52b02 100644 --- a/comp/core/tagger/impl/local_tagger.go +++ b/comp/core/tagger/impl/local_tagger.go @@ -9,16 +9,20 @@ import ( "context" "fmt" "sync" + "time" "github.com/DataDog/datadog-agent/comp/core/config" "github.com/DataDog/datadog-agent/comp/core/tagger/collectors" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/tagstore" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" workloadmeta "github.com/DataDog/datadog-agent/comp/core/workloadmeta/def" taggertypes "github.com/DataDog/datadog-agent/pkg/tagger/types" "github.com/DataDog/datadog-agent/pkg/tagset" + "github.com/DataDog/datadog-agent/pkg/util/containers/metrics" + "github.com/DataDog/datadog-agent/pkg/util/optional" ) // Tagger is the entry class for entity tagging. It hold the tagger collector, @@ -99,6 +103,12 @@ func (t *localTagger) Tag(entityID types.EntityID, cardinality types.TagCardinal return tags.Copy(), nil } +// GenerateContainerIDFromOriginInfo generates a container ID from Origin Info. +func (t *localTagger) GenerateContainerIDFromOriginInfo(originInfo origindetection.OriginInfo) (string, error) { + metaCollector := metrics.GetProvider(optional.NewOption(t.workloadStore)).GetMetaCollector() + return metaCollector.ContainerIDForPodUIDAndContName(originInfo.ExternalData.PodUID, originInfo.ExternalData.ContainerName, originInfo.ExternalData.Init, time.Second) +} + // LegacyTag has the same behaviour as the Tag method, but it receives the entity id as a string and parses it. // If possible, avoid using this function, and use the Tag method instead. // This function exists in order not to break backward compatibility with rtloader and python diff --git a/comp/core/tagger/impl/replay_tagger.go b/comp/core/tagger/impl/replay_tagger.go index ddc9851a898db..6f012e9c9d680 100644 --- a/comp/core/tagger/impl/replay_tagger.go +++ b/comp/core/tagger/impl/replay_tagger.go @@ -11,6 +11,7 @@ import ( "time" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/tagstore" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" @@ -77,6 +78,12 @@ func (t *replayTagger) LegacyTag(entity string, cardinality types.TagCardinality return t.Tag(entityID, cardinality) } +// GenerateContainerIDFromOriginInfo generates a container ID from Origin Info. +// This is a no-op for the replay tagger +func (t *replayTagger) GenerateContainerIDFromOriginInfo(origindetection.OriginInfo) (string, error) { + return "", nil +} + // AccumulateTagsFor returns tags for a given entity at the desired cardinality. func (t *replayTagger) AccumulateTagsFor(entityID types.EntityID, cardinality types.TagCardinality, tb tagset.TagsAccumulator) error { tags := t.store.LookupHashed(entityID, cardinality) diff --git a/comp/core/tagger/impl/tagger.go b/comp/core/tagger/impl/tagger.go index 1ef1a137d6a34..7023106b1c118 100644 --- a/comp/core/tagger/impl/tagger.go +++ b/comp/core/tagger/impl/tagger.go @@ -27,6 +27,7 @@ import ( log "github.com/DataDog/datadog-agent/comp/core/log/def" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" taggermock "github.com/DataDog/datadog-agent/comp/core/tagger/mock" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" "github.com/DataDog/datadog-agent/comp/core/tagger/utils" @@ -538,7 +539,12 @@ func (t *TaggerWrapper) EnrichTags(tb tagset.TagsAccumulator, originInfo taggert } } -// generateContainerIDFromExternalData generates a container ID from the external data +// GenerateContainerIDFromOriginInfo generates a container ID from Origin Info. +func (t *TaggerWrapper) GenerateContainerIDFromOriginInfo(originInfo origindetection.OriginInfo) (string, error) { + return t.defaultTagger.GenerateContainerIDFromOriginInfo(originInfo) +} + +// generateContainerIDFromExternalData generates a container ID from the External Data. func (t *TaggerWrapper) generateContainerIDFromExternalData(e externalData, metricsProvider provider.ContainerIDForPodUIDAndContNameRetriever) (string, error) { return metricsProvider.ContainerIDForPodUIDAndContName(e.podUID, e.containerName, e.init, time.Second) } diff --git a/comp/core/tagger/mock/fake_tagger.go b/comp/core/tagger/mock/fake_tagger.go index b4ff66a65d2c6..8a609d052c766 100644 --- a/comp/core/tagger/mock/fake_tagger.go +++ b/comp/core/tagger/mock/fake_tagger.go @@ -10,6 +10,7 @@ import ( "strconv" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/tagstore" "github.com/DataDog/datadog-agent/comp/core/tagger/telemetry" "github.com/DataDog/datadog-agent/comp/core/tagger/types" @@ -118,6 +119,11 @@ func (f *FakeTagger) LegacyTag(entity string, cardinality types.TagCardinality) return f.Tag(entityID, cardinality) } +// GenerateContainerIDFromOriginInfo fake implementation +func (f *FakeTagger) GenerateContainerIDFromOriginInfo(origindetection.OriginInfo) (string, error) { + return "", nil +} + // GlobalTags fake implementation func (f *FakeTagger) GlobalTags(cardinality types.TagCardinality) ([]string, error) { return f.Tag(types.GetGlobalEntityID(), cardinality) diff --git a/comp/core/tagger/origindetection/go.mod b/comp/core/tagger/origindetection/go.mod new file mode 100644 index 0000000000000..10e53c86ea39d --- /dev/null +++ b/comp/core/tagger/origindetection/go.mod @@ -0,0 +1,14 @@ +module github.com/DataDog/datadog-agent/comp/core/tagger/origindetection + +go 1.22.0 + +require github.com/stretchr/testify v1.10.0 + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.13.1 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/comp/core/tagger/origindetection/go.sum b/comp/core/tagger/origindetection/go.sum new file mode 100644 index 0000000000000..cec386e1e2769 --- /dev/null +++ b/comp/core/tagger/origindetection/go.sum @@ -0,0 +1,23 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/comp/core/tagger/origindetection/origindetection.go b/comp/core/tagger/origindetection/origindetection.go new file mode 100644 index 0000000000000..712792c54f298 --- /dev/null +++ b/comp/core/tagger/origindetection/origindetection.go @@ -0,0 +1,84 @@ +// 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. + +// TODO: A lot of the code in this file is currently duplicated in taggertypes. +// We will need to move all the code in taggertype to this file and remove the taggertypes package. + +// Package origindetection contains the types and functions used for Origin Detection. +package origindetection + +import ( + "strconv" + "strings" +) + +// ProductOrigin is the origin of the product that sent the entity. +type ProductOrigin int + +const ( + // ProductOriginDogStatsDLegacy is the ProductOrigin for DogStatsD in Legacy mode. + // TODO: remove this when dogstatsd_origin_detection_unified is enabled by default + ProductOriginDogStatsDLegacy ProductOrigin = iota + // ProductOriginDogStatsD is the ProductOrigin for DogStatsD. + ProductOriginDogStatsD ProductOrigin = iota + // ProductOriginAPM is the ProductOrigin for APM. + ProductOriginAPM ProductOrigin = iota + + // External Data Prefixes + // These prefixes are used to build the External Data Environment Variable. + + // ExternalDataInitPrefix is the prefix for the Init flag in the External Data. + ExternalDataInitPrefix = "it-" + // ExternalDataContainerNamePrefix is the prefix for the Container Name in the External Data. + ExternalDataContainerNamePrefix = "cn-" + // ExternalDataPodUIDPrefix is the prefix for the Pod UID in the External Data. + ExternalDataPodUIDPrefix = "pu-" +) + +// OriginInfo contains the Origin Detection information. +type OriginInfo struct { + LocalData LocalData // LocalData is the local data list. + ExternalData ExternalData // ExternalData is the external data list. + Cardinality string // Cardinality is the cardinality of the resolved origin. + ProductOrigin ProductOrigin // ProductOrigin is the product that sent the origin information. +} + +// LocalData that is generated by the client and sent to the Agent. +type LocalData struct { + ProcessID uint32 // ProcessID of the container process on the host. + ContainerID string // ContainerID sent from the client. + Inode uint64 // Inode is the Cgroup inode of the container. + PodUID string // PodUID of the pod sent from the client. +} + +// ExternalData generated by the Admission Controller and sent to the Agent. +type ExternalData struct { + Init bool // Init is true if the container is an init container. + ContainerName string // ContainerName is the name of the container as seen by the Admission Controller. + PodUID string // PodUID is the UID of the pod as seen by the Admission Controller. +} + +// GenerateContainerIDFromExternalData generates a container ID from the external data. +type GenerateContainerIDFromExternalData func(externalData ExternalData) (string, error) + +// ParseExternalData parses the external data string into an ExternalData struct. +func ParseExternalData(externalEnv string) (ExternalData, error) { + if externalEnv == "" { + return ExternalData{}, nil + } + var externalData ExternalData + var parsingError error + for _, item := range strings.Split(externalEnv, ",") { + switch { + case strings.HasPrefix(item, ExternalDataInitPrefix): + externalData.Init, parsingError = strconv.ParseBool(item[len(ExternalDataInitPrefix):]) + case strings.HasPrefix(item, ExternalDataContainerNamePrefix): + externalData.ContainerName = item[len(ExternalDataContainerNamePrefix):] + case strings.HasPrefix(item, ExternalDataPodUIDPrefix): + externalData.PodUID = item[len(ExternalDataPodUIDPrefix):] + } + } + return externalData, parsingError +} diff --git a/comp/core/tagger/origindetection/origindetection_test.go b/comp/core/tagger/origindetection/origindetection_test.go new file mode 100644 index 0000000000000..a873093f6191b --- /dev/null +++ b/comp/core/tagger/origindetection/origindetection_test.go @@ -0,0 +1,81 @@ +// 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 origindetection contains the types and functions used for Origin Detection. +package origindetection + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestParseExternalData(t *testing.T) { + tests := []struct { + name string + externalEnv string + expectedData ExternalData + expectedError bool + }{ + { + name: "Empty external data", + externalEnv: "", + expectedData: ExternalData{ + Init: false, + ContainerName: "", + PodUID: "", + }, + expectedError: false, + }, + { + name: "Valid external data with Init", + externalEnv: "it-true,cn-container-name,pu-12345678-90ab-cdef-1234-567890abcdef", + expectedData: ExternalData{ + Init: true, + ContainerName: "container-name", + PodUID: "12345678-90ab-cdef-1234-567890abcdef", + }, + expectedError: false, + }, + { + name: "Invalid Init value", + externalEnv: "it-invalid,cn-container-name,pu-12345678-90ab-cdef-1234-567890abcdef", + expectedData: ExternalData{}, + expectedError: true, + }, + { + name: "Partial external data", + externalEnv: "cn-container-name", + expectedData: ExternalData{ + Init: false, + ContainerName: "container-name", + PodUID: "", + }, + expectedError: false, + }, + { + name: "Unrecognized prefix", + externalEnv: "unknown-prefix", + expectedData: ExternalData{ + Init: false, + ContainerName: "", + PodUID: "", + }, + expectedError: false, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + result, err := ParseExternalData(tc.externalEnv) + + if tc.expectedError { + assert.Error(t, err) + } else { + assert.Equal(t, tc.expectedData, result) + } + }) + } +} diff --git a/comp/core/tagger/server/server.go b/comp/core/tagger/server/server.go index 5e323c43c7ee2..34a719f32c440 100644 --- a/comp/core/tagger/server/server.go +++ b/comp/core/tagger/server/server.go @@ -17,6 +17,7 @@ import ( "github.com/google/uuid" tagger "github.com/DataDog/datadog-agent/comp/core/tagger/def" + "github.com/DataDog/datadog-agent/comp/core/tagger/origindetection" "github.com/DataDog/datadog-agent/comp/core/tagger/proto" "github.com/DataDog/datadog-agent/comp/core/tagger/types" pb "github.com/DataDog/datadog-agent/pkg/proto/pbgo/core" @@ -165,3 +166,22 @@ func (s *Server) TaggerFetchEntity(_ context.Context, in *pb.FetchEntityRequest) Tags: tags, }, nil } + +// TaggerGenerateContainerIDFromOriginInfo request the generation of a container ID from external data from the Tagger. +// This function takes an Origin Info but only uses the ExternalData part of it, this is done for backward compatibility. +func (s *Server) TaggerGenerateContainerIDFromOriginInfo(_ context.Context, in *pb.GenerateContainerIDFromOriginInfoRequest) (*pb.GenerateContainerIDFromOriginInfoResponse, error) { + generatedContainerID, err := s.taggerComponent.GenerateContainerIDFromOriginInfo(origindetection.OriginInfo{ + ExternalData: origindetection.ExternalData{ + Init: *in.ExternalData.Init, + ContainerName: *in.ExternalData.ContainerName, + PodUID: *in.ExternalData.PodUID, + }, + }) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%s", err) + } + + return &pb.GenerateContainerIDFromOriginInfoResponse{ + ContainerID: generatedContainerID, + }, nil +} diff --git a/comp/core/tagger/telemetry/telemetry.go b/comp/core/tagger/telemetry/telemetry.go index 8b50202dd2e99..97c7153d9c052 100644 --- a/comp/core/tagger/telemetry/telemetry.go +++ b/comp/core/tagger/telemetry/telemetry.go @@ -61,6 +61,10 @@ type Store struct { // notification with a group of events. Receives telemetry.Counter + // OriginInfoRequests tracks the number of requests to the Tagger + // to generate a container ID from Origin Info. + OriginInfoRequests telemetry.Counter + LowCardinalityQueries CardinalityTelemetry OrchestratorCardinalityQueries CardinalityTelemetry HighCardinalityQueries CardinalityTelemetry @@ -131,6 +135,12 @@ func NewStore(telemetryComp telemetry.Component) *Store { []string{}, "Number of of times the tagger has received a notification with a group of events", telemetry.Options{NoDoubleUnderscoreSep: true}), + // OriginInfoRequests tracks the number of requests to the tagger + // to generate a container ID from origin info. + OriginInfoRequests: telemetryComp.NewCounterWithOpts(subsystem, "origin_info_requests", + []string{"status"}, "Number of requests to the tagger to generate a container ID from origin info.", + telemetry.Options{NoDoubleUnderscoreSep: true}), + LowCardinalityQueries: newCardinalityTelemetry(queries, types.LowCardinalityString), OrchestratorCardinalityQueries: newCardinalityTelemetry(queries, types.OrchestratorCardinalityString), HighCardinalityQueries: newCardinalityTelemetry(queries, types.HighCardinalityString), diff --git a/go.mod b/go.mod index e44ee3eddac45..e8175e8cf0efb 100644 --- a/go.mod +++ b/go.mod @@ -37,6 +37,7 @@ replace ( github.com/DataDog/datadog-agent/comp/core/secrets => ./comp/core/secrets github.com/DataDog/datadog-agent/comp/core/status => ./comp/core/status github.com/DataDog/datadog-agent/comp/core/status/statusimpl => ./comp/core/status/statusimpl + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection => ./comp/core/tagger/origindetection github.com/DataDog/datadog-agent/comp/core/tagger/tags => ./comp/core/tagger/tags github.com/DataDog/datadog-agent/comp/core/tagger/types => ./comp/core/tagger/types github.com/DataDog/datadog-agent/comp/core/tagger/utils => ./comp/core/tagger/utils @@ -653,6 +654,7 @@ require ( github.com/DataDog/datadog-agent/comp/core/secrets v0.59.0 github.com/DataDog/datadog-agent/comp/core/status v0.59.0-rc.6 github.com/DataDog/datadog-agent/comp/core/status/statusimpl v0.56.0-rc.3 + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/core/tagger/tags v0.0.0-00010101000000-000000000000 github.com/DataDog/datadog-agent/comp/core/tagger/types v0.59.0 github.com/DataDog/datadog-agent/comp/core/telemetry v0.59.0 diff --git a/go.work b/go.work index 880b236237204..c7c6ebf27d3ff 100644 --- a/go.work +++ b/go.work @@ -17,6 +17,7 @@ use ( comp/core/secrets comp/core/status comp/core/status/statusimpl + comp/core/tagger/origindetection comp/core/tagger/tags comp/core/tagger/types comp/core/tagger/utils diff --git a/modules.yml b/modules.yml index f9550b4b82943..291db46bc53cc 100644 --- a/modules.yml +++ b/modules.yml @@ -35,6 +35,7 @@ modules: comp/core/status: used_by_otel: true comp/core/status/statusimpl: default + comp/core/tagger/origindetection: default comp/core/tagger/tags: used_by_otel: true comp/core/tagger/types: diff --git a/pkg/proto/datadog/api/v1/api.proto b/pkg/proto/datadog/api/v1/api.proto index 1ffbfa02aa562..3e53e3aa7c773 100644 --- a/pkg/proto/datadog/api/v1/api.proto +++ b/pkg/proto/datadog/api/v1/api.proto @@ -51,6 +51,22 @@ service AgentSecure { }; }; + // Generates a container ID from Origin Info. + // can be called through the HTTP gateway, and entity will be returned as JSON: + // $ curl -H "authorization: Bearer $(cat /etc/datadog-agent/auth_token)" \ + // -XPOST -k -H "Content-Type: application/json" \ + // --data '{"externalDataInit": false,"externalDataPodUID": "54383382-cea3-49e3-9dda-325436ddd5b8","externalDataContainerName": "dd-trace-py"}' \ + // https://localhost:5001/v1/grpc/tagger/generate_container_id_from_origin_info + // { + // "containerID":"c9fd60251b5237467462dad48999815eb0025f367c6e1abe91e0bd787d5915fc" + // } + rpc TaggerGenerateContainerIDFromOriginInfo(datadog.model.v1.GenerateContainerIDFromOriginInfoRequest) returns (datadog.model.v1.GenerateContainerIDFromOriginInfoResponse) { + option (google.api.http) = { + post: "/v1/grpc/tagger/generate_container_id_from_origin_info" + body: "*" + }; + }; + // fetches an entity from the Tagger with the desired cardinality tags. // can be called through the HTTP gateway, and entity will be returned as JSON: // $ curl -H "authorization: Bearer $(cat /etc/datadog-agent/auth_token)" \ diff --git a/pkg/proto/datadog/model/v1/model.proto b/pkg/proto/datadog/model/v1/model.proto index 506f90fa95702..2d4f96d208483 100644 --- a/pkg/proto/datadog/model/v1/model.proto +++ b/pkg/proto/datadog/model/v1/model.proto @@ -73,6 +73,30 @@ message Entity { repeated string standardTags = 6; } +message GenerateContainerIDFromOriginInfoRequest { + // Nested message for the local data + message LocalData { + optional uint32 processID = 1; // Process ID of the container process on the host. + optional string containerID = 2; // Container ID send from the client. + optional uint64 inode = 3; // Cgroup inode of the container. + optional string podUID = 4; // Pod UID send from the client. + } + + // Nested message for the external data + message ExternalData { + optional bool init = 1; // Init is true if the container is an init container. + optional string containerName = 2; // Container name in the Kubernetes Pod spec. + optional string podUID = 3; // Pod UID in the Kubernetes Pod spec. + } + + optional LocalData localData = 1; // Local data for the container, generated by the client. + optional ExternalData externalData = 2; // External data for the container, generated by the Admission Controller. +} + +message GenerateContainerIDFromOriginInfoResponse { + string containerID = 1; +} + message FetchEntityRequest { EntityId id = 1; TagCardinality cardinality = 2; diff --git a/pkg/proto/pbgo/core/api.pb.go b/pkg/proto/pbgo/core/api.pb.go index 57de2030c617e..f99b9d0f48fbf 100644 --- a/pkg/proto/pbgo/core/api.pb.go +++ b/pkg/proto/pbgo/core/api.pb.go @@ -53,7 +53,7 @@ var file_datadog_api_v1_api_proto_rawDesc = []byte{ 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x48, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x15, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0f, 0x12, 0x0d, 0x2f, 0x76, 0x31, - 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x32, 0x80, 0x0d, 0x0a, 0x0b, 0x41, + 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x68, 0x6f, 0x73, 0x74, 0x32, 0xe8, 0x0e, 0x0a, 0x0b, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x14, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, @@ -63,176 +63,194 @@ var file_datadog_api_v1_api_proto_rawDesc = []byte{ 0x61, 0x6d, 0x54, 0x61, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x24, 0x3a, 0x01, 0x2a, 0x22, 0x1f, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x30, 0x01, 0x12, 0x89, 0x01, 0x0a, - 0x11, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, - 0x74, 0x79, 0x12, 0x24, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, - 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, - 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, - 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x66, 0x65, 0x74, 0x63, - 0x68, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x9b, 0x01, 0x0a, 0x17, 0x44, 0x6f, 0x67, - 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x54, 0x72, 0x69, - 0x67, 0x67, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, - 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x54, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, - 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, - 0x2e, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, - 0x01, 0x2a, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, 0x67, - 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x2f, 0x63, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x2f, 0x74, - 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x8c, 0x01, 0x0a, 0x17, 0x44, 0x6f, 0x67, 0x73, 0x74, - 0x61, 0x74, 0x73, 0x64, 0x53, 0x65, 0x74, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, - 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x1a, 0x25, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, - 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, - 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, - 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x2f, 0x63, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x8f, 0x01, 0x0a, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x74, - 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, - 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, - 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, - 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, - 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, 0x78, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, - 0x79, 0x1a, 0x26, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, - 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x94, 0x01, 0x0a, 0x12, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x48, 0x41, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, - 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, - 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x68, 0x61, 0x12, 0x7d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x48, 0x41, 0x12, 0x16, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, - 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x01, 0x2a, 0x22, 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, - 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x5f, 0x68, 0x61, 0x12, 0xb3, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, - 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, - 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, - 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x57, 0x6f, - 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, - 0x67, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x57, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x2a, 0x3a, 0x01, 0x2a, 0x22, 0x25, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x77, - 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x2f, 0x73, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x30, 0x01, 0x12, 0xaf, 0x01, - 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, - 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, - 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, - 0x3a, 0x01, 0x2a, 0x22, 0x2a, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, - 0x9b, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x32, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, - 0x61, 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x75, - 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x2a, 0x3a, 0x01, 0x2a, 0x22, 0x25, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x61, - 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x73, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x30, 0x01, 0x32, 0xe6, 0x01, - 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x6f, 0x0a, - 0x10, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, - 0x73, 0x12, 0x2c, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, - 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, - 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x2d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, - 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, - 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x61, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, - 0x29, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, - 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x61, 0x72, 0x65, 0x46, 0x69, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x61, 0x74, - 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, - 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x61, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x15, 0x5a, 0x13, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x67, 0x6f, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x30, 0x01, 0x12, 0xe5, 0x01, 0x0a, + 0x27, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, 0x4f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, + 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, + 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, + 0x72, 0x6f, 0x6d, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x3b, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x41, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x3b, 0x3a, 0x01, 0x2a, 0x22, 0x36, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x61, 0x67, 0x67, 0x65, 0x72, 0x2f, 0x67, 0x65, + 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x5f, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x5f, + 0x69, 0x6e, 0x66, 0x6f, 0x12, 0x89, 0x01, 0x0a, 0x11, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x46, + 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x24, 0x2e, 0x64, 0x61, 0x74, + 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, + 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x25, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, + 0x2e, 0x76, 0x31, 0x2e, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x3a, + 0x01, 0x2a, 0x22, 0x1c, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x74, 0x61, 0x67, + 0x67, 0x65, 0x72, 0x2f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, + 0x12, 0x9b, 0x01, 0x0a, 0x17, 0x44, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x43, 0x61, + 0x70, 0x74, 0x75, 0x72, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x27, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, + 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, + 0x54, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x2d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x3a, 0x01, 0x2a, 0x22, 0x22, 0x2f, 0x76, 0x31, 0x2f, + 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x2f, 0x63, + 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x69, 0x67, 0x67, 0x65, 0x72, 0x12, 0x8c, + 0x01, 0x0a, 0x17, 0x44, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x53, 0x65, 0x74, 0x54, + 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1d, 0x2e, 0x64, 0x61, 0x74, + 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, + 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x1a, 0x25, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, + 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x64, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x2f, + 0x63, 0x61, 0x70, 0x74, 0x75, 0x72, 0x65, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x8f, 0x01, + 0x0a, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x73, 0x12, 0x27, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, + 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, + 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x22, 0x3a, 0x01, 0x2a, + 0x22, 0x1d, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x12, + 0x78, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x47, 0x65, 0x74, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x26, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x20, 0x3a, 0x01, 0x2a, 0x22, 0x1b, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x94, 0x01, 0x0a, 0x12, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x48, 0x41, + 0x12, 0x27, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x3a, 0x01, 0x2a, 0x22, 0x20, + 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x73, 0x5f, 0x68, 0x61, + 0x12, 0x7d, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x48, 0x41, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x26, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x29, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x23, 0x3a, 0x01, 0x2a, 0x22, + 0x1e, 0x2f, 0x76, 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x68, 0x61, 0x12, + 0xb3, 0x01, 0x0a, 0x1a, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, + 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x12, 0x2f, + 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, + 0x64, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, 0x65, + 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x30, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, + 0x61, 0x64, 0x6d, 0x65, 0x74, 0x61, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, + 0x65, 0x74, 0x61, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x3a, 0x01, 0x2a, 0x22, 0x25, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x6c, 0x6f, 0x61, 0x64, 0x6d, + 0x65, 0x74, 0x61, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x65, 0x6e, 0x74, 0x69, 0x74, + 0x69, 0x65, 0x73, 0x30, 0x01, 0x12, 0xaf, 0x01, 0x0a, 0x13, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, + 0x65, 0x72, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x2e, + 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, + 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6d, 0x6f, + 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, + 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, + 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x35, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2f, 0x3a, 0x01, 0x2a, 0x22, 0x2a, 0x2f, 0x76, 0x31, + 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, + 0x74, 0x2f, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x5f, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x9b, 0x01, 0x0a, 0x19, 0x41, 0x75, 0x74, 0x6f, + 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x43, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x32, 0x2e, + 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x61, 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, + 0x65, 0x72, 0x79, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x22, 0x30, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x2a, 0x3a, 0x01, 0x2a, 0x22, 0x25, 0x2f, 0x76, + 0x31, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x61, 0x75, 0x74, 0x6f, 0x64, 0x69, 0x73, 0x63, 0x6f, + 0x76, 0x65, 0x72, 0x79, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x73, 0x30, 0x01, 0x32, 0xe6, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, + 0x41, 0x67, 0x65, 0x6e, 0x74, 0x12, 0x6f, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x12, 0x2c, 0x2e, 0x64, 0x61, 0x74, 0x61, + 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, + 0x47, 0x65, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2d, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, + 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x44, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x66, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x61, + 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x29, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, + 0x67, 0x2e, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, + 0x74, 0x46, 0x6c, 0x61, 0x72, 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x2a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x72, 0x65, 0x6d, + 0x6f, 0x74, 0x65, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x6c, 0x61, 0x72, + 0x65, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x15, + 0x5a, 0x13, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x67, 0x6f, + 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var file_datadog_api_v1_api_proto_goTypes = []any{ - (*HostnameRequest)(nil), // 0: datadog.model.v1.HostnameRequest - (*StreamTagsRequest)(nil), // 1: datadog.model.v1.StreamTagsRequest - (*FetchEntityRequest)(nil), // 2: datadog.model.v1.FetchEntityRequest - (*CaptureTriggerRequest)(nil), // 3: datadog.model.v1.CaptureTriggerRequest - (*TaggerState)(nil), // 4: datadog.model.v1.TaggerState - (*ClientGetConfigsRequest)(nil), // 5: datadog.config.ClientGetConfigsRequest - (*empty.Empty)(nil), // 6: google.protobuf.Empty - (*WorkloadmetaStreamRequest)(nil), // 7: datadog.workloadmeta.WorkloadmetaStreamRequest - (*RegisterRemoteAgentRequest)(nil), // 8: datadog.remoteagent.RegisterRemoteAgentRequest - (*GetStatusDetailsRequest)(nil), // 9: datadog.remoteagent.GetStatusDetailsRequest - (*GetFlareFilesRequest)(nil), // 10: datadog.remoteagent.GetFlareFilesRequest - (*HostnameReply)(nil), // 11: datadog.model.v1.HostnameReply - (*StreamTagsResponse)(nil), // 12: datadog.model.v1.StreamTagsResponse - (*FetchEntityResponse)(nil), // 13: datadog.model.v1.FetchEntityResponse - (*CaptureTriggerResponse)(nil), // 14: datadog.model.v1.CaptureTriggerResponse - (*TaggerStateResponse)(nil), // 15: datadog.model.v1.TaggerStateResponse - (*ClientGetConfigsResponse)(nil), // 16: datadog.config.ClientGetConfigsResponse - (*GetStateConfigResponse)(nil), // 17: datadog.config.GetStateConfigResponse - (*WorkloadmetaStreamResponse)(nil), // 18: datadog.workloadmeta.WorkloadmetaStreamResponse - (*RegisterRemoteAgentResponse)(nil), // 19: datadog.remoteagent.RegisterRemoteAgentResponse - (*AutodiscoveryStreamResponse)(nil), // 20: datadog.autodiscovery.AutodiscoveryStreamResponse - (*GetStatusDetailsResponse)(nil), // 21: datadog.remoteagent.GetStatusDetailsResponse - (*GetFlareFilesResponse)(nil), // 22: datadog.remoteagent.GetFlareFilesResponse + (*HostnameRequest)(nil), // 0: datadog.model.v1.HostnameRequest + (*StreamTagsRequest)(nil), // 1: datadog.model.v1.StreamTagsRequest + (*GenerateContainerIDFromOriginInfoRequest)(nil), // 2: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest + (*FetchEntityRequest)(nil), // 3: datadog.model.v1.FetchEntityRequest + (*CaptureTriggerRequest)(nil), // 4: datadog.model.v1.CaptureTriggerRequest + (*TaggerState)(nil), // 5: datadog.model.v1.TaggerState + (*ClientGetConfigsRequest)(nil), // 6: datadog.config.ClientGetConfigsRequest + (*empty.Empty)(nil), // 7: google.protobuf.Empty + (*WorkloadmetaStreamRequest)(nil), // 8: datadog.workloadmeta.WorkloadmetaStreamRequest + (*RegisterRemoteAgentRequest)(nil), // 9: datadog.remoteagent.RegisterRemoteAgentRequest + (*GetStatusDetailsRequest)(nil), // 10: datadog.remoteagent.GetStatusDetailsRequest + (*GetFlareFilesRequest)(nil), // 11: datadog.remoteagent.GetFlareFilesRequest + (*HostnameReply)(nil), // 12: datadog.model.v1.HostnameReply + (*StreamTagsResponse)(nil), // 13: datadog.model.v1.StreamTagsResponse + (*GenerateContainerIDFromOriginInfoResponse)(nil), // 14: datadog.model.v1.GenerateContainerIDFromOriginInfoResponse + (*FetchEntityResponse)(nil), // 15: datadog.model.v1.FetchEntityResponse + (*CaptureTriggerResponse)(nil), // 16: datadog.model.v1.CaptureTriggerResponse + (*TaggerStateResponse)(nil), // 17: datadog.model.v1.TaggerStateResponse + (*ClientGetConfigsResponse)(nil), // 18: datadog.config.ClientGetConfigsResponse + (*GetStateConfigResponse)(nil), // 19: datadog.config.GetStateConfigResponse + (*WorkloadmetaStreamResponse)(nil), // 20: datadog.workloadmeta.WorkloadmetaStreamResponse + (*RegisterRemoteAgentResponse)(nil), // 21: datadog.remoteagent.RegisterRemoteAgentResponse + (*AutodiscoveryStreamResponse)(nil), // 22: datadog.autodiscovery.AutodiscoveryStreamResponse + (*GetStatusDetailsResponse)(nil), // 23: datadog.remoteagent.GetStatusDetailsResponse + (*GetFlareFilesResponse)(nil), // 24: datadog.remoteagent.GetFlareFilesResponse } var file_datadog_api_v1_api_proto_depIdxs = []int32{ 0, // 0: datadog.api.v1.Agent.GetHostname:input_type -> datadog.model.v1.HostnameRequest 1, // 1: datadog.api.v1.AgentSecure.TaggerStreamEntities:input_type -> datadog.model.v1.StreamTagsRequest - 2, // 2: datadog.api.v1.AgentSecure.TaggerFetchEntity:input_type -> datadog.model.v1.FetchEntityRequest - 3, // 3: datadog.api.v1.AgentSecure.DogstatsdCaptureTrigger:input_type -> datadog.model.v1.CaptureTriggerRequest - 4, // 4: datadog.api.v1.AgentSecure.DogstatsdSetTaggerState:input_type -> datadog.model.v1.TaggerState - 5, // 5: datadog.api.v1.AgentSecure.ClientGetConfigs:input_type -> datadog.config.ClientGetConfigsRequest - 6, // 6: datadog.api.v1.AgentSecure.GetConfigState:input_type -> google.protobuf.Empty - 5, // 7: datadog.api.v1.AgentSecure.ClientGetConfigsHA:input_type -> datadog.config.ClientGetConfigsRequest - 6, // 8: datadog.api.v1.AgentSecure.GetConfigStateHA:input_type -> google.protobuf.Empty - 7, // 9: datadog.api.v1.AgentSecure.WorkloadmetaStreamEntities:input_type -> datadog.workloadmeta.WorkloadmetaStreamRequest - 8, // 10: datadog.api.v1.AgentSecure.RegisterRemoteAgent:input_type -> datadog.remoteagent.RegisterRemoteAgentRequest - 6, // 11: datadog.api.v1.AgentSecure.AutodiscoveryStreamConfig:input_type -> google.protobuf.Empty - 9, // 12: datadog.api.v1.RemoteAgent.GetStatusDetails:input_type -> datadog.remoteagent.GetStatusDetailsRequest - 10, // 13: datadog.api.v1.RemoteAgent.GetFlareFiles:input_type -> datadog.remoteagent.GetFlareFilesRequest - 11, // 14: datadog.api.v1.Agent.GetHostname:output_type -> datadog.model.v1.HostnameReply - 12, // 15: datadog.api.v1.AgentSecure.TaggerStreamEntities:output_type -> datadog.model.v1.StreamTagsResponse - 13, // 16: datadog.api.v1.AgentSecure.TaggerFetchEntity:output_type -> datadog.model.v1.FetchEntityResponse - 14, // 17: datadog.api.v1.AgentSecure.DogstatsdCaptureTrigger:output_type -> datadog.model.v1.CaptureTriggerResponse - 15, // 18: datadog.api.v1.AgentSecure.DogstatsdSetTaggerState:output_type -> datadog.model.v1.TaggerStateResponse - 16, // 19: datadog.api.v1.AgentSecure.ClientGetConfigs:output_type -> datadog.config.ClientGetConfigsResponse - 17, // 20: datadog.api.v1.AgentSecure.GetConfigState:output_type -> datadog.config.GetStateConfigResponse - 16, // 21: datadog.api.v1.AgentSecure.ClientGetConfigsHA:output_type -> datadog.config.ClientGetConfigsResponse - 17, // 22: datadog.api.v1.AgentSecure.GetConfigStateHA:output_type -> datadog.config.GetStateConfigResponse - 18, // 23: datadog.api.v1.AgentSecure.WorkloadmetaStreamEntities:output_type -> datadog.workloadmeta.WorkloadmetaStreamResponse - 19, // 24: datadog.api.v1.AgentSecure.RegisterRemoteAgent:output_type -> datadog.remoteagent.RegisterRemoteAgentResponse - 20, // 25: datadog.api.v1.AgentSecure.AutodiscoveryStreamConfig:output_type -> datadog.autodiscovery.AutodiscoveryStreamResponse - 21, // 26: datadog.api.v1.RemoteAgent.GetStatusDetails:output_type -> datadog.remoteagent.GetStatusDetailsResponse - 22, // 27: datadog.api.v1.RemoteAgent.GetFlareFiles:output_type -> datadog.remoteagent.GetFlareFilesResponse - 14, // [14:28] is the sub-list for method output_type - 0, // [0:14] is the sub-list for method input_type + 2, // 2: datadog.api.v1.AgentSecure.TaggerGenerateContainerIDFromOriginInfo:input_type -> datadog.model.v1.GenerateContainerIDFromOriginInfoRequest + 3, // 3: datadog.api.v1.AgentSecure.TaggerFetchEntity:input_type -> datadog.model.v1.FetchEntityRequest + 4, // 4: datadog.api.v1.AgentSecure.DogstatsdCaptureTrigger:input_type -> datadog.model.v1.CaptureTriggerRequest + 5, // 5: datadog.api.v1.AgentSecure.DogstatsdSetTaggerState:input_type -> datadog.model.v1.TaggerState + 6, // 6: datadog.api.v1.AgentSecure.ClientGetConfigs:input_type -> datadog.config.ClientGetConfigsRequest + 7, // 7: datadog.api.v1.AgentSecure.GetConfigState:input_type -> google.protobuf.Empty + 6, // 8: datadog.api.v1.AgentSecure.ClientGetConfigsHA:input_type -> datadog.config.ClientGetConfigsRequest + 7, // 9: datadog.api.v1.AgentSecure.GetConfigStateHA:input_type -> google.protobuf.Empty + 8, // 10: datadog.api.v1.AgentSecure.WorkloadmetaStreamEntities:input_type -> datadog.workloadmeta.WorkloadmetaStreamRequest + 9, // 11: datadog.api.v1.AgentSecure.RegisterRemoteAgent:input_type -> datadog.remoteagent.RegisterRemoteAgentRequest + 7, // 12: datadog.api.v1.AgentSecure.AutodiscoveryStreamConfig:input_type -> google.protobuf.Empty + 10, // 13: datadog.api.v1.RemoteAgent.GetStatusDetails:input_type -> datadog.remoteagent.GetStatusDetailsRequest + 11, // 14: datadog.api.v1.RemoteAgent.GetFlareFiles:input_type -> datadog.remoteagent.GetFlareFilesRequest + 12, // 15: datadog.api.v1.Agent.GetHostname:output_type -> datadog.model.v1.HostnameReply + 13, // 16: datadog.api.v1.AgentSecure.TaggerStreamEntities:output_type -> datadog.model.v1.StreamTagsResponse + 14, // 17: datadog.api.v1.AgentSecure.TaggerGenerateContainerIDFromOriginInfo:output_type -> datadog.model.v1.GenerateContainerIDFromOriginInfoResponse + 15, // 18: datadog.api.v1.AgentSecure.TaggerFetchEntity:output_type -> datadog.model.v1.FetchEntityResponse + 16, // 19: datadog.api.v1.AgentSecure.DogstatsdCaptureTrigger:output_type -> datadog.model.v1.CaptureTriggerResponse + 17, // 20: datadog.api.v1.AgentSecure.DogstatsdSetTaggerState:output_type -> datadog.model.v1.TaggerStateResponse + 18, // 21: datadog.api.v1.AgentSecure.ClientGetConfigs:output_type -> datadog.config.ClientGetConfigsResponse + 19, // 22: datadog.api.v1.AgentSecure.GetConfigState:output_type -> datadog.config.GetStateConfigResponse + 18, // 23: datadog.api.v1.AgentSecure.ClientGetConfigsHA:output_type -> datadog.config.ClientGetConfigsResponse + 19, // 24: datadog.api.v1.AgentSecure.GetConfigStateHA:output_type -> datadog.config.GetStateConfigResponse + 20, // 25: datadog.api.v1.AgentSecure.WorkloadmetaStreamEntities:output_type -> datadog.workloadmeta.WorkloadmetaStreamResponse + 21, // 26: datadog.api.v1.AgentSecure.RegisterRemoteAgent:output_type -> datadog.remoteagent.RegisterRemoteAgentResponse + 22, // 27: datadog.api.v1.AgentSecure.AutodiscoveryStreamConfig:output_type -> datadog.autodiscovery.AutodiscoveryStreamResponse + 23, // 28: datadog.api.v1.RemoteAgent.GetStatusDetails:output_type -> datadog.remoteagent.GetStatusDetailsResponse + 24, // 29: datadog.api.v1.RemoteAgent.GetFlareFiles:output_type -> datadog.remoteagent.GetFlareFilesResponse + 15, // [15:30] is the sub-list for method output_type + 0, // [0:15] is the sub-list for method input_type 0, // [0:0] is the sub-list for extension type_name 0, // [0:0] is the sub-list for extension extendee 0, // [0:0] is the sub-list for field type_name @@ -375,6 +393,17 @@ type AgentSecureClient interface { // } // } TaggerStreamEntities(ctx context.Context, in *StreamTagsRequest, opts ...grpc.CallOption) (AgentSecure_TaggerStreamEntitiesClient, error) + // Generates a container ID from Origin Info. + // can be called through the HTTP gateway, and entity will be returned as JSON: + // + // $ curl -H "authorization: Bearer $(cat /etc/datadog-agent/auth_token)" \ + // -XPOST -k -H "Content-Type: application/json" \ + // --data '{"externalDataInit": false,"externalDataPodUID": "54383382-cea3-49e3-9dda-325436ddd5b8","externalDataContainerName": "dd-trace-py"}' \ + // https://localhost:5001/v1/grpc/tagger/generate_container_id_from_origin_info + // { + // "containerID":"c9fd60251b5237467462dad48999815eb0025f367c6e1abe91e0bd787d5915fc" + // } + TaggerGenerateContainerIDFromOriginInfo(ctx context.Context, in *GenerateContainerIDFromOriginInfoRequest, opts ...grpc.CallOption) (*GenerateContainerIDFromOriginInfoResponse, error) // fetches an entity from the Tagger with the desired cardinality tags. // can be called through the HTTP gateway, and entity will be returned as JSON: // @@ -477,6 +506,15 @@ func (x *agentSecureTaggerStreamEntitiesClient) Recv() (*StreamTagsResponse, err return m, nil } +func (c *agentSecureClient) TaggerGenerateContainerIDFromOriginInfo(ctx context.Context, in *GenerateContainerIDFromOriginInfoRequest, opts ...grpc.CallOption) (*GenerateContainerIDFromOriginInfoResponse, error) { + out := new(GenerateContainerIDFromOriginInfoResponse) + err := c.cc.Invoke(ctx, "/datadog.api.v1.AgentSecure/TaggerGenerateContainerIDFromOriginInfo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *agentSecureClient) TaggerFetchEntity(ctx context.Context, in *FetchEntityRequest, opts ...grpc.CallOption) (*FetchEntityResponse, error) { out := new(FetchEntityResponse) err := c.cc.Invoke(ctx, "/datadog.api.v1.AgentSecure/TaggerFetchEntity", in, out, opts...) @@ -637,6 +675,17 @@ type AgentSecureServer interface { // } // } TaggerStreamEntities(*StreamTagsRequest, AgentSecure_TaggerStreamEntitiesServer) error + // Generates a container ID from Origin Info. + // can be called through the HTTP gateway, and entity will be returned as JSON: + // + // $ curl -H "authorization: Bearer $(cat /etc/datadog-agent/auth_token)" \ + // -XPOST -k -H "Content-Type: application/json" \ + // --data '{"externalDataInit": false,"externalDataPodUID": "54383382-cea3-49e3-9dda-325436ddd5b8","externalDataContainerName": "dd-trace-py"}' \ + // https://localhost:5001/v1/grpc/tagger/generate_container_id_from_origin_info + // { + // "containerID":"c9fd60251b5237467462dad48999815eb0025f367c6e1abe91e0bd787d5915fc" + // } + TaggerGenerateContainerIDFromOriginInfo(context.Context, *GenerateContainerIDFromOriginInfoRequest) (*GenerateContainerIDFromOriginInfoResponse, error) // fetches an entity from the Tagger with the desired cardinality tags. // can be called through the HTTP gateway, and entity will be returned as JSON: // @@ -706,6 +755,9 @@ type UnimplementedAgentSecureServer struct { func (*UnimplementedAgentSecureServer) TaggerStreamEntities(*StreamTagsRequest, AgentSecure_TaggerStreamEntitiesServer) error { return status.Errorf(codes.Unimplemented, "method TaggerStreamEntities not implemented") } +func (*UnimplementedAgentSecureServer) TaggerGenerateContainerIDFromOriginInfo(context.Context, *GenerateContainerIDFromOriginInfoRequest) (*GenerateContainerIDFromOriginInfoResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method TaggerGenerateContainerIDFromOriginInfo not implemented") +} func (*UnimplementedAgentSecureServer) TaggerFetchEntity(context.Context, *FetchEntityRequest) (*FetchEntityResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method TaggerFetchEntity not implemented") } @@ -762,6 +814,24 @@ func (x *agentSecureTaggerStreamEntitiesServer) Send(m *StreamTagsResponse) erro return x.ServerStream.SendMsg(m) } +func _AgentSecure_TaggerGenerateContainerIDFromOriginInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GenerateContainerIDFromOriginInfoRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AgentSecureServer).TaggerGenerateContainerIDFromOriginInfo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/datadog.api.v1.AgentSecure/TaggerGenerateContainerIDFromOriginInfo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AgentSecureServer).TaggerGenerateContainerIDFromOriginInfo(ctx, req.(*GenerateContainerIDFromOriginInfoRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AgentSecure_TaggerFetchEntity_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(FetchEntityRequest) if err := dec(in); err != nil { @@ -952,6 +1022,10 @@ var _AgentSecure_serviceDesc = grpc.ServiceDesc{ ServiceName: "datadog.api.v1.AgentSecure", HandlerType: (*AgentSecureServer)(nil), Methods: []grpc.MethodDesc{ + { + MethodName: "TaggerGenerateContainerIDFromOriginInfo", + Handler: _AgentSecure_TaggerGenerateContainerIDFromOriginInfo_Handler, + }, { MethodName: "TaggerFetchEntity", Handler: _AgentSecure_TaggerFetchEntity_Handler, diff --git a/pkg/proto/pbgo/core/api.pb.gw.go b/pkg/proto/pbgo/core/api.pb.gw.go index 0fac62a1daa72..7be078c4eb534 100644 --- a/pkg/proto/pbgo/core/api.pb.gw.go +++ b/pkg/proto/pbgo/core/api.pb.gw.go @@ -77,6 +77,40 @@ func request_AgentSecure_TaggerStreamEntities_0(ctx context.Context, marshaler r } +func request_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(ctx context.Context, marshaler runtime.Marshaler, client AgentSecureClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GenerateContainerIDFromOriginInfoRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.TaggerGenerateContainerIDFromOriginInfo(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(ctx context.Context, marshaler runtime.Marshaler, server AgentSecureServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq GenerateContainerIDFromOriginInfoRequest + var metadata runtime.ServerMetadata + + newReader, berr := utilities.IOReaderFactory(req.Body) + if berr != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr) + } + if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.TaggerGenerateContainerIDFromOriginInfo(ctx, &protoReq) + return msg, metadata, err + +} + func request_AgentSecure_TaggerFetchEntity_0(ctx context.Context, marshaler runtime.Marshaler, client AgentSecureClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { var protoReq FetchEntityRequest var metadata runtime.ServerMetadata @@ -444,6 +478,29 @@ func RegisterAgentSecureHandlerServer(ctx context.Context, mux *runtime.ServeMux return }) + mux.Handle("POST", pattern_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AgentSecure_TaggerFetchEntity_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -772,6 +829,26 @@ func RegisterAgentSecureHandlerClient(ctx context.Context, mux *runtime.ServeMux }) + mux.Handle("POST", pattern_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + mux.Handle("POST", pattern_AgentSecure_TaggerFetchEntity_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { ctx, cancel := context.WithCancel(req.Context()) defer cancel() @@ -978,6 +1055,8 @@ func RegisterAgentSecureHandlerClient(ctx context.Context, mux *runtime.ServeMux var ( pattern_AgentSecure_TaggerStreamEntities_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "grpc", "tagger", "stream_entities"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "grpc", "tagger", "generate_container_id_from_origin_info"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_AgentSecure_TaggerFetchEntity_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"v1", "grpc", "tagger", "fetch_entity"}, "", runtime.AssumeColonVerbOpt(true))) pattern_AgentSecure_DogstatsdCaptureTrigger_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"v1", "grpc", "dogstatsd", "capture", "trigger"}, "", runtime.AssumeColonVerbOpt(true))) @@ -1002,6 +1081,8 @@ var ( var ( forward_AgentSecure_TaggerStreamEntities_0 = runtime.ForwardResponseStream + forward_AgentSecure_TaggerGenerateContainerIDFromOriginInfo_0 = runtime.ForwardResponseMessage + forward_AgentSecure_TaggerFetchEntity_0 = runtime.ForwardResponseMessage forward_AgentSecure_DogstatsdCaptureTrigger_0 = runtime.ForwardResponseMessage diff --git a/pkg/proto/pbgo/core/model.pb.go b/pkg/proto/pbgo/core/model.pb.go index 3f85da4236b7f..016a2cf53e260 100644 --- a/pkg/proto/pbgo/core/model.pb.go +++ b/pkg/proto/pbgo/core/model.pb.go @@ -628,6 +628,104 @@ func (x *Entity) GetStandardTags() []string { return nil } +type GenerateContainerIDFromOriginInfoRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + LocalData *GenerateContainerIDFromOriginInfoRequest_LocalData `protobuf:"bytes,1,opt,name=localData,proto3,oneof" json:"localData,omitempty"` // Local data for the container, generated by the client. + ExternalData *GenerateContainerIDFromOriginInfoRequest_ExternalData `protobuf:"bytes,2,opt,name=externalData,proto3,oneof" json:"externalData,omitempty"` // External data for the container, generated by the Admission Controller. +} + +func (x *GenerateContainerIDFromOriginInfoRequest) Reset() { + *x = GenerateContainerIDFromOriginInfoRequest{} + mi := &file_datadog_model_v1_model_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateContainerIDFromOriginInfoRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateContainerIDFromOriginInfoRequest) ProtoMessage() {} + +func (x *GenerateContainerIDFromOriginInfoRequest) ProtoReflect() protoreflect.Message { + mi := &file_datadog_model_v1_model_proto_msgTypes[9] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateContainerIDFromOriginInfoRequest.ProtoReflect.Descriptor instead. +func (*GenerateContainerIDFromOriginInfoRequest) Descriptor() ([]byte, []int) { + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{9} +} + +func (x *GenerateContainerIDFromOriginInfoRequest) GetLocalData() *GenerateContainerIDFromOriginInfoRequest_LocalData { + if x != nil { + return x.LocalData + } + return nil +} + +func (x *GenerateContainerIDFromOriginInfoRequest) GetExternalData() *GenerateContainerIDFromOriginInfoRequest_ExternalData { + if x != nil { + return x.ExternalData + } + return nil +} + +type GenerateContainerIDFromOriginInfoResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ContainerID string `protobuf:"bytes,1,opt,name=containerID,proto3" json:"containerID,omitempty"` +} + +func (x *GenerateContainerIDFromOriginInfoResponse) Reset() { + *x = GenerateContainerIDFromOriginInfoResponse{} + mi := &file_datadog_model_v1_model_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateContainerIDFromOriginInfoResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateContainerIDFromOriginInfoResponse) ProtoMessage() {} + +func (x *GenerateContainerIDFromOriginInfoResponse) ProtoReflect() protoreflect.Message { + mi := &file_datadog_model_v1_model_proto_msgTypes[10] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateContainerIDFromOriginInfoResponse.ProtoReflect.Descriptor instead. +func (*GenerateContainerIDFromOriginInfoResponse) Descriptor() ([]byte, []int) { + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{10} +} + +func (x *GenerateContainerIDFromOriginInfoResponse) GetContainerID() string { + if x != nil { + return x.ContainerID + } + return "" +} + type FetchEntityRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -639,7 +737,7 @@ type FetchEntityRequest struct { func (x *FetchEntityRequest) Reset() { *x = FetchEntityRequest{} - mi := &file_datadog_model_v1_model_proto_msgTypes[9] + mi := &file_datadog_model_v1_model_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -651,7 +749,7 @@ func (x *FetchEntityRequest) String() string { func (*FetchEntityRequest) ProtoMessage() {} func (x *FetchEntityRequest) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[9] + mi := &file_datadog_model_v1_model_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -664,7 +762,7 @@ func (x *FetchEntityRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchEntityRequest.ProtoReflect.Descriptor instead. func (*FetchEntityRequest) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{9} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{11} } func (x *FetchEntityRequest) GetId() *EntityId { @@ -693,7 +791,7 @@ type FetchEntityResponse struct { func (x *FetchEntityResponse) Reset() { *x = FetchEntityResponse{} - mi := &file_datadog_model_v1_model_proto_msgTypes[10] + mi := &file_datadog_model_v1_model_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -705,7 +803,7 @@ func (x *FetchEntityResponse) String() string { func (*FetchEntityResponse) ProtoMessage() {} func (x *FetchEntityResponse) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[10] + mi := &file_datadog_model_v1_model_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -718,7 +816,7 @@ func (x *FetchEntityResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use FetchEntityResponse.ProtoReflect.Descriptor instead. func (*FetchEntityResponse) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{10} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{12} } func (x *FetchEntityResponse) GetId() *EntityId { @@ -753,7 +851,7 @@ type EntityId struct { func (x *EntityId) Reset() { *x = EntityId{} - mi := &file_datadog_model_v1_model_proto_msgTypes[11] + mi := &file_datadog_model_v1_model_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -765,7 +863,7 @@ func (x *EntityId) String() string { func (*EntityId) ProtoMessage() {} func (x *EntityId) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[11] + mi := &file_datadog_model_v1_model_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -778,7 +876,7 @@ func (x *EntityId) ProtoReflect() protoreflect.Message { // Deprecated: Use EntityId.ProtoReflect.Descriptor instead. func (*EntityId) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{11} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{13} } func (x *EntityId) GetPrefix() string { @@ -812,7 +910,7 @@ type UnixDogstatsdMsg struct { func (x *UnixDogstatsdMsg) Reset() { *x = UnixDogstatsdMsg{} - mi := &file_datadog_model_v1_model_proto_msgTypes[12] + mi := &file_datadog_model_v1_model_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -824,7 +922,7 @@ func (x *UnixDogstatsdMsg) String() string { func (*UnixDogstatsdMsg) ProtoMessage() {} func (x *UnixDogstatsdMsg) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[12] + mi := &file_datadog_model_v1_model_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -837,7 +935,7 @@ func (x *UnixDogstatsdMsg) ProtoReflect() protoreflect.Message { // Deprecated: Use UnixDogstatsdMsg.ProtoReflect.Descriptor instead. func (*UnixDogstatsdMsg) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{12} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{14} } func (x *UnixDogstatsdMsg) GetTimestamp() int64 { @@ -893,7 +991,7 @@ type TaggerState struct { func (x *TaggerState) Reset() { *x = TaggerState{} - mi := &file_datadog_model_v1_model_proto_msgTypes[13] + mi := &file_datadog_model_v1_model_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -905,7 +1003,7 @@ func (x *TaggerState) String() string { func (*TaggerState) ProtoMessage() {} func (x *TaggerState) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[13] + mi := &file_datadog_model_v1_model_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -918,7 +1016,7 @@ func (x *TaggerState) ProtoReflect() protoreflect.Message { // Deprecated: Use TaggerState.ProtoReflect.Descriptor instead. func (*TaggerState) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{13} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{15} } func (x *TaggerState) GetState() map[string]*Entity { @@ -945,7 +1043,7 @@ type TaggerStateResponse struct { func (x *TaggerStateResponse) Reset() { *x = TaggerStateResponse{} - mi := &file_datadog_model_v1_model_proto_msgTypes[14] + mi := &file_datadog_model_v1_model_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -957,7 +1055,7 @@ func (x *TaggerStateResponse) String() string { func (*TaggerStateResponse) ProtoMessage() {} func (x *TaggerStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_datadog_model_v1_model_proto_msgTypes[14] + mi := &file_datadog_model_v1_model_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -970,7 +1068,7 @@ func (x *TaggerStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use TaggerStateResponse.ProtoReflect.Descriptor instead. func (*TaggerStateResponse) Descriptor() ([]byte, []int) { - return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{14} + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{16} } func (x *TaggerStateResponse) GetLoaded() bool { @@ -980,6 +1078,138 @@ func (x *TaggerStateResponse) GetLoaded() bool { return false } +// Nested message for the local data +type GenerateContainerIDFromOriginInfoRequest_LocalData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + ProcessID *uint32 `protobuf:"varint,1,opt,name=processID,proto3,oneof" json:"processID,omitempty"` // Process ID of the container process on the host. + ContainerID *string `protobuf:"bytes,2,opt,name=containerID,proto3,oneof" json:"containerID,omitempty"` // Container ID send from the client. + Inode *uint64 `protobuf:"varint,3,opt,name=inode,proto3,oneof" json:"inode,omitempty"` // Cgroup inode of the container. + PodUID *string `protobuf:"bytes,4,opt,name=podUID,proto3,oneof" json:"podUID,omitempty"` // Pod UID send from the client. +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) Reset() { + *x = GenerateContainerIDFromOriginInfoRequest_LocalData{} + mi := &file_datadog_model_v1_model_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateContainerIDFromOriginInfoRequest_LocalData) ProtoMessage() {} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) ProtoReflect() protoreflect.Message { + mi := &file_datadog_model_v1_model_proto_msgTypes[17] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateContainerIDFromOriginInfoRequest_LocalData.ProtoReflect.Descriptor instead. +func (*GenerateContainerIDFromOriginInfoRequest_LocalData) Descriptor() ([]byte, []int) { + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{9, 0} +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) GetProcessID() uint32 { + if x != nil && x.ProcessID != nil { + return *x.ProcessID + } + return 0 +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) GetContainerID() string { + if x != nil && x.ContainerID != nil { + return *x.ContainerID + } + return "" +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) GetInode() uint64 { + if x != nil && x.Inode != nil { + return *x.Inode + } + return 0 +} + +func (x *GenerateContainerIDFromOriginInfoRequest_LocalData) GetPodUID() string { + if x != nil && x.PodUID != nil { + return *x.PodUID + } + return "" +} + +// Nested message for the external data +type GenerateContainerIDFromOriginInfoRequest_ExternalData struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Init *bool `protobuf:"varint,1,opt,name=init,proto3,oneof" json:"init,omitempty"` // Init is true if the container is an init container. + ContainerName *string `protobuf:"bytes,2,opt,name=containerName,proto3,oneof" json:"containerName,omitempty"` // Container name as seen by the Admission Controller. + PodUID *string `protobuf:"bytes,3,opt,name=podUID,proto3,oneof" json:"podUID,omitempty"` // Pod UID as seen by the Admission Controller. +} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) Reset() { + *x = GenerateContainerIDFromOriginInfoRequest_ExternalData{} + mi := &file_datadog_model_v1_model_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GenerateContainerIDFromOriginInfoRequest_ExternalData) ProtoMessage() {} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) ProtoReflect() protoreflect.Message { + mi := &file_datadog_model_v1_model_proto_msgTypes[18] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GenerateContainerIDFromOriginInfoRequest_ExternalData.ProtoReflect.Descriptor instead. +func (*GenerateContainerIDFromOriginInfoRequest_ExternalData) Descriptor() ([]byte, []int) { + return file_datadog_model_v1_model_proto_rawDescGZIP(), []int{9, 1} +} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) GetInit() bool { + if x != nil && x.Init != nil { + return *x.Init + } + return false +} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) GetContainerName() string { + if x != nil && x.ContainerName != nil { + return *x.ContainerName + } + return "" +} + +func (x *GenerateContainerIDFromOriginInfoRequest_ExternalData) GetPodUID() string { + if x != nil && x.PodUID != nil { + return *x.PodUID + } + return "" +} + var File_datadog_model_v1_model_proto protoreflect.FileDescriptor var file_datadog_model_v1_model_proto_rawDesc = []byte{ @@ -1055,70 +1285,115 @@ var file_datadog_model_v1_model_proto_rawDesc = []byte{ 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x54, 0x61, 0x67, 0x73, 0x12, 0x22, 0x0a, 0x0c, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x54, 0x61, 0x67, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x72, 0x64, 0x54, - 0x61, 0x67, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x12, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, - 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x02, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, - 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x42, 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, 0x61, - 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, - 0x61, 0x67, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x63, - 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x99, 0x01, 0x0a, 0x13, 0x46, - 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, - 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x42, - 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, - 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, - 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x34, 0x0a, 0x08, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, - 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, - 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0xc2, 0x01, 0x0a, - 0x10, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x4d, 0x73, - 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, - 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x70, - 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x24, 0x0a, - 0x0d, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x05, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x79, 0x53, - 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x79, - 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, - 0x79, 0x22, 0x9f, 0x02, 0x0a, 0x0b, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, - 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x41, 0x0a, 0x06, 0x70, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, + 0x61, 0x67, 0x73, 0x22, 0xff, 0x04, 0x0a, 0x28, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, + 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, 0x4f, + 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x67, 0x0a, 0x09, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x44, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, + 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, + 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, 0x4f, 0x72, + 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x09, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x12, 0x70, 0x0a, 0x0c, 0x65, 0x78, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x47, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, + 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x48, 0x01, 0x52, 0x0c, 0x65, 0x78, 0x74, 0x65, + 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x88, 0x01, 0x01, 0x1a, 0xc0, 0x01, 0x0a, 0x09, + 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, 0x21, 0x0a, 0x09, 0x70, 0x72, 0x6f, + 0x63, 0x65, 0x73, 0x73, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x48, 0x00, 0x52, 0x09, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x44, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0b, + 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x48, 0x01, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, + 0x88, 0x01, 0x01, 0x12, 0x19, 0x0a, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x48, 0x02, 0x52, 0x05, 0x69, 0x6e, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x12, 0x1b, + 0x0a, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, + 0x52, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, + 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x49, 0x44, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x69, 0x6e, + 0x6f, 0x64, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x1a, 0x95, + 0x01, 0x0a, 0x0c, 0x45, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x44, 0x61, 0x74, 0x61, 0x12, + 0x17, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, + 0x04, 0x69, 0x6e, 0x69, 0x74, 0x88, 0x01, 0x01, 0x12, 0x29, 0x0a, 0x0d, 0x63, 0x6f, 0x6e, 0x74, + 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x48, + 0x01, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, + 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x06, 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x88, 0x01, 0x01, + 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x10, 0x0a, 0x0e, 0x5f, 0x63, 0x6f, + 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x70, 0x6f, 0x64, 0x55, 0x49, 0x44, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x44, 0x61, 0x74, 0x61, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, + 0x6c, 0x44, 0x61, 0x74, 0x61, 0x22, 0x4d, 0x0a, 0x29, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, + 0x65, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x44, 0x46, 0x72, 0x6f, 0x6d, + 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x49, + 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, + 0x65, 0x72, 0x49, 0x44, 0x22, 0x84, 0x01, 0x0a, 0x12, 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, + 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2a, 0x0a, 0x02, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, + 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, 0x42, 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, + 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, + 0x54, 0x61, 0x67, 0x43, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0b, + 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x22, 0x99, 0x01, 0x0a, 0x13, + 0x46, 0x65, 0x74, 0x63, 0x68, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, + 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x49, 0x64, 0x52, 0x02, 0x69, 0x64, 0x12, + 0x42, 0x0a, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x43, 0x61, 0x72, 0x64, 0x69, + 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x52, 0x0b, 0x63, 0x61, 0x72, 0x64, 0x69, 0x6e, 0x61, 0x6c, + 0x69, 0x74, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0x34, 0x0a, 0x08, 0x45, 0x6e, 0x74, 0x69, 0x74, + 0x79, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x10, 0x0a, 0x03, 0x75, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0xc2, 0x01, + 0x0a, 0x10, 0x55, 0x6e, 0x69, 0x78, 0x44, 0x6f, 0x67, 0x73, 0x74, 0x61, 0x74, 0x73, 0x64, 0x4d, + 0x73, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0b, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, + 0x7a, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x10, 0x0a, 0x03, + 0x70, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x70, 0x69, 0x64, 0x12, 0x24, + 0x0a, 0x0d, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x79, 0x53, 0x69, 0x7a, 0x65, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, 0x79, + 0x53, 0x69, 0x7a, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, 0x72, + 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x61, 0x6e, 0x63, 0x69, 0x6c, 0x6c, 0x61, + 0x72, 0x79, 0x22, 0x9f, 0x02, 0x0a, 0x0b, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x28, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x2e, 0x50, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, 0x69, - 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x52, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, - 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x50, 0x69, 0x64, 0x4d, - 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x13, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x6f, - 0x61, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x6f, 0x61, 0x64, - 0x65, 0x64, 0x2a, 0x31, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x09, 0x0a, 0x05, 0x41, 0x44, 0x44, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, 0x4f, - 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, 0x45, - 0x54, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0e, 0x54, 0x61, 0x67, 0x43, 0x61, 0x72, 0x64, - 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, 0x00, - 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x43, 0x48, 0x45, 0x53, 0x54, 0x52, 0x41, 0x54, 0x4f, 0x52, - 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x02, 0x42, 0x15, 0x5a, 0x13, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x67, 0x6f, 0x2f, 0x63, - 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x41, 0x0a, 0x06, 0x70, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, 0x6f, 0x64, + 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x2e, 0x50, 0x69, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x70, + 0x69, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x52, 0x0a, 0x0a, 0x53, 0x74, 0x61, 0x74, 0x65, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x64, 0x61, 0x74, 0x61, 0x64, 0x6f, 0x67, 0x2e, 0x6d, + 0x6f, 0x64, 0x65, 0x6c, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x39, 0x0a, 0x0b, 0x50, 0x69, 0x64, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, + 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x2d, 0x0a, 0x13, 0x54, 0x61, 0x67, 0x67, 0x65, 0x72, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6c, + 0x6f, 0x61, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x6f, 0x61, + 0x64, 0x65, 0x64, 0x2a, 0x31, 0x0a, 0x09, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, + 0x12, 0x09, 0x0a, 0x05, 0x41, 0x44, 0x44, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x4d, + 0x4f, 0x44, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x45, 0x4c, + 0x45, 0x54, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x35, 0x0a, 0x0e, 0x54, 0x61, 0x67, 0x43, 0x61, 0x72, + 0x64, 0x69, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x07, 0x0a, 0x03, 0x4c, 0x4f, 0x57, 0x10, + 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x4f, 0x52, 0x43, 0x48, 0x45, 0x53, 0x54, 0x52, 0x41, 0x54, 0x4f, + 0x52, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x49, 0x47, 0x48, 0x10, 0x02, 0x42, 0x15, 0x5a, + 0x13, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x70, 0x62, 0x67, 0x6f, 0x2f, + 0x63, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1134,27 +1409,31 @@ func file_datadog_model_v1_model_proto_rawDescGZIP() []byte { } var file_datadog_model_v1_model_proto_enumTypes = make([]protoimpl.EnumInfo, 2) -var file_datadog_model_v1_model_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_datadog_model_v1_model_proto_msgTypes = make([]protoimpl.MessageInfo, 21) var file_datadog_model_v1_model_proto_goTypes = []any{ - (EventType)(0), // 0: datadog.model.v1.EventType - (TagCardinality)(0), // 1: datadog.model.v1.TagCardinality - (*HostnameRequest)(nil), // 2: datadog.model.v1.HostnameRequest - (*HostnameReply)(nil), // 3: datadog.model.v1.HostnameReply - (*CaptureTriggerRequest)(nil), // 4: datadog.model.v1.CaptureTriggerRequest - (*CaptureTriggerResponse)(nil), // 5: datadog.model.v1.CaptureTriggerResponse - (*StreamTagsRequest)(nil), // 6: datadog.model.v1.StreamTagsRequest - (*StreamTagsResponse)(nil), // 7: datadog.model.v1.StreamTagsResponse - (*StreamTagsEvent)(nil), // 8: datadog.model.v1.StreamTagsEvent - (*DeprecatedFilter)(nil), // 9: datadog.model.v1.DeprecatedFilter - (*Entity)(nil), // 10: datadog.model.v1.Entity - (*FetchEntityRequest)(nil), // 11: datadog.model.v1.FetchEntityRequest - (*FetchEntityResponse)(nil), // 12: datadog.model.v1.FetchEntityResponse - (*EntityId)(nil), // 13: datadog.model.v1.EntityId - (*UnixDogstatsdMsg)(nil), // 14: datadog.model.v1.UnixDogstatsdMsg - (*TaggerState)(nil), // 15: datadog.model.v1.TaggerState - (*TaggerStateResponse)(nil), // 16: datadog.model.v1.TaggerStateResponse - nil, // 17: datadog.model.v1.TaggerState.StateEntry - nil, // 18: datadog.model.v1.TaggerState.PidMapEntry + (EventType)(0), // 0: datadog.model.v1.EventType + (TagCardinality)(0), // 1: datadog.model.v1.TagCardinality + (*HostnameRequest)(nil), // 2: datadog.model.v1.HostnameRequest + (*HostnameReply)(nil), // 3: datadog.model.v1.HostnameReply + (*CaptureTriggerRequest)(nil), // 4: datadog.model.v1.CaptureTriggerRequest + (*CaptureTriggerResponse)(nil), // 5: datadog.model.v1.CaptureTriggerResponse + (*StreamTagsRequest)(nil), // 6: datadog.model.v1.StreamTagsRequest + (*StreamTagsResponse)(nil), // 7: datadog.model.v1.StreamTagsResponse + (*StreamTagsEvent)(nil), // 8: datadog.model.v1.StreamTagsEvent + (*DeprecatedFilter)(nil), // 9: datadog.model.v1.DeprecatedFilter + (*Entity)(nil), // 10: datadog.model.v1.Entity + (*GenerateContainerIDFromOriginInfoRequest)(nil), // 11: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest + (*GenerateContainerIDFromOriginInfoResponse)(nil), // 12: datadog.model.v1.GenerateContainerIDFromOriginInfoResponse + (*FetchEntityRequest)(nil), // 13: datadog.model.v1.FetchEntityRequest + (*FetchEntityResponse)(nil), // 14: datadog.model.v1.FetchEntityResponse + (*EntityId)(nil), // 15: datadog.model.v1.EntityId + (*UnixDogstatsdMsg)(nil), // 16: datadog.model.v1.UnixDogstatsdMsg + (*TaggerState)(nil), // 17: datadog.model.v1.TaggerState + (*TaggerStateResponse)(nil), // 18: datadog.model.v1.TaggerStateResponse + (*GenerateContainerIDFromOriginInfoRequest_LocalData)(nil), // 19: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.LocalData + (*GenerateContainerIDFromOriginInfoRequest_ExternalData)(nil), // 20: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.ExternalData + nil, // 21: datadog.model.v1.TaggerState.StateEntry + nil, // 22: datadog.model.v1.TaggerState.PidMapEntry } var file_datadog_model_v1_model_proto_depIdxs = []int32{ 1, // 0: datadog.model.v1.StreamTagsRequest.cardinality:type_name -> datadog.model.v1.TagCardinality @@ -1163,19 +1442,21 @@ var file_datadog_model_v1_model_proto_depIdxs = []int32{ 8, // 3: datadog.model.v1.StreamTagsResponse.events:type_name -> datadog.model.v1.StreamTagsEvent 0, // 4: datadog.model.v1.StreamTagsEvent.type:type_name -> datadog.model.v1.EventType 10, // 5: datadog.model.v1.StreamTagsEvent.entity:type_name -> datadog.model.v1.Entity - 13, // 6: datadog.model.v1.Entity.id:type_name -> datadog.model.v1.EntityId - 13, // 7: datadog.model.v1.FetchEntityRequest.id:type_name -> datadog.model.v1.EntityId - 1, // 8: datadog.model.v1.FetchEntityRequest.cardinality:type_name -> datadog.model.v1.TagCardinality - 13, // 9: datadog.model.v1.FetchEntityResponse.id:type_name -> datadog.model.v1.EntityId - 1, // 10: datadog.model.v1.FetchEntityResponse.cardinality:type_name -> datadog.model.v1.TagCardinality - 17, // 11: datadog.model.v1.TaggerState.state:type_name -> datadog.model.v1.TaggerState.StateEntry - 18, // 12: datadog.model.v1.TaggerState.pidMap:type_name -> datadog.model.v1.TaggerState.PidMapEntry - 10, // 13: datadog.model.v1.TaggerState.StateEntry.value:type_name -> datadog.model.v1.Entity - 14, // [14:14] is the sub-list for method output_type - 14, // [14:14] is the sub-list for method input_type - 14, // [14:14] is the sub-list for extension type_name - 14, // [14:14] is the sub-list for extension extendee - 0, // [0:14] is the sub-list for field type_name + 15, // 6: datadog.model.v1.Entity.id:type_name -> datadog.model.v1.EntityId + 19, // 7: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.localData:type_name -> datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.LocalData + 20, // 8: datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.externalData:type_name -> datadog.model.v1.GenerateContainerIDFromOriginInfoRequest.ExternalData + 15, // 9: datadog.model.v1.FetchEntityRequest.id:type_name -> datadog.model.v1.EntityId + 1, // 10: datadog.model.v1.FetchEntityRequest.cardinality:type_name -> datadog.model.v1.TagCardinality + 15, // 11: datadog.model.v1.FetchEntityResponse.id:type_name -> datadog.model.v1.EntityId + 1, // 12: datadog.model.v1.FetchEntityResponse.cardinality:type_name -> datadog.model.v1.TagCardinality + 21, // 13: datadog.model.v1.TaggerState.state:type_name -> datadog.model.v1.TaggerState.StateEntry + 22, // 14: datadog.model.v1.TaggerState.pidMap:type_name -> datadog.model.v1.TaggerState.PidMapEntry + 10, // 15: datadog.model.v1.TaggerState.StateEntry.value:type_name -> datadog.model.v1.Entity + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_datadog_model_v1_model_proto_init() } @@ -1183,13 +1464,16 @@ func file_datadog_model_v1_model_proto_init() { if File_datadog_model_v1_model_proto != nil { return } + file_datadog_model_v1_model_proto_msgTypes[9].OneofWrappers = []any{} + file_datadog_model_v1_model_proto_msgTypes[17].OneofWrappers = []any{} + file_datadog_model_v1_model_proto_msgTypes[18].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_datadog_model_v1_model_proto_rawDesc, NumEnums: 2, - NumMessages: 17, + NumMessages: 21, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/proto/pbgo/mocks/core/api_mockgen.pb.go b/pkg/proto/pbgo/mocks/core/api_mockgen.pb.go index 7d7798b724aaa..a28d85a34a874 100644 --- a/pkg/proto/pbgo/mocks/core/api_mockgen.pb.go +++ b/pkg/proto/pbgo/mocks/core/api_mockgen.pb.go @@ -299,6 +299,26 @@ func (mr *MockAgentSecureClientMockRecorder) TaggerFetchEntity(ctx, in interface return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaggerFetchEntity", reflect.TypeOf((*MockAgentSecureClient)(nil).TaggerFetchEntity), varargs...) } +// TaggerGenerateContainerIDFromOriginInfo mocks base method. +func (m *MockAgentSecureClient) TaggerGenerateContainerIDFromOriginInfo(ctx context.Context, in *core.GenerateContainerIDFromOriginInfoRequest, opts ...grpc.CallOption) (*core.GenerateContainerIDFromOriginInfoResponse, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, in} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "TaggerGenerateContainerIDFromOriginInfo", varargs...) + ret0, _ := ret[0].(*core.GenerateContainerIDFromOriginInfoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TaggerGenerateContainerIDFromOriginInfo indicates an expected call of TaggerGenerateContainerIDFromOriginInfo. +func (mr *MockAgentSecureClientMockRecorder) TaggerGenerateContainerIDFromOriginInfo(ctx, in interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, in}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaggerGenerateContainerIDFromOriginInfo", reflect.TypeOf((*MockAgentSecureClient)(nil).TaggerGenerateContainerIDFromOriginInfo), varargs...) +} + // TaggerStreamEntities mocks base method. func (m *MockAgentSecureClient) TaggerStreamEntities(ctx context.Context, in *core.StreamTagsRequest, opts ...grpc.CallOption) (core.AgentSecure_TaggerStreamEntitiesClient, error) { m.ctrl.T.Helper() @@ -865,6 +885,21 @@ func (mr *MockAgentSecureServerMockRecorder) TaggerFetchEntity(arg0, arg1 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaggerFetchEntity", reflect.TypeOf((*MockAgentSecureServer)(nil).TaggerFetchEntity), arg0, arg1) } +// TaggerGenerateContainerIDFromOriginInfo mocks base method. +func (m *MockAgentSecureServer) TaggerGenerateContainerIDFromOriginInfo(arg0 context.Context, arg1 *core.GenerateContainerIDFromOriginInfoRequest) (*core.GenerateContainerIDFromOriginInfoResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TaggerGenerateContainerIDFromOriginInfo", arg0, arg1) + ret0, _ := ret[0].(*core.GenerateContainerIDFromOriginInfoResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// TaggerGenerateContainerIDFromOriginInfo indicates an expected call of TaggerGenerateContainerIDFromOriginInfo. +func (mr *MockAgentSecureServerMockRecorder) TaggerGenerateContainerIDFromOriginInfo(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TaggerGenerateContainerIDFromOriginInfo", reflect.TypeOf((*MockAgentSecureServer)(nil).TaggerGenerateContainerIDFromOriginInfo), arg0, arg1) +} + // TaggerStreamEntities mocks base method. func (m *MockAgentSecureServer) TaggerStreamEntities(arg0 *core.StreamTagsRequest, arg1 core.AgentSecure_TaggerStreamEntitiesServer) error { m.ctrl.T.Helper() From 4189bbbf669b8eb0fe987e02ebcbb4507b001c3b Mon Sep 17 00:00:00 2001 From: Baptiste Foy Date: Tue, 17 Dec 2024 12:40:03 +0100 Subject: [PATCH 64/78] upgrade(installer): Support os & os_version as policy scope (#32281) --- pkg/fleet/internal/cdn/scope_expression.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/fleet/internal/cdn/scope_expression.go b/pkg/fleet/internal/cdn/scope_expression.go index 659d6798c0670..cc86626fcf9c0 100644 --- a/pkg/fleet/internal/cdn/scope_expression.go +++ b/pkg/fleet/internal/cdn/scope_expression.go @@ -10,7 +10,9 @@ import ( "fmt" "regexp" + "github.com/DataDog/datadog-agent/comp/metadata/host/hostimpl/utils" "github.com/DataDog/datadog-agent/pkg/fleet/installer/env" + "github.com/DataDog/datadog-agent/pkg/gohai/platform" "github.com/DataDog/datadog-agent/pkg/version" "github.com/expr-lang/expr" ) @@ -115,6 +117,8 @@ func getScopeExprVars(env *env.Env, hostTagsGetter hostTagsGetter) map[string]in return map[string]interface{}{ "hostname": env.Hostname, "installer_version": version.AgentVersion, // AgentVersion evaluates to the installer version here + "os": platform.CollectInfo().KernelName.ValueOrDefault(), + "os_version": utils.GetOSVersion(), "tags": hostTagsGetter.get(), } From 48e04d76736adedd5f8955e62c6a19c39128d8be Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Tue, 17 Dec 2024 13:16:56 +0100 Subject: [PATCH 65/78] discovery: Fix spurious failures in TestPorts (#32283) --- .../servicediscovery/module/impl_linux_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/collector/corechecks/servicediscovery/module/impl_linux_test.go b/pkg/collector/corechecks/servicediscovery/module/impl_linux_test.go index d40a5216f1355..357409b59712c 100644 --- a/pkg/collector/corechecks/servicediscovery/module/impl_linux_test.go +++ b/pkg/collector/corechecks/servicediscovery/module/impl_linux_test.go @@ -288,13 +288,23 @@ func TestPorts(t *testing.T) { startUDP("udp4") startUDP("udp6") + expectedPortsMap := make(map[uint16]struct{}, len(expectedPorts)) + serviceMap := getServicesMap(t, url) pid := os.Getpid() require.Contains(t, serviceMap, pid) for _, port := range expectedPorts { + expectedPortsMap[port] = struct{}{} assert.Contains(t, serviceMap[pid].Ports, port) } for _, port := range unexpectedPorts { + // An unexpected port number can also be expected since UDP and TCP and + // v4 and v6 are all in the same list. Just skip the extra check in that + // case since it should be rare. + if _, alsoExpected := expectedPortsMap[port]; alsoExpected { + continue + } + assert.NotContains(t, serviceMap[pid].Ports, port) } } From 175edb6c74f2afa7d331c5b3cc090fe1326d207a Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Tue, 17 Dec 2024 14:24:54 +0200 Subject: [PATCH 66/78] usm, npm: rework pid_tgid (#32186) --- pkg/network/ebpf/c/co-re/tracer-fentry.c | 27 +++++----- pkg/network/ebpf/c/pid_tgid.h | 52 +++++++++++++++++++ pkg/network/ebpf/c/prebuilt/conntrack.c | 3 +- pkg/network/ebpf/c/prebuilt/offset-guess.c | 10 ++-- pkg/network/ebpf/c/prebuilt/usm_events_test.c | 3 +- pkg/network/ebpf/c/protocols/sockfd-probes.h | 5 +- .../ebpf/c/protocols/tls/go-tls-conn.h | 3 +- pkg/network/ebpf/c/protocols/tls/https.h | 3 +- pkg/network/ebpf/c/runtime/conntrack.c | 3 +- pkg/network/ebpf/c/runtime/usm.c | 25 ++++----- pkg/network/ebpf/c/shared-libraries/probes.h | 3 +- pkg/network/ebpf/c/sock.h | 3 +- pkg/network/ebpf/c/tracer.c | 35 +++++++------ pkg/network/ebpf/c/tracer/bind.h | 17 +++--- pkg/network/ebpf/c/tracer/stats.h | 3 +- 15 files changed, 130 insertions(+), 65 deletions(-) create mode 100644 pkg/network/ebpf/c/pid_tgid.h diff --git a/pkg/network/ebpf/c/co-re/tracer-fentry.c b/pkg/network/ebpf/c/co-re/tracer-fentry.c index 09259e948047d..b89ab7e351d35 100644 --- a/pkg/network/ebpf/c/co-re/tracer-fentry.c +++ b/pkg/network/ebpf/c/co-re/tracer-fentry.c @@ -8,6 +8,7 @@ #include "ipv6.h" #include "sock.h" #include "skb.h" +#include "pid_tgid.h" #include "tracer/tracer.h" #include "tracer/events.h" @@ -52,7 +53,7 @@ static __always_inline bool event_in_task(char *prog_name) { } static __always_inline int read_conn_tuple_partial_from_flowi4(conn_tuple_t *t, struct flowi4 *fl4, u64 pid_tgid, metadata_mask_t type) { - t->pid = pid_tgid >> 32; + t->pid = GET_USER_MODE_PID(pid_tgid); t->metadata = type; if (t->saddr_l == 0) { @@ -85,7 +86,7 @@ static __always_inline int read_conn_tuple_partial_from_flowi4(conn_tuple_t *t, } static __always_inline int read_conn_tuple_partial_from_flowi6(conn_tuple_t *t, struct flowi6 *fl6, u64 pid_tgid, metadata_mask_t type) { - t->pid = pid_tgid >> 32; + t->pid = GET_USER_MODE_PID(pid_tgid); t->metadata = type; struct in6_addr addr = BPF_CORE_READ(fl6, saddr); @@ -233,7 +234,7 @@ int BPF_PROG(tcp_close, struct sock *sk, long timeout) { u64 pid_tgid = bpf_get_current_pid_tgid(); // Get network namespace id - log_debug("fentry/tcp_close: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("fentry/tcp_close: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); if (!read_conn_tuple(&t, sk, pid_tgid, CONN_TYPE_TCP)) { return 0; } @@ -410,14 +411,14 @@ SEC("fentry/tcp_retransmit_skb") int BPF_PROG(tcp_retransmit_skb, struct sock *sk, struct sk_buff *skb, int segs, int err) { RETURN_IF_NOT_IN_SYSPROBE_TASK("fentry/tcp_retransmit_skb"); log_debug("fexntry/tcp_retransmit"); - u64 tid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); tcp_retransmit_skb_args_t args = {}; args.retrans_out_pre = BPF_CORE_READ(tcp_sk(sk), retrans_out); if (args.retrans_out_pre < 0) { return 0; } - bpf_map_update_with_telemetry(pending_tcp_retransmit_skb, &tid, &args, BPF_ANY); + bpf_map_update_with_telemetry(pending_tcp_retransmit_skb, &pid_tgid, &args, BPF_ANY); return 0; } @@ -426,18 +427,18 @@ SEC("fexit/tcp_retransmit_skb") int BPF_PROG(tcp_retransmit_skb_exit, struct sock *sk, struct sk_buff *skb, int segs, int err) { RETURN_IF_NOT_IN_SYSPROBE_TASK("fexit/tcp_retransmit_skb"); log_debug("fexit/tcp_retransmit"); - u64 tid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); if (err < 0) { - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); return 0; } - tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &tid); + tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &pid_tgid); if (args == NULL) { return 0; } u32 retrans_out_pre = args->retrans_out_pre; u32 retrans_out = BPF_CORE_READ(tcp_sk(sk), retrans_out); - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); if (retrans_out < 0) { return 0; @@ -450,7 +451,7 @@ SEC("fentry/tcp_connect") int BPF_PROG(tcp_connect, struct sock *sk) { RETURN_IF_NOT_IN_SYSPROBE_TASK("fentry/tcp_connect"); u64 pid_tgid = bpf_get_current_pid_tgid(); - log_debug("fentry/tcp_connect: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("fentry/tcp_connect: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); conn_tuple_t t = {}; if (!read_conn_tuple(&t, sk, 0, CONN_TYPE_TCP)) { @@ -479,8 +480,8 @@ int BPF_PROG(tcp_finish_connect, struct sock *sk, struct sk_buff *skb, int rc) { return 0; } u64 pid_tgid = pid_tgid_p->pid_tgid; - t.pid = pid_tgid >> 32; - log_debug("fentry/tcp_finish_connect: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + t.pid = GET_USER_MODE_PID(pid_tgid); + log_debug("fentry/tcp_finish_connect: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); handle_tcp_stats(&t, sk, TCP_ESTABLISHED); handle_message(&t, 0, 0, CONN_DIRECTION_OUTGOING, 0, 0, PACKET_COUNT_NONE, sk); @@ -498,7 +499,7 @@ int BPF_PROG(inet_csk_accept_exit, struct sock *_sk, int flags, int *err, bool k } u64 pid_tgid = bpf_get_current_pid_tgid(); - log_debug("fexit/inet_csk_accept: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("fexit/inet_csk_accept: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); conn_tuple_t t = {}; if (!read_conn_tuple(&t, sk, pid_tgid, CONN_TYPE_TCP)) { diff --git a/pkg/network/ebpf/c/pid_tgid.h b/pkg/network/ebpf/c/pid_tgid.h new file mode 100644 index 0000000000000..a3439bca233a7 --- /dev/null +++ b/pkg/network/ebpf/c/pid_tgid.h @@ -0,0 +1,52 @@ +#ifndef __PID_TGID_H +#define __PID_TGID_H + +/* + * The following documentation is based on https://stackoverflow.com/a/9306150 + * Note on Process and Thread Identifiers: + * + * What users refer to as a "PID" is not quite the same as what the kernel sees. + * + * In the kernel: + * - Each thread has its own ID, called a PID (though it might be better termed a TID, or Thread ID). + * - Threads within the same process share a TGID (Thread Group ID), which is the PID of the first thread + * created when the process was initialized. + * + * When a process is created: + * - It starts as a single thread where the PID and TGID are the same. + * + * When a new thread is created: + * - It receives its own unique PID for independent scheduling by the kernel. + * - It inherits the TGID from the original (parent) thread, tying it to the same process. + * + * This separation allows the kernel to schedule threads independently while maintaining the process view + * (TGID) when reporting information to users. + * + * Example Hierarchy of Threads: + * + * USER VIEW + * vvvvvvvv + * + * | + * <-- PID 43 -->|<----------------- PID 42 -----------------> + * | | + * | +---------+ | + * | | process | | + * | _| pid=42 |_ | + * __(fork) _/ | tgid=42 | \_ (new thread) _ + * / | +---------+ | \ + * +---------+ | | +---------+ + * | process | | | | process | + * | pid=43 | | | | pid=44 | + * | tgid=43 | | | | tgid=42 | + * +---------+ | | +---------+ + * | | + * <-- PID 43 -->|<--------- PID 42 -------->|<--- PID 44 ---> + * | | + * ^^^^^^^^ + * KERNEL VIEW + */ +#define GET_USER_MODE_PID(x) ((x) >> 32) +#define GET_KERNEL_THREAD_ID(x) ((x) & 0xFFFFFFFF) + +#endif // __PID_TGID_H diff --git a/pkg/network/ebpf/c/prebuilt/conntrack.c b/pkg/network/ebpf/c/prebuilt/conntrack.c index 8e77c1fd98ff8..8417806ce3077 100644 --- a/pkg/network/ebpf/c/prebuilt/conntrack.c +++ b/pkg/network/ebpf/c/prebuilt/conntrack.c @@ -12,6 +12,7 @@ #include "conntrack/maps.h" #include "ip.h" #include "ipv6.h" +#include "pid_tgid.h" SEC("kprobe/__nf_conntrack_hash_insert") int BPF_BYPASSABLE_KPROBE(kprobe___nf_conntrack_hash_insert, struct nf_conn *ct) { @@ -32,7 +33,7 @@ int BPF_BYPASSABLE_KPROBE(kprobe___nf_conntrack_hash_insert, struct nf_conn *ct) SEC("kprobe/ctnetlink_fill_info") int BPF_BYPASSABLE_KPROBE(kprobe_ctnetlink_fill_info) { - u32 pid = bpf_get_current_pid_tgid() >> 32; + u32 pid = GET_USER_MODE_PID(bpf_get_current_pid_tgid()); if (pid != systemprobe_pid()) { log_debug("skipping kprobe/ctnetlink_fill_info invocation from non-system-probe process"); return 0; diff --git a/pkg/network/ebpf/c/prebuilt/offset-guess.c b/pkg/network/ebpf/c/prebuilt/offset-guess.c index cc32fee223935..556a770082684 100644 --- a/pkg/network/ebpf/c/prebuilt/offset-guess.c +++ b/pkg/network/ebpf/c/prebuilt/offset-guess.c @@ -298,11 +298,11 @@ int kprobe__sock_common_getsockopt(struct pt_regs* ctx) { SEC("kprobe/tcp_v6_connect") int kprobe__tcp_v6_connect(struct pt_regs* ctx) { struct sock* sk; - u64 pid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); sk = (struct sock*)PT_REGS_PARM1(ctx); - bpf_map_update_elem(&connectsock_ipv6, &pid, &sk, BPF_ANY); + bpf_map_update_elem(&connectsock_ipv6, &pid_tgid, &sk, BPF_ANY); return 0; } @@ -310,17 +310,17 @@ int kprobe__tcp_v6_connect(struct pt_regs* ctx) { // Used for offset guessing (see: pkg/ebpf/offsetguess.go) SEC("kretprobe/tcp_v6_connect") int kretprobe__tcp_v6_connect(struct pt_regs* __attribute__((unused)) ctx) { - u64 pid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); u64 zero = 0; struct sock** skpp; tracer_status_t* status; - skpp = bpf_map_lookup_elem(&connectsock_ipv6, &pid); + skpp = bpf_map_lookup_elem(&connectsock_ipv6, &pid_tgid); if (skpp == 0) { return 0; // missed entry } struct sock* skp = *skpp; - bpf_map_delete_elem(&connectsock_ipv6, &pid); + bpf_map_delete_elem(&connectsock_ipv6, &pid_tgid); status = bpf_map_lookup_elem(&tracer_status, &zero); if (status == NULL || is_sk_buff_event(status->what)) { diff --git a/pkg/network/ebpf/c/prebuilt/usm_events_test.c b/pkg/network/ebpf/c/prebuilt/usm_events_test.c index 820afb50ef0f0..f2bfb640aa54a 100644 --- a/pkg/network/ebpf/c/prebuilt/usm_events_test.c +++ b/pkg/network/ebpf/c/prebuilt/usm_events_test.c @@ -9,6 +9,7 @@ #include "defs.h" #include "map-defs.h" #include "protocols/events.h" +#include "pid_tgid.h" // -------------------------------------------------------- // this is a test program for pkg/networks/protocols/events @@ -37,7 +38,7 @@ struct syscalls_enter_write_args { SEC("tracepoint/syscalls/sys_enter_write") int tracepoint__syscalls__sys_enter_write(struct syscalls_enter_write_args *ctx) { __u32 zero = 0; - __u32 pid = bpf_get_current_pid_tgid() >> 32; + __u32 pid = GET_USER_MODE_PID(bpf_get_current_pid_tgid()); test_ctx_t *test_ctx = bpf_map_lookup_elem(&test, &zero); if (!test_ctx || test_ctx->expected_fd != ctx->fd || test_ctx->expected_pid != pid) return 0; diff --git a/pkg/network/ebpf/c/protocols/sockfd-probes.h b/pkg/network/ebpf/c/protocols/sockfd-probes.h index 9c31f236ab087..06ecbeee6fe57 100644 --- a/pkg/network/ebpf/c/protocols/sockfd-probes.h +++ b/pkg/network/ebpf/c/protocols/sockfd-probes.h @@ -13,6 +13,7 @@ #include "sock.h" #include "sockfd.h" +#include "pid_tgid.h" SEC("kprobe/tcp_close") int BPF_KPROBE(kprobe__tcp_close, struct sock *sk) { @@ -53,7 +54,7 @@ int BPF_KPROBE(kprobe__sockfd_lookup_light, int sockfd) { // but can reduce the accuracy of programs relying on socket FDs for // processes with a lot of FD churn pid_fd_t key = { - .pid = pid_tgid >> 32, + .pid = GET_USER_MODE_PID(pid_tgid), .fd = sockfd, }; conn_tuple_t *t = bpf_map_lookup_elem(&tuple_by_pid_fd, &key); @@ -121,7 +122,7 @@ int BPF_KRETPROBE(kretprobe__sockfd_lookup_light, struct socket *socket) { } pid_fd_t pid_fd = { - .pid = pid_tgid >> 32, + .pid = GET_USER_MODE_PID(pid_tgid), .fd = (*sockfd), }; diff --git a/pkg/network/ebpf/c/protocols/tls/go-tls-conn.h b/pkg/network/ebpf/c/protocols/tls/go-tls-conn.h index ba812de0385bd..d297ad11f57cf 100644 --- a/pkg/network/ebpf/c/protocols/tls/go-tls-conn.h +++ b/pkg/network/ebpf/c/protocols/tls/go-tls-conn.h @@ -4,6 +4,7 @@ #include "bpf_helpers.h" #include "ip.h" #include "port_range.h" +#include "pid_tgid.h" #include "protocols/http/maps.h" #include "protocols/tls/go-tls-types.h" @@ -49,7 +50,7 @@ static __always_inline conn_tuple_t* conn_tup_from_tls_conn(tls_offsets_data_t* // The code path below should be executed only once during the lifecycle of a TLS connection pid_fd_t pid_fd = { - .pid = pid_tgid >> 32, + .pid = GET_USER_MODE_PID(pid_tgid), // fd is populated by the code downstream .fd = 0, }; diff --git a/pkg/network/ebpf/c/protocols/tls/https.h b/pkg/network/ebpf/c/protocols/tls/https.h index f1d0562033a54..ac3c509daaeb3 100644 --- a/pkg/network/ebpf/c/protocols/tls/https.h +++ b/pkg/network/ebpf/c/protocols/tls/https.h @@ -17,6 +17,7 @@ #include "bpf_builtins.h" #include "port_range.h" #include "sock.h" +#include "pid_tgid.h" #include "protocols/amqp/helpers.h" #include "protocols/redis/helpers.h" @@ -250,7 +251,7 @@ static __always_inline conn_tuple_t* tup_from_ssl_ctx(void *ssl_ctx, u64 pid_tgi // the code path below should be executed only once during the lifecycle of a SSL session pid_fd_t pid_fd = { - .pid = pid_tgid >> 32, + .pid = GET_USER_MODE_PID(pid_tgid), .fd = ssl_sock->fd, }; diff --git a/pkg/network/ebpf/c/runtime/conntrack.c b/pkg/network/ebpf/c/runtime/conntrack.c index a054504439230..e7418edcd986e 100644 --- a/pkg/network/ebpf/c/runtime/conntrack.c +++ b/pkg/network/ebpf/c/runtime/conntrack.c @@ -21,6 +21,7 @@ #include "conntrack/maps.h" #include "netns.h" #include "ip.h" +#include "pid_tgid.h" #if defined(FEATURE_TCPV6_ENABLED) || defined(FEATURE_UDPV6_ENABLED) #include "ipv6.h" @@ -50,7 +51,7 @@ int BPF_BYPASSABLE_KPROBE(kprobe___nf_conntrack_hash_insert, struct nf_conn *ct) SEC("kprobe/ctnetlink_fill_info") int BPF_BYPASSABLE_KPROBE(kprobe_ctnetlink_fill_info) { - u32 pid = bpf_get_current_pid_tgid() >> 32; + u32 pid = GET_USER_MODE_PID(bpf_get_current_pid_tgid()); if (pid != systemprobe_pid()) { log_debug("skipping kprobe/ctnetlink_fill_info invocation from non-system-probe process"); return 0; diff --git a/pkg/network/ebpf/c/runtime/usm.c b/pkg/network/ebpf/c/runtime/usm.c index 3cb3f1245a3ab..bf3737b311a92 100644 --- a/pkg/network/ebpf/c/runtime/usm.c +++ b/pkg/network/ebpf/c/runtime/usm.c @@ -13,6 +13,7 @@ #include "ipv6.h" #include "sock.h" #include "port_range.h" +#include "pid_tgid.h" #include "protocols/classification/dispatcher-helpers.h" #include "protocols/http/buffer.h" @@ -84,7 +85,7 @@ int tracepoint__net__netif_receive_skb(void *ctx) { SEC("uprobe/crypto/tls.(*Conn).Write") int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Write) { u64 pid_tgid = bpf_get_current_pid_tgid(); - u64 pid = pid_tgid >> 32; + u64 pid = GET_USER_MODE_PID(pid_tgid); tls_offsets_data_t* od = get_offsets_data(); if (od == NULL) { log_debug("[go-tls-write] no offsets data in map for pid %llu", pid); @@ -125,7 +126,7 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Write) { SEC("uprobe/crypto/tls.(*Conn).Write/return") int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Write__return) { u64 pid_tgid = bpf_get_current_pid_tgid(); - u64 pid = pid_tgid >> 32; + u64 pid = GET_USER_MODE_PID(pid_tgid); tls_offsets_data_t* od = get_offsets_data(); if (od == NULL) { log_debug("[go-tls-write-return] no offsets data in map for pid %llu", pid); @@ -200,10 +201,10 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Write__return) { SEC("uprobe/crypto/tls.(*Conn).Read") int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Read) { u64 pid_tgid = bpf_get_current_pid_tgid(); - u64 pid = pid_tgid >> 32; + u64 pid = GET_USER_MODE_PID(pid_tgid); tls_offsets_data_t* od = get_offsets_data(); if (od == NULL) { - log_debug("[go-tls-read] no offsets data in map for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-read] no offsets data in map for pid %llu", pid); return 0; } @@ -211,7 +212,7 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Read) { go_tls_function_args_key_t call_key = {0}; call_key.pid = pid; if (read_goroutine_id(ctx, &od->goroutine_id, &call_key.goroutine_id)) { - log_debug("[go-tls-read] failed reading go routine id for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-read] failed reading go routine id for pid %llu", pid); return 0; } @@ -219,11 +220,11 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Read) { // (since the parameters might not be live by the time the return probe is hit). go_tls_read_args_data_t call_data = {0}; if (read_location(ctx, &od->read_conn_pointer, sizeof(call_data.conn_pointer), &call_data.conn_pointer)) { - log_debug("[go-tls-read] failed reading conn pointer for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-read] failed reading conn pointer for pid %llu", pid); return 0; } if (read_location(ctx, &od->read_buffer.ptr, sizeof(call_data.b_data), &call_data.b_data)) { - log_debug("[go-tls-read] failed reading buffer pointer for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-read] failed reading buffer pointer for pid %llu", pid); return 0; } @@ -235,7 +236,7 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Read) { SEC("uprobe/crypto/tls.(*Conn).Read/return") int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Read__return) { u64 pid_tgid = bpf_get_current_pid_tgid(); - u64 pid = pid_tgid >> 32; + u64 pid = GET_USER_MODE_PID(pid_tgid); tls_offsets_data_t* od = get_offsets_data(); if (od == NULL) { log_debug("[go-tls-read-return] no offsets data in map for pid %llu", pid); @@ -305,13 +306,13 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Close) { u64 pid_tgid = bpf_get_current_pid_tgid(); tls_offsets_data_t* od = get_offsets_data(); if (od == NULL) { - log_debug("[go-tls-close] no offsets data in map for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-close] no offsets data in map for pid %llu", GET_USER_MODE_PID(pid_tgid)); return 0; } // Read the PID and goroutine ID to make the partial call key go_tls_function_args_key_t call_key = {0}; - call_key.pid = pid_tgid >> 32; + call_key.pid = GET_USER_MODE_PID(pid_tgid); if (read_goroutine_id(ctx, &od->goroutine_id, &call_key.goroutine_id) == 0) { bpf_map_delete_elem(&go_tls_read_args, &call_key); bpf_map_delete_elem(&go_tls_write_args, &call_key); @@ -319,13 +320,13 @@ int BPF_BYPASSABLE_UPROBE(uprobe__crypto_tls_Conn_Close) { void* conn_pointer = NULL; if (read_location(ctx, &od->close_conn_pointer, sizeof(conn_pointer), &conn_pointer)) { - log_debug("[go-tls-close] failed reading close conn pointer for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-close] failed reading close conn pointer for pid %llu", GET_USER_MODE_PID(pid_tgid)); return 0; } conn_tuple_t* t = conn_tup_from_tls_conn(od, conn_pointer, pid_tgid); if (t == NULL) { - log_debug("[go-tls-close] failed getting conn tup from tls conn for pid %llu", pid_tgid >> 32); + log_debug("[go-tls-close] failed getting conn tup from tls conn for pid %llu", GET_USER_MODE_PID(pid_tgid)); return 0; } diff --git a/pkg/network/ebpf/c/shared-libraries/probes.h b/pkg/network/ebpf/c/shared-libraries/probes.h index a33e228a63b26..a45e9d0b494e1 100644 --- a/pkg/network/ebpf/c/shared-libraries/probes.h +++ b/pkg/network/ebpf/c/shared-libraries/probes.h @@ -4,6 +4,7 @@ #include "bpf_telemetry.h" #include "bpf_bypass.h" +#include "pid_tgid.h" #include "shared-libraries/types.h" static __always_inline void fill_path_safe(lib_path_t *path, const char *path_argument) { @@ -38,7 +39,7 @@ static __always_inline void do_sys_open_helper_enter(const char *filename) { } u64 pid_tgid = bpf_get_current_pid_tgid(); - path.pid = pid_tgid >> 32; + path.pid = GET_USER_MODE_PID(pid_tgid); bpf_map_update_with_telemetry(open_at_args, &pid_tgid, &path, BPF_ANY); return; } diff --git a/pkg/network/ebpf/c/sock.h b/pkg/network/ebpf/c/sock.h index c71e48a544f1d..997acc13edef6 100644 --- a/pkg/network/ebpf/c/sock.h +++ b/pkg/network/ebpf/c/sock.h @@ -8,6 +8,7 @@ #include "ip.h" #include "ipv6.h" #include "netns.h" +#include "pid_tgid.h" #ifdef COMPILE_CORE @@ -178,7 +179,7 @@ static __always_inline u16 _sk_family(struct sock *skp) { */ static __always_inline int read_conn_tuple_partial(conn_tuple_t* t, struct sock* skp, u64 pid_tgid, metadata_mask_t type) { int err = 0; - t->pid = pid_tgid >> 32; + t->pid = GET_USER_MODE_PID(pid_tgid); t->metadata = type; // Retrieve network namespace id first since addresses and ports may not be available for unconnected UDP diff --git a/pkg/network/ebpf/c/tracer.c b/pkg/network/ebpf/c/tracer.c index 8de25a2a37e6b..66cefbee1d08d 100644 --- a/pkg/network/ebpf/c/tracer.c +++ b/pkg/network/ebpf/c/tracer.c @@ -19,6 +19,7 @@ #include "tracer/port.h" #include "tracer/tcp_recv.h" #include "protocols/classification/protocol-classification.h" +#include "pid_tgid.h" __maybe_unused static __always_inline bool tcp_failed_connections_enabled() { __u64 val = 0; @@ -226,7 +227,7 @@ int BPF_BYPASSABLE_KPROBE(kprobe__tcp_done, struct sock *sk) { pid_ts_t *failed_conn_pid = bpf_map_lookup_elem(&tcp_ongoing_connect_pid, &skp_conn); if (failed_conn_pid) { bpf_map_delete_elem(&tcp_ongoing_connect_pid, &skp_conn); - t.pid = failed_conn_pid->pid_tgid >> 32; + t.pid = GET_USER_MODE_PID(failed_conn_pid->pid_tgid); } else { increment_telemetry_count(tcp_done_missing_pid); return 0; @@ -256,7 +257,7 @@ int BPF_BYPASSABLE_KPROBE(kprobe__tcp_close, struct sock *sk) { u64 pid_tgid = bpf_get_current_pid_tgid(); // Get network namespace id - log_debug("kprobe/tcp_close: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("kprobe/tcp_close: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); if (!read_conn_tuple(&t, sk, pid_tgid, CONN_TYPE_TCP)) { return 0; } @@ -734,7 +735,7 @@ int BPF_BYPASSABLE_KRETPROBE(kretprobe__udpv6_recvmsg) { static __always_inline int handle_ret_udp_recvmsg_pre_4_7_0(int copied, void *udp_sock_map) { u64 pid_tgid = bpf_get_current_pid_tgid(); - log_debug("kretprobe/udp_recvmsg: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("kretprobe/udp_recvmsg: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); // Retrieve socket pointer from kprobe via pid/tgid udp_recv_sock_t *st = bpf_map_lookup_elem(udp_sock_map, &pid_tgid); @@ -886,18 +887,18 @@ int BPF_BYPASSABLE_KPROBE(kprobe__tcp_retransmit_skb_pre_4_7_0, struct sock *sk) SEC("kretprobe/tcp_retransmit_skb") int BPF_BYPASSABLE_KRETPROBE(kretprobe__tcp_retransmit_skb, int ret) { - __u64 tid = bpf_get_current_pid_tgid(); + __u64 pid_tgid = bpf_get_current_pid_tgid(); if (ret < 0) { - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); return 0; } - tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &tid); + tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &pid_tgid); if (args == NULL) { return 0; } struct sock *sk = args->sk; int segs = args->segs; - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); log_debug("kretprobe/tcp_retransmit: segs: %d", segs); return handle_retransmit(sk, segs); } @@ -908,30 +909,30 @@ int BPF_BYPASSABLE_KRETPROBE(kretprobe__tcp_retransmit_skb, int ret) { SEC("kprobe/tcp_retransmit_skb") int BPF_BYPASSABLE_KPROBE(kprobe__tcp_retransmit_skb, struct sock *sk) { - u64 tid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); tcp_retransmit_skb_args_t args = {}; args.sk = sk; args.segs = 0; BPF_CORE_READ_INTO(&args.retrans_out_pre, tcp_sk(sk), retrans_out); - bpf_map_update_with_telemetry(pending_tcp_retransmit_skb, &tid, &args, BPF_ANY); + bpf_map_update_with_telemetry(pending_tcp_retransmit_skb, &pid_tgid, &args, BPF_ANY); return 0; } SEC("kretprobe/tcp_retransmit_skb") int BPF_BYPASSABLE_KRETPROBE(kretprobe__tcp_retransmit_skb, int rc) { log_debug("kretprobe/tcp_retransmit"); - u64 tid = bpf_get_current_pid_tgid(); + u64 pid_tgid = bpf_get_current_pid_tgid(); if (rc < 0) { - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); return 0; } - tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &tid); + tcp_retransmit_skb_args_t *args = bpf_map_lookup_elem(&pending_tcp_retransmit_skb, &pid_tgid); if (args == NULL) { return 0; } struct sock *sk = args->sk; u32 retrans_out_pre = args->retrans_out_pre; - bpf_map_delete_elem(&pending_tcp_retransmit_skb, &tid); + bpf_map_delete_elem(&pending_tcp_retransmit_skb, &pid_tgid); u32 retrans_out = 0; BPF_CORE_READ_INTO(&retrans_out, tcp_sk(sk), retrans_out); return handle_retransmit(sk, retrans_out - retrans_out_pre); @@ -942,7 +943,7 @@ int BPF_BYPASSABLE_KRETPROBE(kretprobe__tcp_retransmit_skb, int rc) { SEC("kprobe/tcp_connect") int BPF_BYPASSABLE_KPROBE(kprobe__tcp_connect, struct sock *skp) { u64 pid_tgid = bpf_get_current_pid_tgid(); - log_debug("kprobe/tcp_connect: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("kprobe/tcp_connect: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); conn_tuple_t t = {}; if (!read_conn_tuple(&t, skp, 0, CONN_TYPE_TCP)) { @@ -971,8 +972,8 @@ int BPF_BYPASSABLE_KPROBE(kprobe__tcp_finish_connect, struct sock *skp) { } u64 pid_tgid = pid_tgid_p->pid_tgid; - t.pid = pid_tgid >> 32; - log_debug("kprobe/tcp_finish_connect: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + t.pid = GET_USER_MODE_PID(pid_tgid); + log_debug("kprobe/tcp_finish_connect: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); handle_tcp_stats(&t, skp, TCP_ESTABLISHED); handle_message(&t, 0, 0, CONN_DIRECTION_OUTGOING, 0, 0, PACKET_COUNT_NONE, skp); @@ -989,7 +990,7 @@ int BPF_BYPASSABLE_KRETPROBE(kretprobe__inet_csk_accept, struct sock *sk) { } u64 pid_tgid = bpf_get_current_pid_tgid(); - log_debug("kretprobe/inet_csk_accept: tgid: %llu, pid: %llu", pid_tgid >> 32, pid_tgid & 0xFFFFFFFF); + log_debug("kretprobe/inet_csk_accept: kernel thread id: %llu, user mode pid: %llu", GET_KERNEL_THREAD_ID(pid_tgid), GET_USER_MODE_PID(pid_tgid)); conn_tuple_t t = {}; if (!read_conn_tuple(&t, sk, pid_tgid, CONN_TYPE_TCP)) { diff --git a/pkg/network/ebpf/c/tracer/bind.h b/pkg/network/ebpf/c/tracer/bind.h index 67a01a2da4f4c..cc8c3dd99e5cc 100644 --- a/pkg/network/ebpf/c/tracer/bind.h +++ b/pkg/network/ebpf/c/tracer/bind.h @@ -1,6 +1,7 @@ #ifndef __TRACER_BIND_H #define __TRACER_BIND_H +#include "pid_tgid.h" #include "tracer/tracer.h" #include "tracer/maps.h" #include "tracer/port.h" @@ -19,7 +20,7 @@ static __always_inline u16 sockaddr_sin_port(struct sockaddr *addr) { } static __always_inline int sys_enter_bind(struct socket *sock, struct sockaddr *addr) { - __u64 tid = bpf_get_current_pid_tgid(); + __u64 pid_tgid = bpf_get_current_pid_tgid(); __u16 type = 0; bpf_probe_read_kernel_with_telemetry(&type, sizeof(__u16), &sock->type); @@ -28,7 +29,7 @@ static __always_inline int sys_enter_bind(struct socket *sock, struct sockaddr * } if (addr == NULL) { - log_debug("sys_enter_bind: could not read sockaddr, sock=%p, tid=%llu", sock, tid); + log_debug("sys_enter_bind: could not read sockaddr, sock=%p, pid_tgid=%llu", sock, pid_tgid); return 0; } @@ -49,19 +50,19 @@ static __always_inline int sys_enter_bind(struct socket *sock, struct sockaddr * args.addr = addr; - bpf_map_update_with_telemetry(pending_bind, &tid, &args, BPF_ANY); - log_debug("sys_enter_bind: started a bind on UDP sock=%p tid=%llu", sock, tid); + bpf_map_update_with_telemetry(pending_bind, &pid_tgid, &args, BPF_ANY); + log_debug("sys_enter_bind: started a bind on UDP sock=%p pid_tgid=%llu", sock, pid_tgid); return 0; } static __always_inline int sys_exit_bind(__s64 ret) { - __u64 tid = bpf_get_current_pid_tgid(); + __u64 pid_tgid = bpf_get_current_pid_tgid(); // bail if this bind() is not the one we're instrumenting - bind_syscall_args_t *args = bpf_map_lookup_elem(&pending_bind, &tid); + bind_syscall_args_t *args = bpf_map_lookup_elem(&pending_bind, &pid_tgid); - log_debug("sys_exit_bind: tid=%llu, ret=%lld", tid, ret); + log_debug("sys_exit_bind: pid_tgid=%llu, ret=%lld", pid_tgid, ret); if (args == NULL) { log_debug("sys_exit_bind: was not a UDP bind, will not process"); @@ -70,7 +71,7 @@ static __always_inline int sys_exit_bind(__s64 ret) { struct sock * sk = args->sk; struct sockaddr *addr = args->addr; - bpf_map_delete_elem(&pending_bind, &tid); + bpf_map_delete_elem(&pending_bind, &pid_tgid); if (ret != 0) { return 0; diff --git a/pkg/network/ebpf/c/tracer/stats.h b/pkg/network/ebpf/c/tracer/stats.h index b05811c86190a..862d8e32f81f1 100644 --- a/pkg/network/ebpf/c/tracer/stats.h +++ b/pkg/network/ebpf/c/tracer/stats.h @@ -16,6 +16,7 @@ #include "protocols/tls/tags-types.h" #include "ip.h" #include "skb.h" +#include "pid_tgid.h" #ifdef COMPILE_PREBUILT static __always_inline __u64 offset_rtt(); @@ -286,7 +287,7 @@ static __always_inline int handle_skb_consume_udp(struct sock *sk, struct sk_buf flip_tuple(&t); log_debug("skb_consume_udp: bytes=%d", data_len); - t.pid = pid_tgid >> 32; + t.pid = GET_USER_MODE_PID(pid_tgid); t.netns = get_netns_from_sock(sk); return handle_message(&t, 0, data_len, CONN_DIRECTION_UNKNOWN, 0, 1, PACKET_COUNT_INCREMENT, sk); } From a5253a069be79c1016185470e1d6d82a8d58c223 Mon Sep 17 00:00:00 2001 From: pducolin <45568537+pducolin@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:35:54 +0100 Subject: [PATCH 67/78] [gitlab] allow triggering a dev bump of test-infra (#32245) --- tasks/buildimages.py | 20 ++++++++++++-------- tasks/libs/ciproviders/gitlab_api.py | 28 ++++++++++++++++++---------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/tasks/buildimages.py b/tasks/buildimages.py index 2f7d228d4d070..e2bf5316e18c7 100644 --- a/tasks/buildimages.py +++ b/tasks/buildimages.py @@ -1,7 +1,5 @@ from __future__ import annotations -import os - import yaml from invoke import Context, Exit, task @@ -37,17 +35,23 @@ def update(_: Context, tag: str = "", images: str = "", test: bool = True, list_ print(f" {', '.join(modified)}") -@task(help={"commit_sha": "commit sha from the test-infra-definitions repository"}) -def update_test_infra_definitions(ctx: Context, commit_sha: str, go_mod_only: bool = False): +@task( + help={ + "commit_sha": "commit sha from the test-infra-definitions repository", + "go_mod_only": "Update only the go.mod file", + "is_dev_image": "Is the image bumped to a dev version, used to test changes in test-infra-definitions", + } +) +def update_test_infra_definitions(ctx: Context, commit_sha: str, go_mod_only: bool = False, is_dev_image: bool = False): """ Update the test-infra-definition image version in the Gitlab CI as well as in the e2e go.mod """ if not go_mod_only: - update_test_infra_def(".gitlab/common/test_infra_version.yml", commit_sha[:12]) + update_test_infra_def(".gitlab/common/test_infra_version.yml", commit_sha[:12], is_dev_image) - os.chdir("test/new-e2e") - ctx.run(f"go get github.com/DataDog/test-infra-definitions@{commit_sha}") - ctx.run("go mod tidy") + with ctx.cd("test/new-e2e"): + ctx.run(f"go get github.com/DataDog/test-infra-definitions@{commit_sha}") + ctx.run("go mod tidy") @task( diff --git a/tasks/libs/ciproviders/gitlab_api.py b/tasks/libs/ciproviders/gitlab_api.py index cb4e42644e7c6..adfd8aacfb569 100644 --- a/tasks/libs/ciproviders/gitlab_api.py +++ b/tasks/libs/ciproviders/gitlab_api.py @@ -1258,19 +1258,27 @@ def full_config_get_all_stages(full_config: dict) -> set[str]: return all_stages -def update_test_infra_def(file_path, image_tag): +def update_test_infra_def(file_path, image_tag, is_dev_image=False): """ - Override TEST_INFRA_DEFINITIONS_BUILDIMAGES in `.gitlab/common/test_infra_version.yml` file + Updates TEST_INFRA_DEFINITIONS_BUILDIMAGES in `.gitlab/common/test_infra_version.yml` file """ - with open(file_path) as gl: - file_content = gl.readlines() - with open(file_path, "w") as gl: - for line in file_content: - test_infra_def = re.search(r"TEST_INFRA_DEFINITIONS_BUILDIMAGES:\s*(\w+)", line) - if test_infra_def: - gl.write(line.replace(test_infra_def.group(1), image_tag)) + import yaml + + test_infra_def = {} + with open(file_path) as test_infra_version_file: + try: + test_infra_def = yaml.safe_load(test_infra_version_file) + test_infra_def["variables"]["TEST_INFRA_DEFINITIONS_BUILDIMAGES"] = image_tag + if is_dev_image: + test_infra_def["variables"]["TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX"] = "-dev" else: - gl.write(line) + test_infra_def["variables"]["TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX"] = "" + except yaml.YAMLError as e: + raise Exit(f"Error while loading {file_path}: {e}") from e + with open(file_path, "w") as test_infra_version_file: + # Add explicit_start=True to keep the document start marker --- + # See "Document Start" in https://www.yaml.info/learn/document.html for more details + yaml.dump(test_infra_def, test_infra_version_file, explicit_start=True) def update_gitlab_config(file_path, tag, images="", test=True, update=True): From 0eab37397f2a120f891e7f9d3df6f4fe9db29051 Mon Sep 17 00:00:00 2001 From: Guy Arbitman Date: Tue, 17 Dec 2024 14:58:33 +0200 Subject: [PATCH 68/78] usm: sharedlibraries: Increase size of a map (#32286) --- pkg/network/ebpf/c/protocols/tls/native-tls-maps.h | 2 +- pkg/network/usm/ebpf_ssl.go | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h b/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h index 282c593308b16..5e139833d4302 100644 --- a/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h +++ b/pkg/network/ebpf/c/protocols/tls/native-tls-maps.h @@ -17,6 +17,6 @@ BPF_HASH_MAP(bio_new_socket_args, __u64, __u32, 1024) BPF_HASH_MAP(fd_by_ssl_bio, __u32, void *, 1024) -BPF_HASH_MAP(ssl_ctx_by_pid_tgid, __u64, void *, 1024) +BPF_HASH_MAP(ssl_ctx_by_pid_tgid, __u64, void *, 1) #endif diff --git a/pkg/network/usm/ebpf_ssl.go b/pkg/network/usm/ebpf_ssl.go index 64e452ea7fe45..4a7800de41d4b 100644 --- a/pkg/network/usm/ebpf_ssl.go +++ b/pkg/network/usm/ebpf_ssl.go @@ -237,7 +237,8 @@ var gnuTLSProbes = []manager.ProbesSelector{ } const ( - sslSockByCtxMap = "ssl_sock_by_ctx" + sslSockByCtxMap = "ssl_sock_by_ctx" + sslCtxByPIDTGIDMap = "ssl_ctx_by_pid_tgid" ) var ( @@ -271,7 +272,7 @@ var opensslSpec = &protocols.ProtocolSpec{ Name: "fd_by_ssl_bio", }, { - Name: "ssl_ctx_by_pid_tgid", + Name: sslCtxByPIDTGIDMap, }, }, Probes: []*manager.Probe{ @@ -491,6 +492,10 @@ func (o *sslProgram) ConfigureOptions(_ *manager.Manager, options *manager.Optio MaxEntries: o.cfg.MaxTrackedConnections, EditorFlag: manager.EditMaxEntries, } + options.MapSpecEditors[sslCtxByPIDTGIDMap] = manager.MapSpecEditor{ + MaxEntries: o.cfg.MaxTrackedConnections, + EditorFlag: manager.EditMaxEntries, + } } // PreStart is called before the start of the provided eBPF manager. @@ -552,7 +557,7 @@ func (o *sslProgram) DumpMaps(w io.Writer, mapName string, currentMap *ebpf.Map) spew.Fdump(w, key, value) } - case "ssl_ctx_by_pid_tgid": // maps/ssl_ctx_by_pid_tgid (BPF_MAP_TYPE_HASH), key C.__u64, value uintptr // C.void * + case sslCtxByPIDTGIDMap: // maps/ssl_ctx_by_pid_tgid (BPF_MAP_TYPE_HASH), key C.__u64, value uintptr // C.void * io.WriteString(w, "Map: '"+mapName+"', key: 'C.__u64', value: 'uintptr // C.void *'\n") iter := currentMap.Iterate() var key uint64 From 9dd33c3f0814a3aaf31280d1d49ff46d741a9947 Mon Sep 17 00:00:00 2001 From: Alex Lopez Date: Tue, 17 Dec 2024 13:58:44 +0100 Subject: [PATCH 69/78] Disable Omnibus git cache for Windows builds (#32289) --- .gitlab/package_build/windows.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab/package_build/windows.yml b/.gitlab/package_build/windows.yml index ea458c9db77a7..88c93ac82c01c 100644 --- a/.gitlab/package_build/windows.yml +++ b/.gitlab/package_build/windows.yml @@ -36,7 +36,6 @@ -e BUNDLE_MIRROR__RUBYGEMS__ORG=${BUNDLE_MIRROR__RUBYGEMS__ORG} -e PIP_INDEX_URL=${PIP_INDEX_URL} -e API_KEY_ORG2=${API_KEY_ORG2} - -e OMNIBUS_GIT_CACHE_DIR=${Env:TEMP}/${CI_PIPELINE_ID}/omnibus-git-cache -e AGENT_FLAVOR=${AGENT_FLAVOR} registry.ddbuild.io/ci/datadog-agent-buildimages/windows_1809_${ARCH}${Env:DATADOG_AGENT_WINBUILDIMAGES_SUFFIX}:${Env:DATADOG_AGENT_WINBUILDIMAGES} powershell -C "c:\mnt\tasks\winbuildscripts\Build-AgentPackages.ps1 -BuildOutOfSource 1 -InstallDeps 1 -CheckGoVersion 1" From 8c87fc5ddc3423e6c0851b1842e78d2adb06abba Mon Sep 17 00:00:00 2001 From: Jack Phillips Date: Tue, 17 Dec 2024 08:24:57 -0500 Subject: [PATCH 70/78] update embedded permissions (#32119) Co-authored-by: DeForest Richards <56796055+drichards-87@users.noreply.github.com> --- pkg/collector/python/init_windows.go | 38 +++++++++++++++++++ ...embedded-permissions-a8e169f1c079cd41.yaml | 11 ++++++ .../windows/install-test/installtester.go | 25 ------------ .../ConfigureUserCustomActions.cs | 3 -- 4 files changed, 49 insertions(+), 28 deletions(-) create mode 100644 releasenotes/notes/update-embedded-permissions-a8e169f1c079cd41.yaml diff --git a/pkg/collector/python/init_windows.go b/pkg/collector/python/init_windows.go index 04d18175df1f7..3de1ba7a459b0 100644 --- a/pkg/collector/python/init_windows.go +++ b/pkg/collector/python/init_windows.go @@ -9,8 +9,10 @@ package python import ( "os" + "path/filepath" pkgconfigsetup "github.com/DataDog/datadog-agent/pkg/config/setup" + "github.com/DataDog/datadog-agent/pkg/util/winutil" ) // Any platform-specific initialization belongs here. @@ -21,5 +23,41 @@ func initializePlatform() error { os.Unsetenv("PYTHONPATH") } + // only use cache file when not admin + admin, _ := winutil.IsUserAnAdmin() + if !admin { + err := enableSeparatePythonCacheDir() + if err != nil { + return err + } + } + return nil } + +// enableSeparatePythonCacheDir configures Python to use a separate directory for its pycache. +// +// Creates a python-cache subdir in the configuration directory and configures Python to use it via the PYTHONPYCACHEPREFIX env var. +// +// https://docs.python.org/3/using/cmdline.html#envvar-PYTHONPYCACHEPREFIX +func enableSeparatePythonCacheDir() error { + pd, err := winutil.GetProgramDataDir() + if err != nil { + return err + } + pycache := filepath.Join(pd, "python-cache") + + // check if path exists and create directory if it doesn't + if _, err := os.Stat(pycache); os.IsNotExist(err) { + if err := os.MkdirAll(pycache, 0755); err != nil { + return err + } + } else if err != nil { + return err + } + + os.Setenv("PYTHONPYCACHEPREFIX", pycache) + + return nil + +} diff --git a/releasenotes/notes/update-embedded-permissions-a8e169f1c079cd41.yaml b/releasenotes/notes/update-embedded-permissions-a8e169f1c079cd41.yaml new file mode 100644 index 0000000000000..8b9cafd486a16 --- /dev/null +++ b/releasenotes/notes/update-embedded-permissions-a8e169f1c079cd41.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. +--- +security: + - | + Move the embedded Python cache out of the installation directory on Windows. diff --git a/test/new-e2e/tests/windows/install-test/installtester.go b/test/new-e2e/tests/windows/install-test/installtester.go index e0fbee6886600..f0c86fdf8c9d1 100644 --- a/test/new-e2e/tests/windows/install-test/installtester.go +++ b/test/new-e2e/tests/windows/install-test/installtester.go @@ -536,31 +536,6 @@ func (t *Tester) testInstalledFilePermissions(tt *testing.T, ddAgentUserIdentity }) } - // expect to have standard inherited permissions, plus an explciit ACE for ddagentuser - embeddedPaths := []string{ - filepath.Join(t.expectedInstallPath, "embedded3"), - } - if t.ExpectPython2Installed() { - embeddedPaths = append(embeddedPaths, - filepath.Join(t.expectedInstallPath, "embedded2"), - ) - } - agentUserFullAccessDirRule := windows.NewExplicitAccessRuleWithFlags( - ddAgentUserIdentity, - windows.FileFullControl, - windows.AccessControlTypeAllow, - windows.InheritanceFlagsContainer|windows.InheritanceFlagsObject, - windows.PropagationFlagsNone, - ) - for _, path := range embeddedPaths { - out, err := windows.GetSecurityInfoForPath(t.host, path) - require.NoError(tt, err) - if !windows.IsIdentityLocalSystem(ddAgentUserIdentity) { - windows.AssertContainsEqualable(tt, out.Access, agentUserFullAccessDirRule, "%s should have full access rule for %s", path, ddAgentUserIdentity) - } - assert.False(tt, out.AreAccessRulesProtected, "%s should inherit access rules", path) - } - // ensure the agent user does not have an ACE on the install dir out, err := windows.GetSecurityInfoForPath(t.host, t.expectedInstallPath) require.NoError(tt, err) diff --git a/tools/windows/DatadogAgentInstaller/CustomActions/ConfigureUserCustomActions.cs b/tools/windows/DatadogAgentInstaller/CustomActions/ConfigureUserCustomActions.cs index 1447f8a080fd0..817f118b0c797 100644 --- a/tools/windows/DatadogAgentInstaller/CustomActions/ConfigureUserCustomActions.cs +++ b/tools/windows/DatadogAgentInstaller/CustomActions/ConfigureUserCustomActions.cs @@ -586,9 +586,6 @@ private List PathsWithAgentAccess() // agent needs to be able to write to run/ // agent needs to be able to create auth_token _session.Property("APPLICATIONDATADIRECTORY"), - // allow agent to write __pycache__ - Path.Combine(_session.Property("PROJECTLOCATION"), "embedded2"), - Path.Combine(_session.Property("PROJECTLOCATION"), "embedded3"), }; } From d18fc884c26d4e5b1cbf1344b005253c342df311 Mon Sep 17 00:00:00 2001 From: Arthur Bellal Date: Tue, 17 Dec 2024 14:34:37 +0100 Subject: [PATCH 71/78] (fleet) persist proxy settings during setup (#32217) --- pkg/fleet/installer/setup/common/config.go | 8 ++++++++ pkg/fleet/installer/setup/common/setup.go | 12 +++++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/fleet/installer/setup/common/config.go b/pkg/fleet/installer/setup/common/config.go index 650b6b0acebe7..7d9e00aba1483 100644 --- a/pkg/fleet/installer/setup/common/config.go +++ b/pkg/fleet/installer/setup/common/config.go @@ -103,6 +103,7 @@ type DatadogConfig struct { APIKey string `yaml:"api_key"` Hostname string `yaml:"hostname,omitempty"` Site string `yaml:"site,omitempty"` + Proxy DatadogConfigProxy `yaml:"proxy,omitempty"` Env string `yaml:"env,omitempty"` Tags []string `yaml:"tags,omitempty"` LogsEnabled bool `yaml:"logs_enabled,omitempty"` @@ -111,6 +112,13 @@ type DatadogConfig struct { ExpectedTagsDuration string `yaml:"expected_tags_duration,omitempty"` } +// DatadogConfigProxy represents the configuration for the proxy +type DatadogConfigProxy struct { + HTTP string `yaml:"http,omitempty"` + HTTPS string `yaml:"https,omitempty"` + NoProxy []string `yaml:"no_proxy,omitempty"` +} + // DatadogConfigDJM represents the configuration for the Data Jobs Monitoring type DatadogConfigDJM struct { Enabled bool `yaml:"enabled,omitempty"` diff --git a/pkg/fleet/installer/setup/common/setup.go b/pkg/fleet/installer/setup/common/setup.go index 60931ef01da69..0a7b93447c22f 100644 --- a/pkg/fleet/installer/setup/common/setup.go +++ b/pkg/fleet/installer/setup/common/setup.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "os" + "strings" "time" "github.com/DataDog/datadog-agent/pkg/fleet/installer" @@ -60,6 +61,10 @@ Running the %s installation script (https://github.com/DataDog/datadog-agent/tre if err != nil { return nil, fmt.Errorf("failed to create installer: %w", err) } + var proxyNoProxy []string + if os.Getenv("DD_PROXY_NO_PROXY") != "" { + proxyNoProxy = strings.Split(os.Getenv("DD_PROXY_NO_PROXY"), ",") + } span, ctx := telemetry.StartSpanFromContext(ctx, fmt.Sprintf("setup.%s", flavor)) s := &Setup{ configDir: configDir, @@ -75,7 +80,12 @@ Running the %s installation script (https://github.com/DataDog/datadog-agent/tre APIKey: env.APIKey, Hostname: os.Getenv("DD_HOSTNAME"), Site: env.Site, - Env: os.Getenv("DD_ENV"), + Proxy: DatadogConfigProxy{ + HTTP: os.Getenv("DD_PROXY_HTTP"), + HTTPS: os.Getenv("DD_PROXY_HTTPS"), + NoProxy: proxyNoProxy, + }, + Env: os.Getenv("DD_ENV"), }, IntegrationConfigs: make(map[string]IntegrationConfig), }, From 6c2005471e5ffb61ec454ceac51ee9cf7087f615 Mon Sep 17 00:00:00 2001 From: Kevin Fairise <132568982+KevinFairise2@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:54:15 +0100 Subject: [PATCH 72/78] Exit early if CI_COMMIT_REF_NAME is empty (#32241) --- tasks/pipeline.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tasks/pipeline.py b/tasks/pipeline.py index 59aa6a3666172..ad825bee1540d 100644 --- a/tasks/pipeline.py +++ b/tasks/pipeline.py @@ -131,6 +131,9 @@ def auto_cancel_previous_pipelines(ctx): raise Exit("GITLAB_TOKEN variable needed to cancel pipelines on the same ref.", 1) git_ref = os.environ["CI_COMMIT_REF_NAME"] + if git_ref == "": + raise Exit("CI_COMMIT_REF_NAME is empty, skipping pipeline cancellation", 0) + git_sha = os.getenv("CI_COMMIT_SHA") repo = get_gitlab_repo() From 5d81b5d4377ca3be9dac2134700683eedb516337 Mon Sep 17 00:00:00 2001 From: Aldrick Castro Date: Tue, 17 Dec 2024 09:27:20 -0500 Subject: [PATCH 73/78] Corrected how we submit the oracle.can_connect check (#31945) --- pkg/collector/corechecks/oracle/oracle.go | 2 +- .../corechecks/oracle/oracle_integration_test.go | 8 ++++++++ ...-oracle-can_connect-host-tag-c7b00cb45e17735c.yaml | 11 +++++++++++ 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-oracle-can_connect-host-tag-c7b00cb45e17735c.yaml diff --git a/pkg/collector/corechecks/oracle/oracle.go b/pkg/collector/corechecks/oracle/oracle.go index a36830ceb39bb..224555ba85bd6 100644 --- a/pkg/collector/corechecks/oracle/oracle.go +++ b/pkg/collector/corechecks/oracle/oracle.go @@ -137,7 +137,7 @@ func handleServiceCheck(c *Check, err error) { status = servicecheck.ServiceCheckCritical log.Errorf("%s failed to connect: %s", c.logPrompt, err) } - sender.ServiceCheck("oracle.can_connect", status, "", c.tags, message) + sendServiceCheck(c, "oracle.can_connect", status, message) sender.Commit() } diff --git a/pkg/collector/corechecks/oracle/oracle_integration_test.go b/pkg/collector/corechecks/oracle/oracle_integration_test.go index c0df585ca2065..0d37c3d66ec50 100644 --- a/pkg/collector/corechecks/oracle/oracle_integration_test.go +++ b/pkg/collector/corechecks/oracle/oracle_integration_test.go @@ -424,6 +424,14 @@ func buildConnectionString(connectionConfig config.ConnectionConfig) string { return connStr } +func TestCanConnectTags(t *testing.T) { + chk, sender := newDefaultCheck(t, "", "") + err := chk.Run() + require.NoError(t, err) + + sender.AssertServiceCheck(t, "oracle.can_connect", servicecheck.ServiceCheckOK, chk.dbHostname, nil, "") +} + func TestLargeUint64Binding(t *testing.T) { var err error c, _ := newDefaultCheck(t, "", "") diff --git a/releasenotes/notes/fix-oracle-can_connect-host-tag-c7b00cb45e17735c.yaml b/releasenotes/notes/fix-oracle-can_connect-host-tag-c7b00cb45e17735c.yaml new file mode 100644 index 0000000000000..df805c338b08c --- /dev/null +++ b/releasenotes/notes/fix-oracle-can_connect-host-tag-c7b00cb45e17735c.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: + - | + Corrects host tagging for the oracle.can_connect service check From 593c137a1db537f340ffb990c7d30386d7bec289 Mon Sep 17 00:00:00 2001 From: "agent-platform-auto-pr[bot]" <153269286+agent-platform-auto-pr[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:29:14 +0000 Subject: [PATCH 74/78] [test-infra-definitions][automated] Bump test-infra-definitions to 4b4112f5f64d18c33cc15eb1bc8eccb603b71a93 (#32288) Co-authored-by: L3n41c Co-authored-by: pducolin Co-authored-by: adel121 --- .gitlab/common/test_infra_version.yml | 2 +- go.mod | 20 +++--- go.sum | 40 +++++------ test/new-e2e/go.mod | 44 ++++++------ test/new-e2e/go.sum | 84 +++++++++++------------ test/new-e2e/tests/containers/k8s_test.go | 10 ++- test/new-e2e/tests/orchestrator/apply.go | 7 ++ 7 files changed, 110 insertions(+), 97 deletions(-) diff --git a/.gitlab/common/test_infra_version.yml b/.gitlab/common/test_infra_version.yml index 7d435a4bc3a4b..71934be35d15a 100644 --- a/.gitlab/common/test_infra_version.yml +++ b/.gitlab/common/test_infra_version.yml @@ -4,4 +4,4 @@ variables: # and check the job creating the image to make sure you have the right SHA prefix TEST_INFRA_DEFINITIONS_BUILDIMAGES_SUFFIX: "" # Make sure to update test-infra-definitions version in go.mod as well - TEST_INFRA_DEFINITIONS_BUILDIMAGES: 9c7c5005ca28 + TEST_INFRA_DEFINITIONS_BUILDIMAGES: 4b4112f5f64d diff --git a/go.mod b/go.mod index e8175e8cf0efb..30981dbec9ce2 100644 --- a/go.mod +++ b/go.mod @@ -375,19 +375,19 @@ require ( github.com/armon/go-metrics v0.4.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect - github.com/aws/aws-sdk-go-v2 v1.32.5 - github.com/aws/aws-sdk-go-v2/config v1.28.5 - github.com/aws/aws-sdk-go-v2/credentials v1.17.46 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2 v1.32.6 + github.com/aws/aws-sdk-go-v2/config v1.28.6 + github.com/aws/aws-sdk-go-v2/credentials v1.17.47 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect github.com/aws/aws-sdk-go-v2/service/ebs v1.27.0 // indirect github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect github.com/aws/smithy-go v1.22.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40 // indirect diff --git a/go.sum b/go.sum index 44b70bde70c36..26375333ef0cf 100644 --- a/go.sum +++ b/go.sum @@ -321,24 +321,24 @@ github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2z github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= github.com/aws/aws-sdk-go-v2 v1.21.2/go.mod h1:ErQhvNuEMhJjweavOYhxVkn2RUx7kQXVATHrjKtxIpM= -github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= -github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.14/go.mod h1:9NCTOURS8OpxvoAVHq79LK81/zC78hfRWFn+aL0SPcY= github.com/aws/aws-sdk-go-v2/config v1.19.0/go.mod h1:ZwDUgFnQgsazQTnWfeLWk5GjeqTQTL8lMkoE1UXzxdE= -github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= -github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= +github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= +github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= github.com/aws/aws-sdk-go-v2/credentials v1.13.43/go.mod h1:zWJBz1Yf1ZtX5NGax9ZdNjhhI4rgjfgsyk6vTY1yfVg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.13/go.mod h1:f/Ib/qYjhV2/qdsf79H3QP/eRE4AkVyEf6sk7XfZ1tg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.43/go.mod h1:auo+PiyLl0n1l8A0e8RIeR8tOzYPfZZH/JNlrJ8igTQ= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.37/go.mod h1:Qe+2KtKml+FEsQF/DHmDV+xjtche/hwoF75EG4UlHW8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.45/go.mod h1:lD5M20o09/LCuQ2mE62Mb/iSdSlCNuj6H5ci7tW7OsE= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= @@ -352,8 +352,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhv github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.38/go.mod h1:epIZoRSSbRIwLPJU5F+OldHhwZPBdpDeQkRdCeY3+00= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.37/go.mod h1:vBmDnwWXWxNPFRMmG2m/3MKOe+xEcMDo1tanpaWCcck= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.6/go.mod h1:lnc2taBsR9nTlz9meD+lhFZZ9EWY712QHrRflWpTcOA= github.com/aws/aws-sdk-go-v2/service/kms v1.37.6 h1:CZImQdb1QbU9sGgJ9IswhVkxAcjkkD1eQTMA1KHWk+E= github.com/aws/aws-sdk-go-v2/service/kms v1.37.6/go.mod h1:YJDdlK0zsyxVBxGU48AR/Mi8DMrGdc1E3Yij4fNrONA= @@ -363,14 +363,14 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.40.2/go.mod h1:Zjfqt7KhQK+PO1bbOsFNzK github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6 h1:1KDMKvOKNrpD667ORbZ/+4OgvUoaok1gg/MLzrHF9fw= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.34.6/go.mod h1:DmtyfCfONhOyVAJ6ZMTrDSFIeyCBlEO93Qkfhxwbxu0= github.com/aws/aws-sdk-go-v2/service/sso v1.15.2/go.mod h1:gsL4keucRCgW+xA85ALBpRFfdSLH4kHOVSnLMSuBECo= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.17.3/go.mod h1:a7bHA82fyUXOm+ZSWKU6PIoBxrjSprdLoM8xPYvzYVg= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= github.com/aws/aws-sdk-go-v2/service/sts v1.23.2/go.mod h1:Eows6e1uQEsc4ZaHANmsPRzAKcVDrcmjjWiih2+HUUQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= github.com/aws/smithy-go v1.15.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= diff --git a/test/new-e2e/go.mod b/test/new-e2e/go.mod index 008a6b32a4753..2bd2138c3ea69 100644 --- a/test/new-e2e/go.mod +++ b/test/new-e2e/go.mod @@ -45,7 +45,7 @@ replace ( require ( github.com/DataDog/agent-payload/v5 v5.0.138 github.com/DataDog/datadog-agent/comp/otelcol/ddflareextension/def v0.56.2 - github.com/DataDog/datadog-agent/pkg/util/optional v0.59.0 + github.com/DataDog/datadog-agent/pkg/util/optional v0.59.1 github.com/DataDog/datadog-agent/pkg/util/pointer v0.59.0 github.com/DataDog/datadog-agent/pkg/util/scrubber v0.59.0 github.com/DataDog/datadog-agent/pkg/util/testutil v0.56.2 @@ -58,9 +58,9 @@ require ( // `TEST_INFRA_DEFINITIONS_BUILDIMAGES` matches the commit sha in the module version // Example: github.com/DataDog/test-infra-definitions v0.0.0-YYYYMMDDHHmmSS-0123456789AB // => TEST_INFRA_DEFINITIONS_BUILDIMAGES: 0123456789AB - github.com/DataDog/test-infra-definitions v0.0.0-20241211124138-9c7c5005ca28 - github.com/aws/aws-sdk-go-v2 v1.32.5 - github.com/aws/aws-sdk-go-v2/config v1.28.5 + github.com/DataDog/test-infra-definitions v0.0.0-20241217110507-4b4112f5f64d + github.com/aws/aws-sdk-go-v2 v1.32.6 + github.com/aws/aws-sdk-go-v2/config v1.28.6 github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2 @@ -109,22 +109,22 @@ require ( github.com/alessio/shellescape v1.4.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.46 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.47 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 // indirect - github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 + github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ecs v1.52.2 github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 // indirect github.com/aws/smithy-go v1.22.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/blang/semver v3.5.1+incompatible // indirect @@ -217,8 +217,8 @@ require ( github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect github.com/pulumi/esc v0.10.0 // indirect github.com/pulumi/pulumi-command/sdk v1.0.1 // indirect - github.com/pulumi/pulumi-docker/sdk/v4 v4.5.5 // indirect - github.com/pulumi/pulumi-libvirt/sdk v0.4.7 // indirect + github.com/pulumi/pulumi-docker/sdk/v4 v4.5.7 // indirect + github.com/pulumi/pulumi-libvirt/sdk v0.5.3 // indirect github.com/pulumi/pulumi-random/sdk/v4 v4.16.7 // indirect github.com/pulumi/pulumi-tls/sdk/v4 v4.11.1 // indirect github.com/pulumiverse/pulumi-time/sdk v0.1.0 // indirect @@ -284,7 +284,7 @@ require ( github.com/DataDog/datadog-agent/pkg/trace v0.56.0-rc.3 github.com/DataDog/datadog-go/v5 v5.6.0 github.com/aws/aws-sdk-go v1.55.5 - github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492 github.com/digitalocean/go-libvirt v0.0.0-20240812180835-9c6c0a310c6c github.com/hairyhenderson/go-codeowners v0.7.0 @@ -309,7 +309,7 @@ require ( github.com/pulumi/pulumi-azure-native-sdk/managedidentity/v2 v2.73.1 // indirect github.com/pulumi/pulumi-azure-native-sdk/network/v2 v2.73.1 // indirect github.com/pulumi/pulumi-azure-native-sdk/v2 v2.73.1 // indirect - github.com/pulumi/pulumi-eks/sdk/v3 v3.3.0 // indirect + github.com/pulumi/pulumi-eks/sdk/v3 v3.4.0 // indirect github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0 // indirect github.com/twinj/uuid v0.0.0-20151029044442-89173bcdda19 // indirect github.com/x448/float16 v0.8.4 // indirect diff --git a/test/new-e2e/go.sum b/test/new-e2e/go.sum index cbbd971dd8ac3..b08902fb2e378 100644 --- a/test/new-e2e/go.sum +++ b/test/new-e2e/go.sum @@ -17,8 +17,8 @@ github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEU github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49 h1:EbzDX8HPk5uE2FsJYxD74QmMw0/3CqSKhEr6teh0ncQ= github.com/DataDog/mmh3 v0.0.0-20210722141835-012dc69a9e49/go.mod h1:SvsjzyJlSg0rKsqYgdcFxeEVflx3ZNAyFfkUHP0TxXg= -github.com/DataDog/test-infra-definitions v0.0.0-20241211124138-9c7c5005ca28 h1:LaZgAke+RN4wBKNl+R10ewdtKe/C2MJCbp9ozXKlLP8= -github.com/DataDog/test-infra-definitions v0.0.0-20241211124138-9c7c5005ca28/go.mod h1:blPG0VXBgk1oXm2+KHMTMyR0sNI2jv51FACAYPNQvNo= +github.com/DataDog/test-infra-definitions v0.0.0-20241217110507-4b4112f5f64d h1:F1yqGiWtXVsHkMiNUhs8bgaoZ1WlV3rYGRZ1CtCxpm8= +github.com/DataDog/test-infra-definitions v0.0.0-20241217110507-4b4112f5f64d/go.mod h1:1PAUwGjC25ACjfft4HrLEmHliuajlvjzcLFWpuqAIyk= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd_0 v0.0.0-20210310093942-586c1286621f h1:5Vuo4niPKFkfwW55jV4vY0ih3VQ9RaQqeqY67fvRn8A= @@ -52,50 +52,50 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.5 h1:U8vdWJuY7ruAkzaOdD7guwJjD06YSKmnKCJs7s3IkIo= -github.com/aws/aws-sdk-go-v2 v1.32.5/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6 h1:pT3hpW0cOHRJx8Y0DfJUEQuqPild8jRGmSFmBgvydr0= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.6/go.mod h1:j/I2++U0xX+cr44QjHay4Cvxj6FUbnxrgmqN3H1jTZA= -github.com/aws/aws-sdk-go-v2/config v1.28.5 h1:Za41twdCXbuyyWv9LndXxZZv3QhTG1DinqlFsSuvtI0= -github.com/aws/aws-sdk-go-v2/config v1.28.5/go.mod h1:4VsPbHP8JdcdUDmbTVgNL/8w9SqOkM5jyY8ljIxLO3o= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46 h1:AU7RcriIo2lXjUfHFnFKYsLCwgbz1E7Mm95ieIRDNUg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.46/go.mod h1:1FmYyLGL08KQXQ6mcTlifyFXfJVCNJTVGuQP4m0d/UA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20 h1:sDSXIrlsFSFJtWKLQS4PUWRvrT580rrnuLydJrCQ/yA= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.20/go.mod h1:WZ/c+w0ofps+/OUqMwWgnfrgzZH1DZO1RIkktICsqnY= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24 h1:4usbeaes3yJnCFC7kfeyhkdkPtoRYPa/hTmCqMpKpLI= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.24/go.mod h1:5CI1JemjVwde8m2WG3cz23qHKPOxbpkq0HaoreEgLIY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24 h1:N1zsICrQglfzaBnrfM0Ys00860C+QFwu6u/5+LomP+o= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.24/go.mod h1:dCn9HbJ8+K31i8IQ8EWmWj0EiIk0+vKiHNMxTTYveAg= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/config v1.28.6 h1:D89IKtGrs/I3QXOLNTH93NJYtDhm8SYa9Q5CsPShmyo= +github.com/aws/aws-sdk-go-v2/config v1.28.6/go.mod h1:GDzxJ5wyyFSCoLkS+UhGB0dArhb9mI+Co4dHtoTxbko= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47 h1:48bA+3/fCdi2yAwVt+3COvmatZ6jUDNkDTIsqDiMUdw= +github.com/aws/aws-sdk-go-v2/credentials v1.17.47/go.mod h1:+KdckOejLW3Ks3b0E3b5rHsr2f9yuORBum0WPnE5o5w= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21 h1:AmoU1pziydclFT/xRV+xXE/Vb8fttJCLRPv8oAkprc0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.21/go.mod h1:AjUdLYe4Tgs6kpH4Bv7uMZo7pottoyHMn4eTcIcneaY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21 h1:7edmS3VOBDhK00b/MwGtGglCm7hhwNYnjJs/PgFdMQE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.21/go.mod h1:Q9o5h4HoIWG8XfzxqiuK/CGUbepCJ8uTlaE3bAbxytQ= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0 h1:k97fGog9Tl0woxTiSIHN14Qs5ehqK6GXejUwkhJYyL0= github.com/aws/aws-sdk-go-v2/service/ec2 v1.190.0/go.mod h1:mzj8EEjIHSN2oZRXiw1Dd+uB4HZTl7hC8nBzX9IZMWw= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2 h1:VDQaVwGOokbd3VUbHF+wupiffdrbAZPdQnr5XZMJqrs= -github.com/aws/aws-sdk-go-v2/service/ecr v1.36.2/go.mod h1:lvUlMghKYmSxSfv0vU7pdU/8jSY+s0zpG8xXhaGKCw0= -github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4 h1:CTkPGE8fiElvLtYWl/U+Eu5+1fVXiZbJUjyVCRSRgxk= -github.com/aws/aws-sdk-go-v2/service/ecs v1.47.4/go.mod h1:sMFLFhL27cKYa/eQYZp4asvIwHsnJWrAzTUpy9AQdnU= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7 h1:R+5XKIJga2K9Dkj0/iQ6fD/MBGo02oxGGFTc512lK/Q= +github.com/aws/aws-sdk-go-v2/service/ecr v1.36.7/go.mod h1:fDPQV/6ONOQOjvtKhtypIy1wcGLcKYtoK/lvZ9fyDGQ= +github.com/aws/aws-sdk-go-v2/service/ecs v1.52.2 h1:LRM6z+wmXqAgCvuH36RR+Wf8SZZhvOVjt6f5r38V2II= +github.com/aws/aws-sdk-go-v2/service/ecs v1.52.2/go.mod h1:Ghi1OWUv4+VMEULWiHsKH2gNA3KAcMoLWsvU0eRXvIA= github.com/aws/aws-sdk-go-v2/service/eks v1.51.0 h1:BYyB+byjQ7oyupe3v+YjTp1yfmfNEwChYA2naCc85xI= github.com/aws/aws-sdk-go-v2/service/eks v1.51.0/go.mod h1:oaPCqTzAe8C5RQZJGRD4RENcV7A4n99uGxbD4rULbNg= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2 h1:4FMHqLfk0efmTqhXVRL5xYRqlEBNBiRI7N6w4jsEdd4= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.2/go.mod h1:LWoqeWlK9OZeJxsROW2RqrSPvQHKTpp69r/iDjwsSaw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5 h1:wtpJ4zcwrSbwhECWQoI/g6WM9zqCcSpHDJIWSbMLOu4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.5/go.mod h1:qu/W9HXQbbQ4+1+JcZp0ZNPV31ym537ZJN+fiS7Ti8E= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2 h1:t7iUP9+4wdc5lt3E41huP+GvQZJD38WLsgVp4iOtAjg= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.2/go.mod h1:/niFCtmuQNxqx9v8WAPq5qh7EH25U4BF6tjoyq9bObM= -github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0 h1:xA6XhTF7PE89BCNHJbQi8VvPzcgMtmGC5dr8S8N7lHk= -github.com/aws/aws-sdk-go-v2/service/s3 v1.66.0/go.mod h1:cB6oAuus7YXRZhWCc1wIwPywwZ1XwweNp2TVAEGYeB8= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2 h1:z6Pq4+jtKlhK4wWJGHRGwMLGjC1HZwAO3KJr/Na0tSU= github.com/aws/aws-sdk-go-v2/service/ssm v1.55.2/go.mod h1:DSmu/VZzpQlAubWBbAvNpt+S4k/XweglJi4XaDGyvQk= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6 h1:3zu537oLmsPfDMyjnUS2g+F2vITgy5pB74tHI+JBNoM= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.6/go.mod h1:WJSZH2ZvepM6t6jwu4w/Z45Eoi75lPN7DcydSRtJg6Y= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5 h1:K0OQAsDywb0ltlFrZm0JHPY3yZp/S9OaoLU33S7vPS8= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.5/go.mod h1:ORITg+fyuMoeiQFiVGoqB3OydVTLkClw/ljbblMq6Cc= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1 h1:6SZUVRQNvExYlMLbHdlKB48x0fLbc2iVROyaNEwBHbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.1/go.mod h1:GqWyYCwLXnlUB1lOAXQyNSPqPLQJvmo8J0DWBzp9mtg= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7 h1:rLnYAfXQ3YAccocshIH5mzNNwZBkBo+bP6EhIxak6Hw= +github.com/aws/aws-sdk-go-v2/service/sso v1.24.7/go.mod h1:ZHtuQJ6t9A/+YDuxOLnbryAmITtr8UysSny3qcyvJTc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6 h1:JnhTZR3PiYDNKlXy50/pNeix9aGMo6lLpXwJ1mw8MD4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.6/go.mod h1:URronUEGfXZN1VpdktPSD1EkAL9mfrV+2F4sjH38qOY= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2 h1:s4074ZO1Hk8qv65GqNXqDjmkf4HSQqJukaLuuW0TpDA= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.2/go.mod h1:mVggCnIWoM09jP71Wh+ea7+5gAp53q+49wDFs1SW5z8= github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492 h1:Ihams/fjKo4iWwM313ng2gCJWoetsL7ZQkXhOTmVUq4= github.com/aws/session-manager-plugin v0.0.0-20241119210807-82dc72922492/go.mod h1:7n17tunRPUsniNBu5Ja9C7WwJWTdOzaLqr/H0Ns3uuI= github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= @@ -421,16 +421,16 @@ github.com/pulumi/pulumi-azure-native-sdk/v2 v2.73.1 h1:yzXxwwq3tHdtSOi5vjKmKXq7 github.com/pulumi/pulumi-azure-native-sdk/v2 v2.73.1/go.mod h1:ChjIUNDNeN6jI33ZOivHUFqM6purDiLP01mghMGe1Fs= github.com/pulumi/pulumi-command/sdk v1.0.1 h1:ZuBSFT57nxg/fs8yBymUhKLkjJ6qmyN3gNvlY/idiN0= github.com/pulumi/pulumi-command/sdk v1.0.1/go.mod h1:C7sfdFbUIoXKoIASfXUbP/U9xnwPfxvz8dBpFodohlA= -github.com/pulumi/pulumi-docker/sdk/v4 v4.5.5 h1:7OjAfgLz5PAy95ynbgPAlWls5WBe4I/QW/61TdPWRlQ= -github.com/pulumi/pulumi-docker/sdk/v4 v4.5.5/go.mod h1:XZKLFXbw13olxuztlWnmVUPYZp2a+BqzqhuMl0j/Ow8= -github.com/pulumi/pulumi-eks/sdk/v3 v3.3.0 h1:F3xAOBZ/In4PqydTsKeg3tou/c5FZ+JTp5dQO0oMjqE= -github.com/pulumi/pulumi-eks/sdk/v3 v3.3.0/go.mod h1:QbAamxfUpDJC81BGtyEuV0P88RrdbOjQEhbgY+OOPpg= +github.com/pulumi/pulumi-docker/sdk/v4 v4.5.7 h1:cuIl5YyIghqtnFMGsdtPOeaNSix5S2CrqO0/UZ1Yjsc= +github.com/pulumi/pulumi-docker/sdk/v4 v4.5.7/go.mod h1:f2ek887nKRSwNtqTqCFENJSOH0PXm1b3FhzSXYL0IyM= +github.com/pulumi/pulumi-eks/sdk/v3 v3.4.0 h1:s2Cpu6E2lmADNUbutbJGm6O+O9j0mBLlrhQmc40ukt0= +github.com/pulumi/pulumi-eks/sdk/v3 v3.4.0/go.mod h1:QbAamxfUpDJC81BGtyEuV0P88RrdbOjQEhbgY+OOPpg= github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0 h1:21oSj+TKlKTzQcxN9Hik7iSNNHPUQXN4s3itOnahy/w= github.com/pulumi/pulumi-gcp/sdk/v7 v7.38.0/go.mod h1:YaEZms1NgXFqGhObKVofcAeWXu2V+3t/BAXdHQZq7fU= github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.3 h1:quqoGsLbF7lpGpGU4mi5WfVLIAo4gfvoQeYYmemx1Dg= github.com/pulumi/pulumi-kubernetes/sdk/v4 v4.18.3/go.mod h1:9dBA6+rtpKmyZB3k1XryUOHDOuNdoTODFKEEZZCtrz8= -github.com/pulumi/pulumi-libvirt/sdk v0.4.7 h1:/BBnqqx/Gbg2vINvJxXIVb58THXzw2lSqFqxlRSXH9M= -github.com/pulumi/pulumi-libvirt/sdk v0.4.7/go.mod h1:VKvjhAm1sGtzKZruYwIhgascabEx7+oVVRCoxp/cPi4= +github.com/pulumi/pulumi-libvirt/sdk v0.5.3 h1:CiUGTweLLIxbAbADxxnwPv4BK8pxXfU8urokJvK1ihM= +github.com/pulumi/pulumi-libvirt/sdk v0.5.3/go.mod h1:gAhyIZKtzs4rknrl8fu8BQnyqijAmViFbaUkyuHt4xY= github.com/pulumi/pulumi-random/sdk/v4 v4.16.7 h1:39rhOe/PTUGMYia8pR5T2wbxxMt2pwrlonf0ncYKSzE= github.com/pulumi/pulumi-random/sdk/v4 v4.16.7/go.mod h1:cxxDhJzUPt/YElfvlWa15Q4NGF6XXS8kUs4OQsCxSBk= github.com/pulumi/pulumi-tls/sdk/v4 v4.11.1 h1:tXemWrzeVTqG8zq6hBdv1TdPFXjgZ+dob63a/6GlF1o= diff --git a/test/new-e2e/tests/containers/k8s_test.go b/test/new-e2e/tests/containers/k8s_test.go index 872714c799525..84a65c936561e 100644 --- a/test/new-e2e/tests/containers/k8s_test.go +++ b/test/new-e2e/tests/containers/k8s_test.go @@ -469,7 +469,10 @@ func (suite *k8sSuite) TestNginx() { `^pod_name:nginx-[[:alnum:]]+-[[:alnum:]]+$`, `^pod_phase:running$`, `^short_image:apps-nginx-server$`, - `^email:team-container-platform@datadoghq.com$`, + `^domain:deployment$`, + `^mail:team-container-platform@datadoghq.com$`, + `^org:agent-org$`, + `^parent-name:nginx$`, `^team:contp$`, }, AcceptUnexpectedTags: true, @@ -544,7 +547,10 @@ func (suite *k8sSuite) TestNginx() { `^pod_name:nginx-[[:alnum:]]+-[[:alnum:]]+$`, `^pod_phase:running$`, `^short_image:apps-nginx-server$`, - `^email:team-container-platform@datadoghq.com$`, + `^domain:deployment$`, + `^mail:team-container-platform@datadoghq.com$`, + `^org:agent-org$`, + `^parent-name:nginx$`, `^team:contp$`, }, Message: `GET / HTTP/1\.1`, diff --git a/test/new-e2e/tests/orchestrator/apply.go b/test/new-e2e/tests/orchestrator/apply.go index ee4e8668ab40c..45b3d792102a7 100644 --- a/test/new-e2e/tests/orchestrator/apply.go +++ b/test/new-e2e/tests/orchestrator/apply.go @@ -18,6 +18,7 @@ import ( dogstatsdstandalone "github.com/DataDog/test-infra-definitions/components/datadog/dogstatsd-standalone" fakeintakeComp "github.com/DataDog/test-infra-definitions/components/datadog/fakeintake" localKubernetes "github.com/DataDog/test-infra-definitions/components/kubernetes" + "github.com/DataDog/test-infra-definitions/components/kubernetes/vpa" resAws "github.com/DataDog/test-infra-definitions/resources/aws" "github.com/DataDog/test-infra-definitions/scenarios/aws/ec2" "github.com/DataDog/test-infra-definitions/scenarios/aws/fakeintake" @@ -86,6 +87,12 @@ func createCluster(ctx *pulumi.Context) (*resAws.Environment, *localKubernetes.C if err != nil { return nil, nil, nil, err } + + // Deploy VPA CRD + if _, err := vpa.DeployCRD(&awsEnv, kindKubeProvider); err != nil { + return nil, nil, nil, err + } + return &awsEnv, kindCluster, kindKubeProvider, nil } From 2b44f9730152dbff11d2c2499775fd6701360f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Beauz=C3=A9e-Luyssen?= Date: Tue, 17 Dec 2024 17:00:46 +0100 Subject: [PATCH 75/78] omnibus: vcredist: move the actual file copy to the recipe (#32299) --- omnibus/config/software/python3.rb | 1 - omnibus/config/software/vc_redist_14.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/omnibus/config/software/python3.rb b/omnibus/config/software/python3.rb index 6e5636064bad2..2fe37a032fc24 100644 --- a/omnibus/config/software/python3.rb +++ b/omnibus/config/software/python3.rb @@ -81,7 +81,6 @@ license "Python-2.0" command "XCOPY /YEHIR *.* \"#{windows_safe_path(python_3_embedded)}\"" - command "copy /y \"#{windows_safe_path(vcrt140_root)}\\*.dll\" \"#{windows_safe_path(python_3_embedded)}\"" # Install pip python = "#{windows_safe_path(python_3_embedded)}\\python.exe" diff --git a/omnibus/config/software/vc_redist_14.rb b/omnibus/config/software/vc_redist_14.rb index ecc8067943f96..226a1145bb993 100644 --- a/omnibus/config/software/vc_redist_14.rb +++ b/omnibus/config/software/vc_redist_14.rb @@ -23,5 +23,5 @@ else source_msm = "Microsoft_VC141_CRT_x64.msm" end - command "powershell -C \"#{windows_safe_path(script_root)} -file #{source_msm} -targetDir .\\expanded\"" + command "powershell -C \"#{windows_safe_path(script_root)} -file #{source_msm} -targetDir \"#{windows_safe_path(python_3_embedded)}\"\"" end From f5794905d1fc472991da3edecf4d0957461df92a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9lian=20Raimbault?= <161456554+CelianR@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:07:13 -0500 Subject: [PATCH 76/78] Don't run notify gitlab ci changes on merge queue (#32293) --- .gitlab/notify/notify.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitlab/notify/notify.yml b/.gitlab/notify/notify.yml index f40187465a50f..13131a7fecb4a 100644 --- a/.gitlab/notify/notify.yml +++ b/.gitlab/notify/notify.yml @@ -80,8 +80,8 @@ notify_github: script: - !reference [.setup_agent_github_app] # Python 3.12 changes default behavior how packages are installed. - # In particular, --break-system-packages command line option is - # required to use the old behavior or use a virtual env. https://github.com/actions/runner-images/issues/8615 + # In particular, --break-system-packages command line option is + # required to use the old behavior or use a virtual env. https://github.com/actions/runner-images/issues/8615 - python3 -m pip install -r tasks/libs/requirements-github.txt --break-system-packages - messagefile="$(mktemp)" - echo "Use this command from [test-infra-definitions](https://github.com/DataDog/test-infra-definitions) to manually test this PR changes on a VM:" >> "$messagefile" @@ -98,6 +98,7 @@ notify_gitlab_ci_changes: needs: [compute_gitlab_ci_config] tags: ["arch:amd64"] rules: + - !reference [.except_mergequeue] - changes: paths: - .gitlab-ci.yml @@ -105,8 +106,8 @@ notify_gitlab_ci_changes: compare_to: main # TODO: use a variable, when this is supported https://gitlab.com/gitlab-org/gitlab/-/issues/369916 script: # Python 3.12 changes default behavior how packages are installed. - # In particular, --break-system-packages command line option is - # required to use the old behavior or use a virtual env. https://github.com/actions/runner-images/issues/8615 + # In particular, --break-system-packages command line option is + # required to use the old behavior or use a virtual env. https://github.com/actions/runner-images/issues/8615 - python3 -m pip install -r tasks/libs/requirements-github.txt --break-system-packages - !reference [.setup_agent_github_app] - inv -e notify.gitlab-ci-diff --from-diff artifacts/diff.gitlab-ci.yml --pr-comment From 5cde5c4d2dc3c143ff6efc6ef70a73fc6e248e9e Mon Sep 17 00:00:00 2001 From: Alexandre Menasria <47357713+amenasria@users.noreply.github.com> Date: Tue, 17 Dec 2024 17:24:10 +0100 Subject: [PATCH 77/78] [CI] Refactor the Agent integration tests (#31801) --- tasks/__init__.py | 2 +- tasks/agent.py | 88 ++++---------------- tasks/cluster_agent.py | 39 +++------ tasks/dogstatsd.py | 34 +++----- tasks/gointegrationtest.py | 161 +++++++++++++++++++++++++++++++++++++ tasks/gotest.py | 36 --------- tasks/trace_agent.py | 20 ++--- 7 files changed, 208 insertions(+), 172 deletions(-) create mode 100644 tasks/gointegrationtest.py diff --git a/tasks/__init__.py b/tasks/__init__.py index d59a06de7463b..0dffed23dacaf 100644 --- a/tasks/__init__.py +++ b/tasks/__init__.py @@ -87,13 +87,13 @@ tidy, tidy_all, ) +from tasks.gointegrationtest import integration_tests from tasks.gotest import ( check_otel_build, check_otel_module_versions, e2e_tests, get_impacted_packages, get_modified_packages, - integration_tests, lint_go, send_unit_tests_stats, test, diff --git a/tasks/agent.py b/tasks/agent.py index d4e5d13230885..19cd6170b96c6 100644 --- a/tasks/agent.py +++ b/tasks/agent.py @@ -17,6 +17,11 @@ from tasks.build_tags import add_fips_tags, filter_incompatible_tags, get_build_tags, get_default_build_tags from tasks.devcontainer import run_on_devcontainer from tasks.flavor import AgentFlavor +from tasks.gointegrationtest import ( + CORE_AGENT_LINUX_IT_CONF, + CORE_AGENT_WINDOWS_IT_CONF, + containerized_integration_tests, +) from tasks.libs.common.utils import ( REPO_PATH, bin_name, @@ -578,79 +583,18 @@ def integration_tests(ctx, race=False, remote_docker=False, go_mod="readonly", t """ Run integration tests for the Agent """ - if sys.platform == 'win32': - return _windows_integration_tests(ctx, race=race, go_mod=go_mod, timeout=timeout) - else: - # TODO: See if these will function on Windows - return _linux_integration_tests(ctx, race=race, remote_docker=remote_docker, go_mod=go_mod, timeout=timeout) - - -def _windows_integration_tests(ctx, race=False, go_mod="readonly", timeout=""): - test_args = { - "go_mod": go_mod, - "go_build_tags": " ".join(get_default_build_tags(build="test")), - "race_opt": "-race" if race else "", - "exec_opts": "", - "timeout_opt": f"-timeout {timeout}" if timeout else "", - } - - go_cmd = 'go test {timeout_opt} -mod={go_mod} {race_opt} -tags "{go_build_tags}" {exec_opts}'.format(**test_args) # noqa: FS002 - - tests = [ - { - # Run eventlog tests with the Windows API, which depend on the EventLog service - "dir": "./pkg/util/winutil/", - 'prefix': './eventlog/...', - 'extra_args': '-evtapi Windows', - }, - { - # Run eventlog tailer tests with the Windows API, which depend on the EventLog service - "dir": ".", - 'prefix': './pkg/logs/tailers/windowsevent/...', - 'extra_args': '-evtapi Windows', - }, - { - # Run eventlog check tests with the Windows API, which depend on the EventLog service - "dir": ".", - # Don't include submodules, since the `-evtapi` flag is not defined in them - 'prefix': './comp/checks/windowseventlog/windowseventlogimpl/check', - 'extra_args': '-evtapi Windows', - }, - ] - - for test in tests: - with ctx.cd(f"{test['dir']}"): - ctx.run(f"{go_cmd} {test['prefix']} {test['extra_args']}") - - -def _linux_integration_tests(ctx, race=False, remote_docker=False, go_mod="readonly", timeout=""): - test_args = { - "go_mod": go_mod, - "go_build_tags": " ".join(get_default_build_tags(build="test")), - "race_opt": "-race" if race else "", - "exec_opts": "", - "timeout_opt": f"-timeout {timeout}" if timeout else "", - } - - # since Go 1.13, the -exec flag of go test could add some parameters such as -test.timeout - # to the call, we don't want them because while calling invoke below, invoke - # thinks that the parameters are for it to interpret. - # we're calling an intermediate script which only pass the binary name to the invoke task. - if remote_docker: - test_args["exec_opts"] = f"-exec \"{os.getcwd()}/test/integration/dockerize_tests.sh\"" - - go_cmd = 'go test {timeout_opt} -mod={go_mod} {race_opt} -tags "{go_build_tags}" {exec_opts}'.format(**test_args) # noqa: FS002 - - prefixes = [ - "./test/integration/config_providers/...", - "./test/integration/corechecks/...", - "./test/integration/listeners/...", - "./test/integration/util/kubelet/...", - ] - - for prefix in prefixes: - ctx.run(f"{go_cmd} {prefix}") + return containerized_integration_tests( + ctx, CORE_AGENT_WINDOWS_IT_CONF, race=race, go_mod=go_mod, timeout=timeout + ) + return containerized_integration_tests( + ctx, + CORE_AGENT_LINUX_IT_CONF, + race=race, + remote_docker=remote_docker, + go_mod=go_mod, + timeout=timeout, + ) def check_supports_python_version(check_dir, python): diff --git a/tasks/cluster_agent.py b/tasks/cluster_agent.py index 8bbde5be51ece..b929270831a58 100644 --- a/tasks/cluster_agent.py +++ b/tasks/cluster_agent.py @@ -12,10 +12,10 @@ from invoke import task from invoke.exceptions import Exit -from tasks.build_tags import get_build_tags, get_default_build_tags +from tasks.build_tags import get_default_build_tags from tasks.cluster_agent_helpers import build_common, clean_common, refresh_assets_common, version_common from tasks.cws_instrumentation import BIN_PATH as CWS_INSTRUMENTATION_BIN_PATH -from tasks.libs.common.utils import TestsNotSupportedError +from tasks.gointegrationtest import CLUSTER_AGENT_IT_CONF, containerized_integration_tests from tasks.libs.releasing.version import load_release_versions # constants @@ -92,33 +92,14 @@ def integration_tests(ctx, race=False, remote_docker=False, go_mod="readonly", t """ Run integration tests for cluster-agent """ - if sys.platform == 'win32': - raise TestsNotSupportedError('Cluster Agent integration tests are not supported on Windows') - - # We need docker for the kubeapiserver integration tests - tags = get_default_build_tags(build="cluster-agent") + ["docker", "test"] - - go_build_tags = " ".join(get_build_tags(tags, [])) - race_opt = "-race" if race else "" - exec_opts = "" - timeout_opt = f"-timeout {timeout}" if timeout else "" - - # since Go 1.13, the -exec flag of go test could add some parameters such as -test.timeout - # to the call, we don't want them because while calling invoke below, invoke - # thinks that the parameters are for it to interpret. - # we're calling an intermediate script which only pass the binary name to the invoke task. - if remote_docker: - exec_opts = f"-exec \"{os.getcwd()}/test/integration/dockerize_tests.sh\"" - - go_cmd = f'go test {timeout_opt} -mod={go_mod} {race_opt} -tags "{go_build_tags}" {exec_opts}' - - prefixes = [ - "./test/integration/util/kube_apiserver", - "./test/integration/util/leaderelection", - ] - - for prefix in prefixes: - ctx.run(f"{go_cmd} {prefix}") + containerized_integration_tests( + ctx, + CLUSTER_AGENT_IT_CONF, + race=race, + remote_docker=remote_docker, + go_mod=go_mod, + timeout=timeout, + ) @task diff --git a/tasks/dogstatsd.py b/tasks/dogstatsd.py index 08dbe80a85993..311560a3d118e 100644 --- a/tasks/dogstatsd.py +++ b/tasks/dogstatsd.py @@ -11,7 +11,8 @@ 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, TestsNotSupportedError, bin_name, get_build_flags, get_root +from tasks.gointegrationtest import DOGSTATSD_IT_CONF, containerized_integration_tests +from tasks.libs.common.utils import REPO_PATH, bin_name, get_build_flags, get_root from tasks.windows_resources import build_messagetable, build_rc, versioninfo_vars # constants @@ -174,29 +175,14 @@ def integration_tests(ctx, race=False, remote_docker=False, go_mod="readonly", t """ Run integration tests for dogstatsd """ - if sys.platform == 'win32': - raise TestsNotSupportedError('DogStatsD integration tests are not supported on Windows') - - go_build_tags = " ".join(get_default_build_tags(build="test")) - race_opt = "-race" if race else "" - exec_opts = "" - timeout_opt = f"-timeout {timeout}" if timeout else "" - - # since Go 1.13, the -exec flag of go test could add some parameters such as -test.timeout - # to the call, we don't want them because while calling invoke below, invoke - # thinks that the parameters are for it to interpret. - # we're calling an intermediate script which only pass the binary name to the invoke task. - if remote_docker: - exec_opts = f"-exec \"{os.getcwd()}/test/integration/dockerize_tests.sh\"" - - go_cmd = f'go test {timeout_opt} -mod={go_mod} {race_opt} -tags "{go_build_tags}" {exec_opts}' - - prefixes = [ - "./test/integration/dogstatsd/...", - ] - - for prefix in prefixes: - ctx.run(f"{go_cmd} {prefix}") + containerized_integration_tests( + ctx, + DOGSTATSD_IT_CONF, + race=race, + remote_docker=remote_docker, + go_mod=go_mod, + timeout=timeout, + ) @task diff --git a/tasks/gointegrationtest.py b/tasks/gointegrationtest.py new file mode 100644 index 0000000000000..1d5cc56c8b9cf --- /dev/null +++ b/tasks/gointegrationtest.py @@ -0,0 +1,161 @@ +import os +import sys +import traceback +from dataclasses import dataclass + +from invoke import Context, task +from invoke.exceptions import Exit + +from tasks.build_tags import get_default_build_tags +from tasks.libs.common.utils import TestsNotSupportedError, gitlab_section + + +@dataclass +class IntegrationTest: + """ + Integration tests + """ + + prefix: str + dir: str = None + extra_args: str = None + + +@dataclass +class IntegrationTestsConfig: + """ + Integration tests configuration + """ + + name: str + go_build_tags: list[str] + tests: list[IntegrationTest] + env: dict[str, str] = None + is_windows_supported: bool = True + + +CORE_AGENT_LINUX_IT_CONF = IntegrationTestsConfig( + name="Core Agent Linux", + go_build_tags=get_default_build_tags(build="test"), + tests=[ + IntegrationTest(prefix="./test/integration/config_providers/..."), + IntegrationTest(prefix="./test/integration/corechecks/..."), + IntegrationTest(prefix="./test/integration/listeners/..."), + IntegrationTest(prefix="./test/integration/util/kubelet/..."), + ], + is_windows_supported=False, +) + +CORE_AGENT_WINDOWS_IT_CONF = IntegrationTestsConfig( + name="Core Agent Windows", + go_build_tags=get_default_build_tags(build="test"), + tests=[ + # Run eventlog tests with the Windows API, which depend on the EventLog service + IntegrationTest(dir="./pkg/util/winutil/", prefix="./eventlog/...", extra_args="-evtapi Windows"), + # Run eventlog tailer tests with the Windows API, which depend on the EventLog service + IntegrationTest(dir=".", prefix="./pkg/logs/tailers/windowsevent/...", extra_args="-evtapi Windows"), + # Run eventlog check tests with the Windows API, which depend on the EventLog service + # Don't include submodules, since the `-evtapi` flag is not defined in them + IntegrationTest( + dir=".", prefix="./comp/checks/windowseventlog/windowseventlogimpl/check", extra_args="-evtapi Windows" + ), + ], +) + +DOGSTATSD_IT_CONF = IntegrationTestsConfig( + name="DogStatsD", + go_build_tags=get_default_build_tags(build="test"), + tests=[IntegrationTest(prefix="./test/integration/dogstatsd/...")], + is_windows_supported=False, +) + +CLUSTER_AGENT_IT_CONF = IntegrationTestsConfig( + name="Cluster Agent", + go_build_tags=get_default_build_tags(build="cluster-agent") + ["docker", "test"], + tests=[ + IntegrationTest(prefix="./test/integration/util/kube_apiserver"), + IntegrationTest(prefix="./test/integration/util/leaderelection"), + ], + is_windows_supported=False, +) + +TRACE_AGENT_IT_CONF = IntegrationTestsConfig( + name="Trace Agent", + go_build_tags=get_default_build_tags(build="test"), + tests=[IntegrationTest(prefix="./cmd/trace-agent/test/testsuite/...")], + env={"INTEGRATION": "yes"}, + is_windows_supported=False, +) + + +def containerized_integration_tests( + ctx: Context, + integration_tests_config: IntegrationTestsConfig, + race=False, + remote_docker=False, + go_mod="readonly", + timeout="", +): + if sys.platform == 'win32' and not integration_tests_config.is_windows_supported: + raise TestsNotSupportedError(f'{integration_tests_config.name} integration tests are not supported on Windows') + test_args = { + "go_mod": go_mod, + "go_build_tags": " ".join(integration_tests_config.go_build_tags), + "race_opt": "-race" if race else "", + "exec_opts": "", + "timeout_opt": f"-timeout {timeout}" if timeout else "", + } + + # since Go 1.13, the -exec flag of go test could add some parameters such as -test.timeout + # to the call, we don't want them because while calling invoke below, invoke + # thinks that the parameters are for it to interpret. + # we're calling an intermediate script which only pass the binary name to the invoke task. + if remote_docker: + test_args["exec_opts"] = f"-exec \"{os.getcwd()}/test/integration/dockerize_tests.sh\"" + + go_cmd = 'go test {timeout_opt} -mod={go_mod} {race_opt} -tags "{go_build_tags}" {exec_opts}'.format(**test_args) # noqa: FS002 + + for it in integration_tests_config.tests: + if it.dir: + with ctx.cd(f"{it.dir}"): + ctx.run(f"{go_cmd} {it.prefix}", env=integration_tests_config.env) + else: + ctx.run(f"{go_cmd} {it.prefix}", env=integration_tests_config.env) + + +@task +def integration_tests(ctx, race=False, remote_docker=False, timeout=""): + """ + Run all the available integration tests + """ + core_agent_conf = CORE_AGENT_WINDOWS_IT_CONF if sys.platform == 'win32' else CORE_AGENT_LINUX_IT_CONF + tests = { + "Agent Core": lambda: containerized_integration_tests( + ctx, core_agent_conf, race=race, remote_docker=remote_docker, timeout=timeout + ), + "DogStatsD": lambda: containerized_integration_tests( + ctx, DOGSTATSD_IT_CONF, race=race, remote_docker=remote_docker, timeout=timeout + ), + "Cluster Agent": lambda: containerized_integration_tests( + ctx, CLUSTER_AGENT_IT_CONF, race=race, remote_docker=remote_docker, timeout=timeout + ), + "Trace Agent": lambda: containerized_integration_tests(ctx, TRACE_AGENT_IT_CONF, race=race, timeout=timeout), + } + tests_failures = {} + for t_name, t in tests.items(): + with gitlab_section(f"Running the {t_name} integration tests", collapsed=True, echo=True): + try: + t() + except TestsNotSupportedError as e: + print(f"Skipping {t_name}: {e}") + except Exception: + # Keep printing the traceback not to have to wait until all tests are done to see what failed + traceback.print_exc() + # Storing the traceback to print it at the end without directly raising the exception + tests_failures[t_name] = traceback.format_exc() + if tests_failures: + print("The following integration tests failed:") + for t_name in tests_failures: + print(f"- {t_name}") + print("See the above logs to get the full traceback.") + raise Exit(code=1) diff --git a/tasks/gotest.py b/tasks/gotest.py index 7ca411e7521ea..4aa9a4b3e151f 100644 --- a/tasks/gotest.py +++ b/tasks/gotest.py @@ -11,7 +11,6 @@ import os import re import sys -import traceback from collections import defaultdict from collections.abc import Iterable from datetime import datetime @@ -22,13 +21,10 @@ from invoke.context import Context from invoke.exceptions import Exit -from tasks.agent import integration_tests as agent_integration_tests from tasks.build_tags import compute_build_tags_for_flavor -from tasks.cluster_agent import integration_tests as dca_integration_tests from tasks.collector import OCB_VERSION from tasks.coverage import PROFILE_COV, CodecovWorkaround from tasks.devcontainer import run_on_devcontainer -from tasks.dogstatsd import integration_tests as dsd_integration_tests from tasks.flavor import AgentFlavor from tasks.libs.common.color import color_message from tasks.libs.common.datadog_api import create_count, send_metrics @@ -36,7 +32,6 @@ from tasks.libs.common.gomodules import get_default_modules from tasks.libs.common.junit_upload_core import enrich_junitxml, produce_junit_tar from tasks.libs.common.utils import ( - TestsNotSupportedError, clean_nested_paths, get_build_flags, gitlab_section, @@ -46,7 +41,6 @@ from tasks.modules import GoModule, get_module_by_path from tasks.test_core import ModuleTestResult, process_input_args, process_module_results, test_core from tasks.testwasher import TestWasher -from tasks.trace_agent import integration_tests as trace_integration_tests from tasks.update_go import PATTERN_MAJOR_MINOR_BUGFIX GO_TEST_RESULT_TMP_JSON = 'module_test_output.json' @@ -405,36 +399,6 @@ def test( print(f"Tests final status (including re-runs): {color_message('ALL TESTS PASSED', 'green')}") -@task -def integration_tests(ctx, race=False, remote_docker=False, timeout=""): - """ - Run all the available integration tests - """ - tests = { - "Agent": lambda: agent_integration_tests(ctx, race=race, remote_docker=remote_docker, timeout=timeout), - "DogStatsD": lambda: dsd_integration_tests(ctx, race=race, remote_docker=remote_docker, timeout=timeout), - "Cluster Agent": lambda: dca_integration_tests(ctx, race=race, remote_docker=remote_docker, timeout=timeout), - "Trace Agent": lambda: trace_integration_tests(ctx, race=race, timeout=timeout), - } - tests_failures = {} - for t_name, t in tests.items(): - with gitlab_section(f"Running the {t_name} integration tests", collapsed=True, echo=True): - try: - t() - except TestsNotSupportedError as e: - print(f"Skipping {t_name}: {e}") - except Exception: - # Keep printing the traceback not to have to wait until all tests are done to see what failed - traceback.print_exc() - # Storing the traceback to print it at the end without directly raising the exception - tests_failures[t_name] = traceback.format_exc() - if tests_failures: - print("Integration tests failed:") - for t_name, t_failure in tests_failures.items(): - print(f"{t_name}:\n{t_failure}") - raise Exit(code=1) - - @task def e2e_tests(ctx, target="gitlab", agent_image="", dca_image="", argo_workflow="default"): """ diff --git a/tasks/trace_agent.py b/tasks/trace_agent.py index d65ae6861fcd8..0ec1dd1956aa8 100644 --- a/tasks/trace_agent.py +++ b/tasks/trace_agent.py @@ -5,7 +5,8 @@ from tasks.build_tags import add_fips_tags, filter_incompatible_tags, get_build_tags, get_default_build_tags from tasks.flavor import AgentFlavor -from tasks.libs.common.utils import REPO_PATH, TestsNotSupportedError, bin_name, get_build_flags +from tasks.gointegrationtest import TRACE_AGENT_IT_CONF, containerized_integration_tests +from tasks.libs.common.utils import REPO_PATH, bin_name, get_build_flags from tasks.windows_resources import build_messagetable, build_rc, versioninfo_vars BIN_PATH = os.path.join(".", "bin", "trace-agent") @@ -81,15 +82,14 @@ def integration_tests(ctx, race=False, go_mod="readonly", timeout="10m"): """ Run integration tests for trace agent """ - if sys.platform == 'win32': - raise TestsNotSupportedError('Trace Agent integration tests are not supported on Windows') - - go_build_tags = " ".join(get_default_build_tags(build="test")) - race_opt = "-race" if race else "" - timeout_opt = f"-timeout {timeout}" if timeout else "" - - go_cmd = f'go test {timeout_opt} -mod={go_mod} {race_opt} -v -tags "{go_build_tags}"' - ctx.run(f"{go_cmd} ./cmd/trace-agent/test/testsuite/...", env={"INTEGRATION": "yes"}) + containerized_integration_tests( + ctx, + TRACE_AGENT_IT_CONF, + race=race, + remote_docker=False, + go_mod=go_mod, + timeout=timeout, + ) @task From 381a3cb19124c62dccb064ea35c326b26bc3077f Mon Sep 17 00:00:00 2001 From: Kevin Gosse Date: Tue, 17 Dec 2024 17:37:35 +0100 Subject: [PATCH 78/78] Capture additional entries from the event log (#31559) Co-authored-by: Ursula Chen <58821586+urseberry@users.noreply.github.com> --- pkg/flare/archive_win.go | 2 +- .../notes/add-events-to-flare-b10cb7bf1aaf4d50.yaml | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-events-to-flare-b10cb7bf1aaf4d50.yaml diff --git a/pkg/flare/archive_win.go b/pkg/flare/archive_win.go index e62f23d58a8dc..96afe5ac17a94 100644 --- a/pkg/flare/archive_win.go +++ b/pkg/flare/archive_win.go @@ -31,7 +31,7 @@ var ( eventLogChannelsToExport = map[string]string{ "System": "Event/System/Provider[@Name=\"Service Control Manager\"]", - "Application": "Event/System/Provider[@Name=\"datadog-trace-agent\" or @Name=\"DatadogAgent\"]", + "Application": "Event/System/Provider[@Name=\"datadog-trace-agent\" or @Name=\"DatadogAgent\" or @Name=\".NET Runtime\" or @Name=\"Application Error\"]", "Microsoft-Windows-WMI-Activity/Operational": "*", } execTimeout = 30 * time.Second diff --git a/releasenotes/notes/add-events-to-flare-b10cb7bf1aaf4d50.yaml b/releasenotes/notes/add-events-to-flare-b10cb7bf1aaf4d50.yaml new file mode 100644 index 0000000000000..9bff569f5f774 --- /dev/null +++ b/releasenotes/notes/add-events-to-flare-b10cb7bf1aaf4d50.yaml @@ -0,0 +1,11 @@ +# Each section from every release note are combined when the +# CHANGELOG.rst is rendered. So the text needs to be worded so that +# it does not depend on any information only available in another +# section. This may mean repeating some details, but each section +# must be readable independently of the other. +# +# Each section note must be formatted as reStructuredText. +--- +enhancements: + - | + On Windows, Agent flares now include event logs for .NET applications.