Skip to content

Commit 86fff90

Browse files
committed
feat(ryuk): honor ryuk.container.privileged property and env var
Mirror testcontainers-dotnet behavior and resolve SELinux denials on Fedora (issue #183) by letting users run Ryuk in privileged mode via either the `ryuk.container.privileged` property in `~/.testcontainers.properties` or the `TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED` environment variable. Both `"true"` and `"1"` are treated as truthy. The environment variable takes precedence over the property when both are set, matching the dotnet implementation. The resolver is extracted as `Testcontainers.ryuk_privileged?/1` (@doc false) so it can be unit-tested without launching Ryuk. Fixes #183
1 parent 7d0f0bb commit 86fff90

2 files changed

Lines changed: 109 additions & 2 deletions

File tree

lib/testcontainers.ex

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,32 @@ defmodule Testcontainers do
450450
end
451451
end
452452

453+
@doc false
454+
# Resolves whether Ryuk should run in privileged mode.
455+
#
456+
# Mirrors testcontainers-dotnet: honors the `ryuk.container.privileged`
457+
# property and the `TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED` environment
458+
# variable. The environment variable takes precedence when both are set.
459+
# Values `"true"` and `"1"` are treated as truthy.
460+
def ryuk_privileged?(properties) when is_map(properties) do
461+
env_value = System.get_env("TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED")
462+
prop_value = Map.get(properties, "ryuk.container.privileged")
463+
464+
value = env_value || prop_value
465+
466+
truthy?(value)
467+
end
468+
469+
defp truthy?(value) when is_binary(value) do
470+
case String.downcase(String.trim(value)) do
471+
"true" -> true
472+
"1" -> true
473+
_ -> false
474+
end
475+
end
476+
477+
defp truthy?(_), do: false
478+
453479
@doc false
454480
def running_in_container?(
455481
dockerenv_path \\ "/.dockerenv",
@@ -668,7 +694,7 @@ defmodule Testcontainers do
668694
end
669695

670696
defp start_ryuk(conn, session_id, properties, docker_host, docker_hostname) do
671-
ryuk_privileged = Map.get(properties, "ryuk.container.privileged", "false") == "true"
697+
ryuk_privileged = ryuk_privileged?(properties)
672698

673699
ryuk_config =
674700
Container.new("testcontainers/ryuk:#{Constants.ryuk_version()}")

test/testcontainers_test.exs

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,88 @@ defmodule TestcontainersTest do
22
alias Testcontainers.Connection
33
alias Testcontainers.Container
44
alias Testcontainers.Docker
5-
use ExUnit.Case, async: true
5+
# async: false because ryuk_privileged? tests mutate process environment
6+
use ExUnit.Case, async: false
7+
8+
@ryuk_privileged_env "TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED"
9+
@ryuk_privileged_prop "ryuk.container.privileged"
10+
11+
describe "ryuk_privileged?/1" do
12+
setup do
13+
original = System.get_env(@ryuk_privileged_env)
14+
15+
on_exit(fn ->
16+
case original do
17+
nil -> System.delete_env(@ryuk_privileged_env)
18+
value -> System.put_env(@ryuk_privileged_env, value)
19+
end
20+
end)
21+
22+
:ok
23+
end
24+
25+
test "returns false when neither property nor env var is set" do
26+
System.delete_env(@ryuk_privileged_env)
27+
refute Testcontainers.ryuk_privileged?(%{})
28+
end
29+
30+
test "returns true when property is 'true'" do
31+
System.delete_env(@ryuk_privileged_env)
32+
assert Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "true"})
33+
end
34+
35+
test "returns true when property is '1'" do
36+
System.delete_env(@ryuk_privileged_env)
37+
assert Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "1"})
38+
end
39+
40+
test "returns false when property is 'false'" do
41+
System.delete_env(@ryuk_privileged_env)
42+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "false"})
43+
end
44+
45+
test "returns false when property is '0'" do
46+
System.delete_env(@ryuk_privileged_env)
47+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "0"})
48+
end
49+
50+
test "returns true when env var is 'true'" do
51+
System.put_env(@ryuk_privileged_env, "true")
52+
assert Testcontainers.ryuk_privileged?(%{})
53+
end
54+
55+
test "returns true when env var is '1'" do
56+
System.put_env(@ryuk_privileged_env, "1")
57+
assert Testcontainers.ryuk_privileged?(%{})
58+
end
59+
60+
test "returns false when env var is 'false'" do
61+
System.put_env(@ryuk_privileged_env, "false")
62+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "true"})
63+
end
64+
65+
test "env var takes precedence over property (env false, prop true)" do
66+
System.put_env(@ryuk_privileged_env, "false")
67+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "true"})
68+
end
69+
70+
test "env var takes precedence over property (env true, prop false)" do
71+
System.put_env(@ryuk_privileged_env, "true")
72+
assert Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "false"})
73+
end
74+
75+
test "treats arbitrary strings as falsy" do
76+
System.delete_env(@ryuk_privileged_env)
77+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "yes"})
78+
refute Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => ""})
79+
end
80+
81+
test "is case-insensitive and trims whitespace" do
82+
System.delete_env(@ryuk_privileged_env)
83+
assert Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => "TRUE"})
84+
assert Testcontainers.ryuk_privileged?(%{@ryuk_privileged_prop => " true "})
85+
end
86+
end
687

788
test "cleans up containers on terminate" do
889
{:ok, pid} = Testcontainers.start_link(name: :cleanup_test1)

0 commit comments

Comments
 (0)