4
4
from __future__ import annotations
5
5
6
6
from contextlib import contextmanager
7
+ from dataclasses import dataclass
7
8
from shutil import which
8
- from typing import TYPE_CHECKING
9
+ from typing import Any , Callable , ContextManager , Protocol , runtime_checkable
9
10
10
11
import pytest
11
12
15
16
from .subprocess import run_command
16
17
from .utils import get_active_env , get_current_check_name
17
18
18
- if TYPE_CHECKING :
19
- from contextlib import AbstractContextManager
20
- from typing import Self
21
19
20
+ def _setup_conditions (conditions : list [Callable [[], Any ]] | None , cluster_config : ClusterConfig ):
21
+ if not conditions :
22
+ return
22
23
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 )
25
27
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
31
28
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 ): ...
39
37
40
38
41
39
@contextmanager
42
40
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 ,
51
49
):
52
50
"""
53
51
This utility provides a convenient way to safely set up and tear down Kind environments.
54
52
55
53
:param sleep: Number of seconds to wait before yielding.
56
- :type sleep: ``float``
57
54
:param endpoints: Endpoints to verify access for before yielding. Shorthand for adding
58
55
``conditions.CheckEndpoints(endpoints)`` to the ``conditions`` argument.
59
- :type endpoints: ``list`` of ``str``, or a single ``str``
60
56
:param conditions: A list of callable objects that will be executed before yielding to check for errors.
61
- :type conditions: ``callable``
62
57
:param env_vars: A dictionary to update ``os.environ`` with during execution.
63
- :type env_vars: ``dict``
64
58
:param wrappers: A list of context managers to use during execution.
65
59
:param kind_config: A path to a yaml file that contains the configuration for creating the kind cluster.
66
- :type kind_config: ``str``
67
60
:param attempts: Number of attempts to run `up` and the `conditions` successfully. Defaults to 2 in CI.
68
- :type attempts: ``int``
69
61
:param attempts_wait: Time to wait between attempts.
70
- :type attempts_wait: ``int``
71
62
"""
72
63
if not which ('kind' ):
73
64
pytest .skip ('Kind not available' )
@@ -88,8 +79,7 @@ def kind_run(
88
79
set_up = KindUp (cluster_name , kind_config )
89
80
tear_down = KindDown (cluster_name )
90
81
91
- # Set up wrappers with cluster-specific configuration
92
- _setup_wrappers (wrappers , cluster_name )
82
+ _setup_conditions (conditions , ClusterConfig (cluster_name ))
93
83
94
84
with environment_run (
95
85
up = set_up ,
@@ -135,14 +125,14 @@ def __call__(self):
135
125
run_command (['kind' , 'delete' , 'cluster' , '--name' , self .cluster_name ], check = True )
136
126
137
127
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.
140
130
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
142
132
to load images into the Kind cluster after it's created.
143
133
144
134
Example:
145
- with kind_run(wrappers =[KindLoad("my-image:latest")]):
135
+ with kind_run(conditions =[KindLoad("my-image:latest")]):
146
136
# The image is now loaded in the kind cluster
147
137
pass
148
138
"""
@@ -151,14 +141,12 @@ def __init__(self, image: str):
151
141
self .image = image
152
142
self .cluster_name : str | None = None
153
143
154
- def __enter__ (self ) -> Self :
144
+ def __call__ (self ):
155
145
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" )
157
147
158
148
load_cmd = ['kind' , 'load' , 'docker-image' , self .image , '--name' , self .cluster_name ]
159
149
run_command (load_cmd , check = True )
160
- return self
161
150
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
0 commit comments