Skip to content

Commit 350f246

Browse files
fix(core): improper reading of .testcontainers.properties (#863)
fix #864 The environment variables were not overridden from the .testcontainers.properties file for ryuk variables. This causes the properties file to never actually be used. This commit detects the environment variable, and if unspecified falls back to the properties file, and if not specified, defaults to false --------- Co-authored-by: David Ankin <[email protected]>
1 parent b21e5e3 commit 350f246

File tree

2 files changed

+97
-12
lines changed

2 files changed

+97
-12
lines changed

core/testcontainers/core/config.py

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import docker
1313

14+
ENABLE_FLAGS = ("yes", "true", "t", "y", "1")
15+
1416

1517
class ConnectionMode(Enum):
1618
bridge_ip = "bridge_ip"
@@ -45,16 +47,6 @@ def get_docker_socket() -> str:
4547
return "/var/run/docker.sock"
4648

4749

48-
def get_bool_env(name: str) -> bool:
49-
"""
50-
Get environment variable named `name` and convert it to bool.
51-
52-
Defaults to False.
53-
"""
54-
value = environ.get(name, "")
55-
return value.lower() in ("yes", "true", "t", "y", "1")
56-
57-
5850
TC_FILE = ".testcontainers.properties"
5951
TC_GLOBAL = Path.home() / TC_FILE
6052

@@ -96,11 +88,20 @@ def read_tc_properties() -> dict[str, str]:
9688

9789
@dataclass
9890
class TestcontainersConfiguration:
91+
def _render_bool(self, env_name: str, prop_name: str) -> bool:
92+
env_val = environ.get(env_name, None)
93+
if env_val is not None:
94+
return env_val.lower() in ENABLE_FLAGS
95+
prop_val = self.tc_properties.get(prop_name, None)
96+
if prop_val is not None:
97+
return prop_val.lower() in ENABLE_FLAGS
98+
return False
99+
99100
max_tries: int = int(environ.get("TC_MAX_TRIES", "120"))
100101
sleep_time: float = float(environ.get("TC_POOLING_INTERVAL", "1"))
101102
ryuk_image: str = environ.get("RYUK_CONTAINER_IMAGE", "testcontainers/ryuk:0.8.1")
102-
ryuk_privileged: bool = get_bool_env("TESTCONTAINERS_RYUK_PRIVILEGED")
103-
ryuk_disabled: bool = get_bool_env("TESTCONTAINERS_RYUK_DISABLED")
103+
_ryuk_privileged: Optional[bool] = None
104+
_ryuk_disabled: Optional[bool] = None
104105
_ryuk_docker_socket: str = ""
105106
ryuk_reconnection_timeout: str = environ.get("RYUK_RECONNECTION_TIMEOUT", "10s")
106107
tc_properties: dict[str, str] = field(default_factory=read_tc_properties)
@@ -129,6 +130,28 @@ def docker_auth_config(self, value: str) -> None:
129130
def tc_properties_get_tc_host(self) -> Union[str, None]:
130131
return self.tc_properties.get("tc.host")
131132

133+
@property
134+
def ryuk_privileged(self) -> bool:
135+
if self._ryuk_privileged is not None:
136+
return bool(self._ryuk_privileged)
137+
self._ryuk_privileged = self._render_bool("TESTCONTAINERS_RYUK_PRIVILEGED", "ryuk.container.privileged")
138+
return self._ryuk_privileged
139+
140+
@ryuk_privileged.setter
141+
def ryuk_privileged(self, value: bool) -> None:
142+
self._ryuk_privileged = value
143+
144+
@property
145+
def ryuk_disabled(self) -> bool:
146+
if self._ryuk_disabled is not None:
147+
return bool(self._ryuk_disabled)
148+
self._ryuk_disabled = self._render_bool("TESTCONTAINERS_RYUK_DISABLED", "ryuk.disabled")
149+
return self._ryuk_disabled
150+
151+
@ryuk_disabled.setter
152+
def ryuk_disabled(self, value: bool) -> None:
153+
self._ryuk_disabled = value
154+
132155
@property
133156
def timeout(self) -> float:
134157
return self.max_tries * self.sleep_time

core/tests/test_config.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,68 @@ def test_read_tc_properties(monkeypatch: MonkeyPatch) -> None:
2828
assert config.tc_properties == {"tc.host": "some_value"}
2929

3030

31+
def test_set_tc_properties(monkeypatch: MonkeyPatch) -> None:
32+
"""
33+
Ensure the configuration file variables can be read if no environment variable is set
34+
"""
35+
with tempfile.TemporaryDirectory() as tmpdirname:
36+
file = f"{tmpdirname}/{TC_FILE}"
37+
with open(file, "w") as f:
38+
f.write("ryuk.disabled=true\n")
39+
f.write("ryuk.container.privileged=false\n")
40+
41+
monkeypatch.setattr("testcontainers.core.config.TC_GLOBAL", file)
42+
43+
config = TCC()
44+
45+
assert config.ryuk_disabled == True
46+
assert config.ryuk_privileged == False
47+
48+
49+
def test_override_tc_properties_1(monkeypatch: MonkeyPatch) -> None:
50+
"""
51+
Ensure that we can re-set the configuration variables programattically to override
52+
testcontainers.properties
53+
"""
54+
with tempfile.TemporaryDirectory() as tmpdirname:
55+
file = f"{tmpdirname}/{TC_FILE}"
56+
with open(file, "w") as f:
57+
f.write("ryuk.disabled=true\n")
58+
f.write("ryuk.container.privileged=false\n")
59+
60+
monkeypatch.setattr("testcontainers.core.config.TC_GLOBAL", file)
61+
62+
config = TCC()
63+
config.ryuk_disabled = False
64+
config.ryuk_privileged = True
65+
66+
assert config.ryuk_disabled == False
67+
assert config.ryuk_privileged == True
68+
69+
70+
def test_override_tc_properties_2(monkeypatch: MonkeyPatch) -> None:
71+
"""
72+
Ensure that we can override the testcontainers.properties with environment variables
73+
"""
74+
with tempfile.TemporaryDirectory() as tmpdirname:
75+
file = f"{tmpdirname}/{TC_FILE}"
76+
with open(file, "w") as f:
77+
f.write("ryuk.disabled=true\n")
78+
f.write("ryuk.container.privileged=false\n")
79+
80+
monkeypatch.setattr("testcontainers.core.config.TC_GLOBAL", file)
81+
82+
import os
83+
84+
os.environ["TESTCONTAINERS_RYUK_DISABLED"] = "false"
85+
os.environ["TESTCONTAINERS_RYUK_PRIVILEGED"] = "true"
86+
87+
config = TCC()
88+
89+
assert config.ryuk_disabled == False
90+
assert config.ryuk_privileged == True
91+
92+
3193
@mark.parametrize("docker_auth_config_env", ["key=value", ""])
3294
@mark.parametrize("warning_dict", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}])
3395
@mark.parametrize("warning_dict_post", [{}, {"key": "value"}, {"DOCKER_AUTH_CONFIG": "TEST"}])

0 commit comments

Comments
 (0)