Skip to content

Commit 9b46168

Browse files
Move KindLoad to a condition instead of a wrapper (#21292) (#21307)
* Move KindLoad to a condition instead of a wrapper * Add changelog * Clean up typing (cherry picked from commit 586c0a2) Co-authored-by: Juanpe Araque <[email protected]>
1 parent fe63772 commit 9b46168

File tree

3 files changed

+40
-55
lines changed

3 files changed

+40
-55
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Move KindLoad to a built-in condition instead of a wrapper

datadog_checks_dev/datadog_checks/dev/kind.py

Lines changed: 33 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
from __future__ import annotations
55

66
from contextlib import contextmanager
7+
from dataclasses import dataclass
78
from shutil import which
8-
from typing import TYPE_CHECKING
9+
from typing import Any, Callable, ContextManager, Protocol, runtime_checkable
910

1011
import pytest
1112

@@ -15,59 +16,49 @@
1516
from .subprocess import run_command
1617
from .utils import get_active_env, get_current_check_name
1718

18-
if TYPE_CHECKING:
19-
from contextlib import AbstractContextManager
20-
from typing import Self
2119

20+
def _setup_conditions(conditions: list[Callable[[], Any]] | None, cluster_config: ClusterConfig):
21+
if not conditions:
22+
return
2223

23-
def _setup_wrappers(wrappers: list[AbstractContextManager] | None, cluster_name: str):
24-
"""Set up wrappers with cluster-specific configuration.
24+
for condition in conditions:
25+
if isinstance(condition, ClusterCondition):
26+
condition.add_cluster_info(cluster_config)
2527

26-
:param wrappers: List of wrapper instances to configure
27-
:param cluster_name: The name of the Kind cluster
28-
"""
29-
if not wrappers:
30-
return
3128

32-
for wrapper in wrappers:
33-
match wrapper:
34-
case KindLoad():
35-
wrapper.cluster_name = cluster_name
36-
case _:
37-
# No special setup needed for other wrapper types
38-
pass
29+
@dataclass
30+
class ClusterConfig:
31+
cluster_name: str
32+
33+
34+
@runtime_checkable
35+
class ClusterCondition(Protocol):
36+
def add_cluster_info(self, cluster_config: ClusterConfig): ...
3937

4038

4139
@contextmanager
4240
def kind_run(
43-
sleep=None,
44-
endpoints=None,
45-
conditions=None,
46-
env_vars=None,
47-
wrappers=None,
48-
kind_config=None,
49-
attempts=None,
50-
attempts_wait=1,
41+
sleep: float | None = None,
42+
endpoints: str | list[str] | None = None,
43+
conditions: list[Callable[[], Any]] | None = None,
44+
env_vars: dict[str, str] | None = None,
45+
wrappers: list[ContextManager] | None = None,
46+
kind_config: str | None = None,
47+
attempts: int | None = None,
48+
attempts_wait: int = 1,
5149
):
5250
"""
5351
This utility provides a convenient way to safely set up and tear down Kind environments.
5452
5553
:param sleep: Number of seconds to wait before yielding.
56-
:type sleep: ``float``
5754
:param endpoints: Endpoints to verify access for before yielding. Shorthand for adding
5855
``conditions.CheckEndpoints(endpoints)`` to the ``conditions`` argument.
59-
:type endpoints: ``list`` of ``str``, or a single ``str``
6056
:param conditions: A list of callable objects that will be executed before yielding to check for errors.
61-
:type conditions: ``callable``
6257
:param env_vars: A dictionary to update ``os.environ`` with during execution.
63-
:type env_vars: ``dict``
6458
:param wrappers: A list of context managers to use during execution.
6559
:param kind_config: A path to a yaml file that contains the configuration for creating the kind cluster.
66-
:type kind_config: ``str``
6760
:param attempts: Number of attempts to run `up` and the `conditions` successfully. Defaults to 2 in CI.
68-
:type attempts: ``int``
6961
:param attempts_wait: Time to wait between attempts.
70-
:type attempts_wait: ``int``
7162
"""
7263
if not which('kind'):
7364
pytest.skip('Kind not available')
@@ -88,8 +79,7 @@ def kind_run(
8879
set_up = KindUp(cluster_name, kind_config)
8980
tear_down = KindDown(cluster_name)
9081

91-
# Set up wrappers with cluster-specific configuration
92-
_setup_wrappers(wrappers, cluster_name)
82+
_setup_conditions(conditions, ClusterConfig(cluster_name))
9383

9484
with environment_run(
9585
up=set_up,
@@ -135,14 +125,14 @@ def __call__(self):
135125
run_command(['kind', 'delete', 'cluster', '--name', self.cluster_name], check=True)
136126

137127

138-
class KindLoad:
139-
"""Context manager for loading Docker images into a Kind cluster.
128+
class KindLoad(LazyFunction):
129+
"""Condition for loading Docker images into a Kind cluster.
140130
141-
This context manager should be passed to the wrappers argument in environment_run
131+
This condition should be passed to the conditions argument in environment_run
142132
to load images into the Kind cluster after it's created.
143133
144134
Example:
145-
with kind_run(wrappers=[KindLoad("my-image:latest")]):
135+
with kind_run(conditions=[KindLoad("my-image:latest")]):
146136
# The image is now loaded in the kind cluster
147137
pass
148138
"""
@@ -151,14 +141,12 @@ def __init__(self, image: str):
151141
self.image = image
152142
self.cluster_name: str | None = None
153143

154-
def __enter__(self) -> Self:
144+
def __call__(self):
155145
if self.cluster_name is None:
156-
raise RuntimeError("cluster_name must be set before entering KindLoad context")
146+
raise RuntimeError("cluster_name must be set before calling KindLoad")
157147

158148
load_cmd = ['kind', 'load', 'docker-image', self.image, '--name', self.cluster_name]
159149
run_command(load_cmd, check=True)
160-
return self
161150

162-
def __exit__(self, exc_type, exc_val, exc_tb):
163-
"""Exit the context manager (no cleanup needed for image loading)."""
164-
pass
151+
def add_cluster_info(self, cluster_config: ClusterConfig):
152+
self.cluster_name = cluster_config.cluster_name

datadog_checks_dev/tests/test_kind.py

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,20 @@ def test_retry_condition_failed_only_on_first_run(self):
6161

6262

6363
class TestKindLoad:
64-
def test_kind_load_context_manager_without_cluster_name(self):
64+
def test_kind_load_without_cluster_name(self):
6565
kind_load = KindLoad("test-image:latest")
6666

67-
with pytest.raises(RuntimeError, match="cluster_name must be set before entering KindLoad context"):
68-
with kind_load:
69-
pass
67+
with pytest.raises(RuntimeError, match="cluster_name must be set before calling KindLoad"):
68+
kind_load()
7069

7170
@patch('datadog_checks.dev.kind.run_command')
72-
def test_kind_load_context_manager_with_cluster_name(self, mock_run_command):
73-
"""Test that KindLoad calls the correct kind load command when cluster_name is set."""
71+
def test_kind_load_with_cluster_name(self, mock_run_command):
7472
image = "test-image:latest"
7573
cluster_name = "test-cluster"
7674
kind_load = KindLoad(image)
7775
kind_load.cluster_name = cluster_name
7876

79-
with kind_load as ctx:
80-
assert ctx is kind_load
77+
kind_load()
8178

8279
mock_run_command.assert_called_once_with(
8380
['kind', 'load', 'docker-image', image, '--name', cluster_name], check=True
@@ -86,7 +83,6 @@ def test_kind_load_context_manager_with_cluster_name(self, mock_run_command):
8683
@not_windows_ci
8784
@patch('datadog_checks.dev.kind.run_command')
8885
def test_kind_load_integration_with_kind_run(self, mock_run_command):
89-
"""Test that KindLoad integrates correctly with kind_run."""
9086
image = "test-image:latest"
9187
kind_load = KindLoad(image)
9288

@@ -100,7 +96,7 @@ def test_kind_load_integration_with_kind_run(self, mock_run_command):
10096
mock_down_instance = MagicMock()
10197
mock_kind_down.return_value = mock_down_instance
10298

103-
with kind_run(wrappers=[kind_load]):
99+
with kind_run(conditions=[kind_load]):
104100
# Verify that cluster_name was set on the KindLoad instance
105101
assert kind_load.cluster_name is not None
106102
assert kind_load.cluster_name.startswith('cluster-')

0 commit comments

Comments
 (0)