diff --git a/conftest.py b/conftest.py index f4bc7851a03..5f6c3d0d608 100644 --- a/conftest.py +++ b/conftest.py @@ -425,6 +425,13 @@ def pytest_collection_finish(session: pytest.Session) -> None: if session.config.option.replay: setup_properties.load(context.scenario.host_log_folder) + if not session.items: + if not session.config.option.replay: + setup_properties.dump(context.scenario.host_log_folder) + return + + context.scenario.execute_post_collection_warmups() + last_item_file = "" for item in session.items: if _item_is_skipped(item): diff --git a/pyproject.toml b/pyproject.toml index e3fa02ba142..546bf39c3ac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -220,6 +220,8 @@ ignore = [ ] "tests/test_the_test/*" = [ "S605", # start-process-with-a-shell: test the test needs to run shell commands + "ANN001", # missing-type-function-argument: stubs of internal interfaces + "SLF001", # private-member-access: tests of warmup ordering need to inspect privates ] "utils/_context/_scenarios/auto_injection.py" = [ "PLC0415", # import-outside-top-level TODO diff --git a/tests/test_the_test/test_collection_warmups.py b/tests/test_the_test/test_collection_warmups.py new file mode 100644 index 00000000000..f529b57a79e --- /dev/null +++ b/tests/test_the_test/test_collection_warmups.py @@ -0,0 +1,137 @@ +"""Tests for post-collection warmup ordering (ADR-002).""" + +from contextlib import contextmanager +from threading import RLock +from unittest.mock import MagicMock, patch + +import pytest + +from utils import interfaces, scenarios +from utils._context._scenarios.core import Scenario +from utils._context._scenarios.endtoend import EndToEndScenario +from utils._context.containers import ProxyContainer, TestedContainer + + +def _stub_configure(self, *, host_log_folder: str, replay: bool) -> None: # noqa: ARG001 + """Stand-in for *Container.configure that skips Docker and file I/O.""" + self.host_log_folder = host_log_folder + self._starting_lock = RLock() + + +@contextmanager +def _configured_scenario(*, library_version: str | None, agent_version: str | None): + """Yield a configured EndToEndScenario with all Docker / interface I/O patched out. + + Image labels drive label-based version detection in container.configure(). + """ + scenario = EndToEndScenario("FAKE_E2E", doc="test") + + scenario.weblog_container.image.labels = { + "system-tests-library": "python", + "system-tests-weblog-variant": "flask", + } + if library_version: + scenario.weblog_container.image.labels["system-tests-library-version"] = library_version + scenario.weblog_container.image.env = {} + + scenario.agent_container.image.labels = {} + if agent_version: + scenario.agent_container.image.labels["org.opencontainers.image.version"] = agent_version + + cfg = MagicMock() + cfg.option.replay = False + + with ( + patch("utils._context._scenarios.endtoend.get_docker_client") as mock_dc, + patch.object(TestedContainer, "configure", _stub_configure), + patch.object(ProxyContainer, "configure", _stub_configure), + patch.object(interfaces.agent, "configure"), + patch.object(interfaces.library, "configure"), + patch.object(interfaces.backend, "configure"), + patch.object(interfaces.library_dotnet_managed, "configure"), + patch.object(interfaces.library_stdout, "configure"), + patch.object(interfaces.agent_stdout, "configure"), + ): + mock_dc.return_value.info.return_value = {"CgroupVersion": "2"} + scenario.configure(cfg) + try: + yield scenario + finally: + # Pop from the global scenario group registry to avoid polluting other tests. + for group in scenario.scenario_groups: + if scenario in group.scenarios: + group.scenarios.remove(scenario) + + +@scenarios.test_the_test +class Test_WarmupOrdering: + """Warmup list invariants after EndToEndScenario.configure().""" + + def test_defer_path_container_startup_not_in_warmups(self): + with _configured_scenario(library_version="1.2.3", agent_version="7.50.0") as s: + for fn in (s._create_network, s._start_containers): + assert fn not in s.warmups + for c in s._containers: + assert c.post_start not in s.warmups + + def test_defer_path_post_collection_order(self): + with _configured_scenario(library_version="1.2.3", agent_version="7.50.0") as s: + pcw = s.post_collection_warmups + idx_net = pcw.index(s._create_network) + idx_wdg = pcw.index(s._start_interfaces_watchdog) + idx_start = pcw.index(s._start_containers) + idx_readiness = pcw.index(s._wait_for_app_readiness) + + assert idx_net < idx_wdg < idx_start < idx_readiness + for c in s._containers: + idx_ps = pcw.index(c.post_start) + assert idx_start < idx_ps < idx_readiness + + def test_defer_path_weblog_system_info_before_readiness(self): + with _configured_scenario(library_version="1.2.3", agent_version="7.50.0") as s: + pcw = s.post_collection_warmups + assert pcw.index(s._get_weblog_system_info) < pcw.index(s._wait_for_app_readiness) + + def test_fallback_path_watchdog_after_network(self): + """Library version from label only: watchdog must follow network creation.""" + with _configured_scenario(library_version="1.2.3", agent_version=None) as s: + assert s._create_network in s.warmups + assert s._start_containers in s.warmups + assert s.warmups.index(s._create_network) < s.warmups.index(s._start_interfaces_watchdog) + assert s.warmups.index(s._start_interfaces_watchdog) < s.warmups.index(s._start_containers) + + def test_legacy_path_watchdog_after_network(self): + with _configured_scenario(library_version=None, agent_version=None) as s: + assert s.warmups.index(s._create_network) < s.warmups.index(s._start_interfaces_watchdog) + assert s.warmups.index(s._start_interfaces_watchdog) < s.warmups.index(s._start_containers) + + +@scenarios.test_the_test +class Test_ExecutePostCollectionWarmups: + def test_all_callables_are_invoked(self): + calls: list[int] = [] + scenario = Scenario("FAKE", doc="", github_workflow="testthetest") + scenario.post_collection_warmups = [lambda i=i: calls.append(i) for i in range(3)] + + scenario.execute_post_collection_warmups() + + assert calls == [0, 1, 2] + + def test_error_calls_close_targets_and_reraises(self): + closed: list[bool] = [] + + class FakeScenario(Scenario): + def close_targets(self): + closed.append(True) + + scenario = FakeScenario("FAKE2", doc="", github_workflow="testthetest") + + def boom(): + raise RuntimeError("boom") + + scenario.post_collection_warmups = [boom] + + with pytest.raises(RuntimeError, match="boom"): + scenario.execute_post_collection_warmups() + + assert closed == [True] diff --git a/tests/test_the_test/test_decorators.py b/tests/test_the_test/test_decorators.py index 4c5e200ef8f..8f2ecb8d466 100644 --- a/tests/test_the_test/test_decorators.py +++ b/tests/test_the_test/test_decorators.py @@ -27,7 +27,7 @@ def is_skipped(item: Any, reason: str): # noqa: ANN401 raise Exception(f"{item} is not skipped, or not with the good reason") -def is_not_skipped(item): # noqa: ANN001 +def is_not_skipped(item): if hasattr(item, "pytestmark"): for mark in item.pytestmark: if mark.name == ("skip", "xfail"): diff --git a/tests/test_the_test/test_docker_scenario.py b/tests/test_the_test/test_docker_scenario.py index ef3241b9cb9..c0d16e9475e 100644 --- a/tests/test_the_test/test_docker_scenario.py +++ b/tests/test_the_test/test_docker_scenario.py @@ -14,7 +14,7 @@ def __init__(self, name: str, events: list | None = None) -> None: def configure(self, *, host_log_folder: str, replay: bool): # noqa: ARG002 self._starting_lock = RLock() - def start(self, network): # noqa: ARG002, ANN001 + def start(self, network): # noqa: ARG002 self._test_events.append(f"start {self.name}") self.healthy = True diff --git a/utils/_context/_scenarios/core.py b/utils/_context/_scenarios/core.py index 799fc47b74f..fef4959e0ed 100644 --- a/utils/_context/_scenarios/core.py +++ b/utils/_context/_scenarios/core.py @@ -2,12 +2,12 @@ import os from pathlib import Path import shutil +from collections.abc import Callable from typing import TYPE_CHECKING if TYPE_CHECKING: from utils._context.component_version import Version - from collections.abc import Callable import pytest from utils._logger import logger, get_log_formatter @@ -128,6 +128,7 @@ def __init__( group.scenarios.append(self) self.warmups: list[Callable] = [] + self.post_collection_warmups: list[Callable] = [] self.collect_only: bool = False def _create_log_subfolder(self, subfolder: str, *, remove_if_exists: bool = False): @@ -187,10 +188,15 @@ def pytest_sessionstart(self, session: pytest.Session): # noqa: ARG002 """Called at the very begining of the process""" logger.terminal.write_sep("=", "test context", bold=True) + self._run_warmups(self.warmups, label="") + def execute_post_collection_warmups(self): + self._run_warmups(self.post_collection_warmups, label="post-collection ") + + def _run_warmups(self, warmups: list[Callable], *, label: str): try: - for warmup in self.warmups: - logger.info(f"Executing warmup {warmup}") + for warmup in warmups: + logger.info(f"Executing {label}warmup {warmup}") warmup() except: self.close_targets() diff --git a/utils/_context/_scenarios/debugger.py b/utils/_context/_scenarios/debugger.py index ce7eb51c7c7..ceffd5be5e9 100644 --- a/utils/_context/_scenarios/debugger.py +++ b/utils/_context/_scenarios/debugger.py @@ -76,7 +76,10 @@ def configure(self, config: pytest.Config): self.agent_container.environment["DD_AGENT_HOST"] = weblog_env["DD_AGENT_HOST"] if not self.replay: - self.warmups.append(self._wait_for_agent_debugging) + target = ( + self.post_collection_warmups if self._start_containers in self.post_collection_warmups else self.warmups + ) + target.append(self._wait_for_agent_debugging) def _wait_for_agent_debugging(self) -> None: logger.stdout("Wait for /debugger/v1/diagnostics endpoint on agent") diff --git a/utils/_context/_scenarios/endtoend.py b/utils/_context/_scenarios/endtoend.py index 718c4589541..b108155fabc 100644 --- a/utils/_context/_scenarios/endtoend.py +++ b/utils/_context/_scenarios/endtoend.py @@ -1,5 +1,7 @@ from typing import Literal import os +from collections.abc import Callable # noqa: TC003 (used at runtime in annotation) + import pytest from docker.models.networks import Network @@ -93,14 +95,19 @@ def configure(self, config: pytest.Config): # noqa: ARG002 if not self.replay: docker_info = get_docker_client().info() self.components["docker.Cgroup"] = docker_info.get("CgroupVersion", None) - self.warmups.append(self._create_network) - self.warmups.append(self._start_containers) for container in reversed(self._containers): container.configure(host_log_folder=self.host_log_folder, replay=self.replay) - for container in self._containers: - self.warmups.append(container.post_start) + self._container_warmups: list[Callable] = [ + lambda: logger.stdout("Starting containers..."), + self._create_network, + self._start_containers, + *(c.post_start for c in self._containers), + ] + + if not self.replay: + self.warmups.extend(self._container_warmups) def get_container_by_dd_integration_name(self, name: str): for container in self._containers: @@ -157,7 +164,6 @@ def _create_network(self) -> None: self._network = get_docker_client().networks.create(name, check_duplicate=True) def _start_containers(self): - logger.stdout("Starting containers...") threads = [] for container in self._containers: @@ -338,12 +344,41 @@ def configure(self, config: pytest.Config): else: self.library_interface_timeout = self._library_interface_timeout - if not self.replay: - self.warmups.insert(1, self._start_interfaces_watchdog) + # Resolve component versions (from image labels when available, else as a warmup). + library_known = self.weblog_container._library is not None # noqa: SLF001 + agent_known = self.agent_container.agent_version is not None + + if library_known: + self._set_library_component() + else: + self.warmups.append(self._set_library_component) + self.warmups.append(self._log_weblog_info) + + if agent_known: + self._set_agent_component() + else: + self.warmups.append(self._set_agent_component) + self.warmups.append(self._log_agent_info) + + if self.replay: + return + + self.post_collection_warmups.append(self._wait_for_app_readiness) + self.post_collection_warmups.append(self._set_weblog_domain) + + if library_known and agent_known: + # Both versions known upfront: defer container startup to post-collection so an empty + # test session skips Docker entirely. Watchdog must run after network/before containers. + for step in self._container_warmups: + self.warmups.remove(step) + steps = list(self._container_warmups) + steps.insert(2, self._start_interfaces_watchdog) + steps.append(self._get_weblog_system_info) + self.post_collection_warmups[0:0] = steps + else: + net_idx = self.warmups.index(self._create_network) + self.warmups.insert(net_idx + 1, self._start_interfaces_watchdog) self.warmups.append(self._get_weblog_system_info) - self.warmups.append(self._wait_for_app_readiness) - self.warmups.append(self._set_weblog_domain) - self.warmups.append(self._set_components) def _set_containers_dependancies(self) -> None: if self._use_proxy_for_agent: @@ -389,11 +424,25 @@ def _set_weblog_domain(self): if self.enable_ipv6: self.weblog_container.set_weblog_domain_for_ipv6(self._network) - def _set_components(self): - self.components["agent"] = self.agent_version + def _log_agent_info(self): + logger.stdout(f"Agent: {self.agent_container.agent_version}") + logger.stdout(f"Backend: {self.agent_container.dd_site}") + + def _log_weblog_info(self): + logger.stdout(f"Library: {self.library}") + if self.weblog_container.appsec_rules_file: + logger.stdout("Using a custom appsec rules file") + if self.weblog_container.uds_mode: + logger.stdout(f"UDS socket: {self.weblog_container.uds_socket}") + logger.stdout(f"Weblog variant: {self.weblog_variant}") + + def _set_library_component(self): self.components["library"] = self.library.version self.components[self.library.name] = self.library.version + def _set_agent_component(self): + self.components["agent"] = self.agent_version + def _wait_for_app_readiness(self): if self._use_proxy_for_weblog: logger.debug("Wait for app readiness") diff --git a/utils/_context/_scenarios/go_proxies.py b/utils/_context/_scenarios/go_proxies.py index 4df5f66d5f9..fff39c35cff 100644 --- a/utils/_context/_scenarios/go_proxies.py +++ b/utils/_context/_scenarios/go_proxies.py @@ -108,7 +108,7 @@ def _wait_for_app_readiness(self) -> None: logger.debug("Agent ready") def _set_components(self) -> None: - self.components["agent"] = self._agent_container.agent_version + self.components["agent"] = self._agent_container.agent_version or Version("0.0.0") lib = self.library self.components["library"] = lib.version self.components[lib.name] = lib.version diff --git a/utils/_context/containers.py b/utils/_context/containers.py index e21bf2ca686..96d6cac81cb 100644 --- a/utils/_context/containers.py +++ b/utils/_context/containers.py @@ -746,7 +746,7 @@ def post_start(self): class AgentContainer(TestedContainer): apm_receiver_port: int = 8127 dogstatsd_port: int = 8125 - agent_version: Version + agent_version: Version | None = None def __init__( self, @@ -802,15 +802,21 @@ def __init__( }, ) + def configure(self, *, host_log_folder: str, replay: bool): + super().configure(host_log_folder=host_log_folder, replay=replay) + version_str = self.image.labels.get("org.opencontainers.image.version") + if version_str: + self.agent_version = ComponentVersion("agent", version_str).version + def post_start(self): + if self.agent_version is not None: + return + with open(self.healthcheck_log_file, encoding="utf-8") as f: data = json.load(f) self.agent_version = ComponentVersion("agent", data["version"]).version - logger.stdout(f"Agent: {self.agent_version}") - logger.stdout(f"Backend: {self.dd_site}") - @property def dd_site(self): return os.environ.get("DD_SITE", "datad0g.com") @@ -1008,6 +1014,25 @@ def configure(self, *, host_log_folder: str, replay: bool): library = self.image.labels["system-tests-library"] + version_from_label = self.image.labels.get("system-tests-library-version") + if version_from_label: + self._library = ComponentVersion(library, version_from_label) + elif replay: + # In replay mode, post_start is never called (containers do not start). + # Load the library version from the saved healthcheck log so that + # init_patterns() below can install the library-specific log-filtering + # rules (e.g. PHP env-var skip patterns that prevent SOME_SECRET_ENV leaks). + try: + with open(self.healthcheck_log_file, encoding="utf-8") as f: + data = json.load(f) + lib = data["library"] + self._library = ComponentVersion(lib["name"], lib["version"]) + except Exception as e: + logger.warning(f"Could not load library version in configure (replay): {e}") + + if self._library is not None: + self.stdout_interface.init_patterns(self._library) + header_tags = "" if library in ("cpp_nginx", "cpp_httpd", "dotnet", "java", "python"): header_tags = "user-agent:http.request.headers.user-agent" @@ -1112,27 +1137,21 @@ def set_weblog_domain_for_ipv6(self, network: Network): def post_start(self): logger.debug(f"Docker host is {weblog.domain}") - with open(self.healthcheck_log_file, encoding="utf-8") as f: - data = json.load(f) - lib = data["library"] - - self._library = ComponentVersion(lib["name"], lib["version"]) + if self._library is None: + with open(self.healthcheck_log_file, encoding="utf-8") as f: + data = json.load(f) + lib = data["library"] - logger.stdout(f"Library: {self.library}") + self._library = ComponentVersion(lib["name"], lib["version"]) + logger.warning( + "Library version from healthcheck — add system-tests-library-version label to speed up startup" + ) if self._container is not None: exit_code, output = self.exec_run("cat /binaries/metadata.txt") if exit_code == 0 and output: logger.stdout(f"Library metadata:\n{output.decode('utf-8', errors='replace').strip()}") - if self.appsec_rules_file: - logger.stdout("Using a custom appsec rules file") - - if self.uds_mode: - logger.stdout(f"UDS socket: {self.uds_socket}") - - logger.stdout(f"Weblog variant: {self.weblog_variant}") - self.stdout_interface.init_patterns(self.library) @property @@ -1208,6 +1227,20 @@ def __init__( # Remove port bindings, as only the LambdaProxyContainer needs to expose a server self.ports = {} + def configure(self, *, host_log_folder: str, replay: bool): + super().configure(host_log_folder=host_log_folder, replay=replay) + + if replay and self._library is None: + # In replay mode, containers do not start and post_start is never called. + # Load the library version from the healthcheck log saved during the previous run. + try: + with open(self.healthcheck_log_file, encoding="utf-8") as f: + data = json.load(f) + lib = data["library"] + self._library = ComponentVersion(lib["name"], lib["version"]) + except Exception as e: + logger.warning(f"Could not load library version from healthcheck log in replay mode: {e}") + class PostgresContainer(SqlDbTestedContainer): def __init__(self) -> None: @@ -1679,6 +1712,17 @@ def __init__( }, ) + def configure(self, *, host_log_folder: str, replay: bool): + super().configure(host_log_folder=host_log_folder, replay=replay) + if replay: + try: + with open(self.healthcheck_log_file, encoding="utf-8") as f: + data = json.load(f) + lib = data["library"] + self.library = ComponentVersion("envoy", lib["version"]) + except Exception as e: + logger.warning(f"Could not load library version from healthcheck log in replay mode: {e}") + def post_start(self): with open(self.healthcheck_log_file, encoding="utf-8") as f: data = json.load(f) @@ -1755,6 +1799,17 @@ def __init__( }, ) + def configure(self, *, host_log_folder: str, replay: bool): + super().configure(host_log_folder=host_log_folder, replay=replay) + if replay: + try: + with open(self.healthcheck_log_file, encoding="utf-8") as f: + data = json.load(f) + lib = data["library"] + self.library = ComponentVersion("haproxy", lib["version"]) + except Exception as e: + logger.warning(f"Could not load library version from healthcheck log in replay mode: {e}") + def post_start(self): with open(self.healthcheck_log_file, encoding="utf-8") as f: data = json.load(f) diff --git a/utils/build/build.sh b/utils/build/build.sh index 159dc3c1e9a..79dff8895eb 100755 --- a/utils/build/build.sh +++ b/utils/build/build.sh @@ -303,6 +303,17 @@ build() { DOCKERFILE=utils/build/docker/${TEST_LIBRARY}/${WEBLOG_VARIANT}.Dockerfile + # Pre-build the .NET assembly-version tool image so both poc/uds Dockerfiles can + # COPY --from=system_tests/dotnet-version-tool without each duplicating the stage. + if [[ $TEST_LIBRARY == dotnet ]]; then + run_build_command docker buildx build \ + --load \ + ${DOCKER_PLATFORM_ARGS} \ + -f utils/build/docker/dotnet/build-helpers/version-tool.Dockerfile \ + -t system_tests/dotnet-version-tool \ + . + fi + GITHUB_TOKEN_SECRET_ARG="" if [ -n "${GITHUB_TOKEN_FILE:-}" ]; then @@ -341,6 +352,18 @@ build() { $EXTRA_DOCKER_ARGS \ . + # Read library version baked into the image by install_ddtrace.sh and re-tag + # with a system-tests-library-version label so the scenario can skip the + # post-start healthcheck round-trip when no tests are selected. + CID=$(docker create system_tests/weblog) + LIBRARY_VERSION=$(docker cp "${CID}:/system-tests-library-version" - 2>/dev/null | tar -xO 2>/dev/null | tr -d '[:space:]' || true) + docker rm "${CID}" > /dev/null + if [ -n "${LIBRARY_VERSION}" ]; then + docker build \ + --label "system-tests-library-version=${LIBRARY_VERSION}" \ + -t system_tests/weblog - <<< "FROM system_tests/weblog" + fi + if test -f "binaries/waf_rule_set.json"; then run_build_command docker buildx build \ diff --git a/utils/build/docker/cpp_httpd/install_ddtrace.sh b/utils/build/docker/cpp_httpd/install_ddtrace.sh index dece759f857..cf1c8d67026 100755 --- a/utils/build/docker/cpp_httpd/install_ddtrace.sh +++ b/utils/build/docker/cpp_httpd/install_ddtrace.sh @@ -25,5 +25,6 @@ fi echo '{"status": "ok", "library": {"name": "cpp_httpd", "version": "'"$HTTPD_DATADOG_VERSION"'"}}' > /app/healthcheck.json echo "$HTTPD_DATADOG_VERSION" > SYSTEM_TESTS_LIBRARY_VERSION +echo "${HTTPD_DATADOG_VERSION#v}" > /system-tests-library-version cat /app/healthcheck.json diff --git a/utils/build/docker/cpp_kong/install_ddtrace.sh b/utils/build/docker/cpp_kong/install_ddtrace.sh index 11ef895c154..1ce4c60dcb1 100755 --- a/utils/build/docker/cpp_kong/install_ddtrace.sh +++ b/utils/build/docker/cpp_kong/install_ddtrace.sh @@ -122,6 +122,7 @@ if [ "$KONG_IS_RELEASE" = "false" ]; then fi echo "${PLUGIN_VERSION}" > /builds/SYSTEM_TESTS_LIBRARY_VERSION +echo "${PLUGIN_VERSION}" > /system-tests-library-version printf '{"status":"ok","library":{"name":"cpp_kong","version":"%s"}}' \ "$PLUGIN_VERSION" > /builds/healthcheck.json diff --git a/utils/build/docker/cpp_nginx/install_ddtrace.sh b/utils/build/docker/cpp_nginx/install_ddtrace.sh index 078afb5c30a..a1aa443a1ab 100755 --- a/utils/build/docker/cpp_nginx/install_ddtrace.sh +++ b/utils/build/docker/cpp_nginx/install_ddtrace.sh @@ -28,6 +28,7 @@ function epilogue { echo "DataDog/nginx-datadog version: ${module_version}" echo "$module_version" | tr -d v > SYSTEM_TESTS_LIBRARY_VERSION + echo "$module_version" | tr -d v > /system-tests-library-version rm -f /etc/nginx/nginx.conf if version_first_is_greater "$module_version" "v1.1.0"; then diff --git a/utils/build/docker/dotnet/build-helpers/version-tool.Dockerfile b/utils/build/docker/dotnet/build-helpers/version-tool.Dockerfile new file mode 100644 index 00000000000..13720500fb3 --- /dev/null +++ b/utils/build/docker/dotnet/build-helpers/version-tool.Dockerfile @@ -0,0 +1,5 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-version-tool +WORKDIR /app +ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 +COPY utils/build/docker/dotnet/GetAssemblyVersion ./ +RUN dotnet publish -c Release -o /out diff --git a/utils/build/docker/dotnet/install_ddtrace.sh b/utils/build/docker/dotnet/install_ddtrace.sh index f785cf445cd..d66538aeb8c 100755 --- a/utils/build/docker/dotnet/install_ddtrace.sh +++ b/utils/build/docker/dotnet/install_ddtrace.sh @@ -28,6 +28,7 @@ if [ $(ls /binaries/Datadog.Trace.ClrProfiler.Native.so | wc -l) = 1 ]; then else if [ $(ls datadog-dotnet-apm*.tar.gz | wc -l) = 1 ]; then echo "Install ddtrace from $(ls datadog-dotnet-apm*.tar.gz)" + DDTRACE_VERSION=$(ls datadog-dotnet-apm*.tar.gz | grep -oP '\d+\.\d+\.\d+' | head -1) else echo "Install ddtrace from github releases" if ! DDTRACE_VERSION="$(get_latest_release DataDog/dd-trace-dotnet)"; then @@ -46,4 +47,5 @@ else fi tar xzf $(ls datadog-dotnet-apm*.tar.gz) -C /opt/datadog + echo "$DDTRACE_VERSION" > /system-tests-library-version fi diff --git a/utils/build/docker/dotnet/poc.Dockerfile b/utils/build/docker/dotnet/poc.Dockerfile index 44884499673..ab8502f1cbf 100644 --- a/utils/build/docker/dotnet/poc.Dockerfile +++ b/utils/build/docker/dotnet/poc.Dockerfile @@ -26,6 +26,15 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl COPY utils/build/docker/dotnet/install_ddtrace.sh binaries/ /binaries/ RUN --mount=type=secret,id=github_token /binaries/install_ddtrace.sh +# extract library version from installed assembly if install script could not determine it +COPY --from=system_tests/dotnet-version-tool /out /tmp/get-assembly-version/ +RUN if [ ! -f /system-tests-library-version ]; then \ + dll=$(ls /opt/datadog/net*/Datadog.Trace.dll 2>/dev/null | head -1); \ + if [ -n "$dll" ]; then \ + /tmp/get-assembly-version/GetAssemblyVersion "$dll" > /system-tests-library-version; \ + fi; \ + fi && rm -rf /tmp/get-assembly-version + # Enable Datadog .NET SDK ENV CORECLR_ENABLE_PROFILING=1 ENV CORECLR_PROFILER='{846F5F1C-F9AE-4B07-969E-05C26BC060D8}' diff --git a/utils/build/docker/dotnet/uds.Dockerfile b/utils/build/docker/dotnet/uds.Dockerfile index 76bf708b309..4d50ecf2693 100644 --- a/utils/build/docker/dotnet/uds.Dockerfile +++ b/utils/build/docker/dotnet/uds.Dockerfile @@ -20,6 +20,15 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y curl COPY utils/build/docker/dotnet/install_ddtrace.sh binaries/ /binaries/ RUN --mount=type=secret,id=github_token /binaries/install_ddtrace.sh +# extract library version from installed assembly if install script could not determine it +COPY --from=system_tests/dotnet-version-tool /out /tmp/get-assembly-version/ +RUN if [ ! -f /system-tests-library-version ]; then \ + dll=$(ls /opt/datadog/net*/Datadog.Trace.dll 2>/dev/null | head -1); \ + if [ -n "$dll" ]; then \ + /tmp/get-assembly-version/GetAssemblyVersion "$dll" > /system-tests-library-version; \ + fi; \ + fi && rm -rf /tmp/get-assembly-version + # Enable Datadog .NET SDK ENV CORECLR_ENABLE_PROFILING=1 ENV CORECLR_PROFILER='{846F5F1C-F9AE-4B07-969E-05C26BC060D8}' diff --git a/utils/build/docker/golang/install_ddtrace.sh b/utils/build/docker/golang/install_ddtrace.sh index 7859f9e4df2..d6e1525cb51 100755 --- a/utils/build/docker/golang/install_ddtrace.sh +++ b/utils/build/docker/golang/install_ddtrace.sh @@ -60,6 +60,7 @@ go mod tidy lib_mod_dir=$(go list -f '{{.Dir}}' -m github.com/DataDog/dd-trace-go/v2) version=$(sed -nrE 's#.*"v(.*)".*#\1#p' "${lib_mod_dir}/internal/version/version.go") # Parse the version string content "v.*" echo "${version}" > SYSTEM_TESTS_LIBRARY_VERSION +echo "${version}" > /system-tests-library-version # Output the version of dd-trace-go (per go.mod, as well as the built-in tag). echo "dd-trace-go go.mod version: $(go list -f '{{ .Version }}' -m github.com/DataDog/dd-trace-go/v2)" diff --git a/utils/build/docker/java/install_ddtrace.sh b/utils/build/docker/java/install_ddtrace.sh index 20d67c955ac..30eeb608ee1 100755 --- a/utils/build/docker/java/install_ddtrace.sh +++ b/utils/build/docker/java/install_ddtrace.sh @@ -55,3 +55,5 @@ SYSTEM_TESTS_LIBRARY_VERSION=$(cat /binaries/SYSTEM_TESTS_LIBRARY_VERSION) echo "dd-trace version: $(cat /binaries/SYSTEM_TESTS_LIBRARY_VERSION)" +cp /binaries/SYSTEM_TESTS_LIBRARY_VERSION /system-tests-library-version + diff --git a/utils/build/docker/nodejs/install_ddtrace.sh b/utils/build/docker/nodejs/install_ddtrace.sh index 4b5f7fc2afd..7edd6a28c13 100755 --- a/utils/build/docker/nodejs/install_ddtrace.sh +++ b/utils/build/docker/nodejs/install_ddtrace.sh @@ -37,4 +37,6 @@ else echo "install from NPM" install_custom_target dd-trace fi + + node -e "process.stdout.write(require('dd-trace/package.json').version)" > /system-tests-library-version fi diff --git a/utils/build/docker/php/common/install_ddtrace.sh b/utils/build/docker/php/common/install_ddtrace.sh index a05b7411263..ad9719f56e4 100755 --- a/utils/build/docker/php/common/install_ddtrace.sh +++ b/utils/build/docker/php/common/install_ddtrace.sh @@ -172,6 +172,8 @@ fi php -d error_reporting='' -d extension=ddtrace.so -d extension=ddappsec.so -r 'echo phpversion("ddtrace");' > \ /binaries/SYSTEM_TESTS_LIBRARY_VERSION +cp /binaries/SYSTEM_TESTS_LIBRARY_VERSION /system-tests-library-version + find /opt -name ddappsec-helper -exec ln -s '{}' /usr/local/bin/ \; mkdir -p /etc/dd-appsec diff --git a/utils/build/docker/python/install_ddtrace.sh b/utils/build/docker/python/install_ddtrace.sh index f6e43feda70..db77bbfd7ce 100755 --- a/utils/build/docker/python/install_ddtrace.sh +++ b/utils/build/docker/python/install_ddtrace.sh @@ -44,3 +44,5 @@ else echo "ERROR: Found several usable wheel files in binaries/, abort." exit 1 fi + +python -c "import ddtrace; print(ddtrace.__version__, end='')" > /system-tests-library-version diff --git a/utils/build/docker/ruby/install_ddtrace.sh b/utils/build/docker/ruby/install_ddtrace.sh index 67e4b52a67a..5f1eede64da 100755 --- a/utils/build/docker/ruby/install_ddtrace.sh +++ b/utils/build/docker/ruby/install_ddtrace.sh @@ -67,3 +67,6 @@ fi bundle config set --local without test development bundle install + +bundle exec ruby -e "require 'datadog'; print Datadog::VERSION::STRING" > /system-tests-library-version 2>/dev/null \ + || bundle exec ruby -e "require 'ddtrace'; print DDTrace::VERSION" > /system-tests-library-version