Skip to content

K8s injector dev first scenario #4692

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions tests/k8s_injector_dev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Package for K8s injector-dev tests."""
30 changes: 30 additions & 0 deletions tests/k8s_injector_dev/test_k8s_injector_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from utils import scenarios, features, logger, context
from utils.injector_dev.harness import Harness


@features.k8s_admission_controller
@scenarios.k8s_injector_dev_single_service
class TestK8sLibInjection:
"""Test K8s injector dev tool"""

def test_k8s_injector_dev(self):
logger.info("Testing K8s lib injection using the injector-dev tool")

# Make sure current_scenario_provision is not None
if context.k8s_scenario_provision is None:
raise ValueError("k8s_scenario_provision is None, the scenario provision file was not created")

# Convert Path to string for Harness.new
scenario_path = str(context.k8s_scenario_provision)

# Initialize the harness with Must(New(t))
h = Harness.must(Harness.new(scenario_path))

# Test the python-injection deployment
injected = h.deployment("app-injection", "application")
h.require(injected.has_injection())
h.require(injected.has_env("DD_PROFILING_ENABLED", "true"))

# Test the python-no-injection deployment
not_injected = h.deployment("app-no-injection", "application")
h.require(not_injected.has_no_injection())
1 change: 1 addition & 0 deletions tests/test_the_test/test_group_rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ def test_tracer_release():
scenarios.k8s_lib_injection_spark_djm,
scenarios.k8s_lib_injection_uds,
scenarios.k8s_lib_injection,
scenarios.k8s_injector_dev_single_service,
scenarios.lib_injection_validation_unsupported_lang,
scenarios.lib_injection_validation,
scenarios.local_auto_injection_install_script,
Expand Down
8 changes: 8 additions & 0 deletions utils/_context/_scenarios/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from .test_the_test import TestTheTestScenario
from .auto_injection import InstallerAutoInjectionScenario
from .k8s_lib_injection import WeblogInjectionScenario, K8sScenario, K8sSparkScenario, K8sManualInstrumentationScenario
from .k8s_injector_dev import K8sInjectorDevScenario
from .docker_ssi import DockerSSIScenario
from .external_processing import ExternalProcessingScenario
from .ipv6 import IPV6Scenario
Expand Down Expand Up @@ -910,6 +911,13 @@ class _Scenarios:
)
k8s_lib_injection_spark_djm = K8sSparkScenario("K8S_LIB_INJECTION_SPARK_DJM", doc="Kubernetes lib injection DJM")

# K8s Injector dev scenarios
k8s_injector_dev_single_service = K8sInjectorDevScenario(
"K8S_INJECTOR_DEV_SINGLE_SERVICE",
doc="Kubernetes Injector Dev Scenario",
scenario_provision="single-service.yaml",
)

docker_ssi = DockerSSIScenario(
"DOCKER_SSI",
doc="Validates the installer and the ssi on a docker environment",
Expand Down
150 changes: 150 additions & 0 deletions utils/_context/_scenarios/k8s_injector_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
from pathlib import Path

import pytest

from utils._context.component_version import ComponentVersion, Version

from utils.k8s.k8s_component_image import (
K8sComponentImage,
extract_library_version,
extract_injector_version,
extract_cluster_agent_version,
)
from utils._logger import logger
from utils.injector_dev.injector_client import InjectorDevClient
from utils.injector_dev.scenario_provision_updater import ScenarioProvisionUpdater
from .core import Scenario, scenario_groups, ScenarioGroup

# Default scenario groups for K8sInjectorDevScenario
DEFAULT_SCENARIO_GROUPS: list[ScenarioGroup] = [scenario_groups.all, scenario_groups.lib_injection]


class K8sInjectorDevScenario(Scenario):
"""Scenario that tests kubernetes lib injection using the injector-dev tool"""

def __init__(
self,
name: str,
doc: str,
scenario_provision: str,
scenario_groups: list[ScenarioGroup] | None = None,
) -> None:
if scenario_groups is None:
scenario_groups = DEFAULT_SCENARIO_GROUPS
super().__init__(name, doc=doc, github_workflow="libinjection", scenario_groups=scenario_groups)
# provision template
self.scenario_provision = scenario_provision
# Used to store the path to the actual scenario provision file (with injected component images/log folder)
self.current_scenario_provision: Path | None = None

def configure(self, config: pytest.Config):
# These are the tested components: dd_cluser_agent_version, weblog image, library_init_version, injector version
self.k8s_weblog = config.option.k8s_weblog

# Get component images: weblog, lib init, cluster agent, injector
self.k8s_weblog_img = K8sComponentImage(config.option.k8s_weblog_img, lambda _: "weblog-version-1.0")

self.k8s_lib_init_img = K8sComponentImage(config.option.k8s_lib_init_img, extract_library_version)

self.k8s_cluster_img = K8sComponentImage(config.option.k8s_cluster_img, extract_cluster_agent_version)

self.k8s_injector_img = K8sComponentImage(
config.option.k8s_injector_img if config.option.k8s_injector_img else "gcr.io/datadoghq/apm-inject:latest",
extract_injector_version,
)

# Get component versions: lib init, cluster agent, injector
self._library = ComponentVersion(config.option.k8s_library, self.k8s_lib_init_img.version)
self.components["library"] = self._library.version
self.components["cluster_agent"] = self.k8s_cluster_img.version
self._datadog_apm_inject_version = f"v{self.k8s_injector_img.version}"
self.components["datadog-apm-inject"] = self._datadog_apm_inject_version

# is it on sleep mode?
self._sleep_mode = config.option.sleep

# Check if the scenario_provision file exists
scenario_provision_path = Path("utils") / "build" / "injector-dev" / self.scenario_provision
if not scenario_provision_path.exists():
raise FileNotFoundError(
f"Scenario provision file not found at {scenario_provision_path}. Please build it first."
)

# Initialize the injector client
self.injector_client = InjectorDevClient()

def print_context(self):
logger.stdout(".:: K8s Lib injection test components ::.")
logger.stdout(f"Weblog: {self.k8s_weblog}")
logger.stdout(f"Weblog image: {self.k8s_weblog_img.registry_url}")
logger.stdout(f"Library: {self._library}")
logger.stdout(f"Lib init image: {self.k8s_lib_init_img.registry_url}")
logger.stdout(f"Cluster agent version: {self.k8s_cluster_img.version}")
logger.stdout(f"Cluster agent image: {self.k8s_cluster_img.registry_url}")
logger.stdout(f"Injector version: {self._datadog_apm_inject_version}")
logger.stdout(f"Injector image: {self.k8s_injector_img.registry_url}")

def get_warmups(self):
warmups = super().get_warmups()
warmups.append(self.print_context)
warmups.append(lambda: logger.terminal.write_sep("=", "Starting injector-dev", bold=True))
warmups.append(self._start_injector_dev)
warmups.append(lambda: logger.terminal.write_sep("=", "Applying injector-dev scenario", bold=True))
warmups.append(self._apply_scenario_injector_dev)

return warmups

def pytest_sessionfinish(self, session: pytest.Session, exitstatus: int): # noqa: ARG002
self.close_targets()

def close_targets(self):
logger.info("Destroying cluster")
self._stop_injector_dev()

def _start_injector_dev(self):
"""Start the injector-dev tool"""
self.injector_client.start(debug=True)

def _stop_injector_dev(self):
"""Stop the injector-dev tool"""
self.injector_client.stop(clean_k8s=True)

def _apply_scenario_injector_dev(self):
"""Applies the scenario in yaml format to the injector-dev tool."""
# Create a ScenarioProvisionUpdater instance pointing to the logs directory
logs_dir = Path(f"logs_{self.name}")
updater = ScenarioProvisionUpdater(logs_dir=logs_dir)

# Update the scenario file with the component images
self.current_scenario_provision = updater.update_scenario(
self.scenario_provision,
self.k8s_cluster_img,
self.k8s_injector_img,
self.k8s_weblog_img, # Include the weblog image
self.k8s_lib_init_img, # Include the library init image
self._library.name, # Specify the language being tested
)

logger.info(f"Updated scenario file written to {self.current_scenario_provision}")

# Apply the updated scenario
if self.current_scenario_provision:
self.injector_client.apply_scenario(self.current_scenario_provision, wait=True, debug=True)
else:
raise RuntimeError("No scenario provision file found. Please ensure the scenario is properly configured.")

@property
def library(self):
return self._library

@property
def weblog_variant(self):
return self.k8s_weblog

@property
def k8s_cluster_agent_version(self):
return Version(self.k8s_cluster_img.version)

@property
def dd_apm_inject_version(self):
return self._datadog_apm_inject_version
4 changes: 4 additions & 0 deletions utils/_context/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ def vm_os_cpu(self) -> str:
def vm_name(self) -> str:
return self.virtual_machine.name

@property
def k8s_scenario_provision(self) -> str:
return self._get_scenario_property("current_scenario_provision", {})

def serialize(self):
result = {
"weblog_variant": self.weblog_variant,
Expand Down
55 changes: 55 additions & 0 deletions utils/build/injector-dev/single-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
helm:
apps:
- name: app-injection
namespace: application
values:
service:
port: 18080
image:
repository: registry.ddbuild.io/ci/injector-dev/python
tag: 2cd78ded
podLabels:
tags.datadoghq.com/env: local
app: "billing-service"
env:
- name: DD_TRACE_DEBUG
value: "true"
- name: DD_APM_INSTRUMENTATION_DEBUG
value: "true"
- name: app-no-injection
namespace: application
values:
service:
port: 18080
image:
repository: registry.ddbuild.io/ci/injector-dev/python
tag: 2cd78ded
podLabels:
tags.datadoghq.com/env: local
env:
- name: DD_TRACE_DEBUG
value: "true"
- name: DD_APM_INSTRUMENTATION_DEBUG
value: "true"
versions:
agent: "7.64.0"
cluster_agent: "1.0.0"
injector: "0.35.0"
config:
datadog:
apm:
instrumentation:
enabled: true
targets:
- name: "billing-service"
podSelector:
matchLabels:
app: "billing-service"
namespaceSelector:
matchNames:
- "application"
ddTraceVersions:
python: "latest"
ddTraceConfigs:
- name: "DD_PROFILING_ENABLED"
value: "true"
6 changes: 6 additions & 0 deletions utils/injector_dev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
"""Module providing utilities for the injector-dev tool."""

from .injector_client import InjectorDevClient
from .scenario_provision_updater import ScenarioProvisionUpdater

__all__ = ["InjectorDevClient", "ScenarioProvisionUpdater"]
Loading
Loading