Skip to content

Commit 61e5916

Browse files
authored
Basic copy_to implementation (#242)
* Basic copy_to implementation * fix hash * remove deprecation in running cli tests * use inotify tools for phoenix * inotify tools for all jobs * refactor create and start container to guarantee correct runs * extract pulling image from create_and_start_container * with ip? * what about no wait strategy? * address rebase * fix hash * with waiting strategy * revert inotify install * revert create container API
1 parent 77a0ba6 commit 61e5916

6 files changed

Lines changed: 71 additions & 9 deletions

File tree

lib/container.ex

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ defmodule Testcontainers.Container do
2020
privileged: false,
2121
bind_mounts: [],
2222
bind_volumes: [],
23+
copy_to: [],
2324
labels: %{},
2425
auto_remove: false,
2526
container_id: nil,
@@ -281,6 +282,14 @@ defmodule Testcontainers.Container do
281282
%__MODULE__{config | hostname: hostname}
282283
end
283284

285+
def with_copy_to(%__MODULE__{} = config, target, source)
286+
when is_binary(target) and is_binary(source) do
287+
%__MODULE__{
288+
config
289+
| copy_to: [%{"target" => target, "contents" => source} | config.copy_to]
290+
}
291+
end
292+
284293
@doc """
285294
Gets the host port on the container for the given exposed port.
286295
"""

lib/copy_to.ex

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
defmodule Testcontainers.CopyTo do
2+
alias Testcontainers.Docker
3+
4+
@doc """
5+
Copy a string of contents into a file at target
6+
"""
7+
def copy_to(conn, id, %{"target" => target, "contents" => contents})
8+
when is_binary(target) and is_binary(contents) do
9+
Docker.Api.put_file(id, conn, Path.dirname(target), Path.basename(target), contents)
10+
end
11+
12+
# add more implementation for copy_to as you need them, eg.
13+
# - copy a dir into a dir
14+
# - copy a file into a file
15+
# - copy an archive into a file
16+
end

lib/testcontainers.ex

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ defmodule Testcontainers do
99

1010
require Logger
1111

12+
alias Testcontainers.CopyTo
1213
alias Testcontainers.Constants
1314
alias Testcontainers.WaitStrategy
1415
alias Testcontainers.Docker.Api
@@ -389,7 +390,10 @@ defmodule Testcontainers do
389390
{:ok, connected}
390391

391392
{:error, reason} ->
392-
Logger.info("Connection failed with #{inspect(reason)}. Retrying... Attempt #{reattempt_count + 1}/5")
393+
Logger.info(
394+
"Connection failed with #{inspect(reason)}. Retrying... Attempt #{reattempt_count + 1}/5"
395+
)
396+
393397
:timer.sleep(1000)
394398
create_ryuk_socket(container, docker_hostname, reattempt_count + 1)
395399
end
@@ -454,7 +458,8 @@ defmodule Testcontainers do
454458
config = resolve_pull_policy(config, state.properties)
455459

456460
with :ok <- maybe_pull_image(config, state.conn),
457-
{:ok, id} <- Api.create_container(config, state.conn) do
461+
{:ok, id} <- Api.create_container(config, state.conn),
462+
:ok <- copy_to_container(id, config, state.conn) do
458463
start_and_wait_container(id, config, config_builder, state)
459464
end
460465
end
@@ -515,6 +520,16 @@ defmodule Testcontainers do
515520
:ok
516521
end
517522

523+
defp copy_to_container(id, config, conn) do
524+
Enum.reduce(config.copy_to, :ok, fn
525+
copy_to, :ok ->
526+
CopyTo.copy_to(conn, id, copy_to)
527+
528+
_, error ->
529+
error
530+
end)
531+
end
532+
518533
defp wait_for_container(container, wait_strategies, conn) do
519534
Enum.reduce(wait_strategies, :ok, fn
520535
wait_strategy, :ok ->

mix.exs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ defmodule TestcontainersElixir.MixProject do
2323
links: %{"GitHub" => @source_url},
2424
licenses: ["MIT"]
2525
],
26-
preferred_cli_env: [
27-
test: :test,
28-
citest: :test,
29-
"testcontainers.test": :test
30-
],
3126
test_coverage: [
3227
summary: [threshold: 50],
3328
ignore_modules: [
@@ -39,6 +34,10 @@ defmodule TestcontainersElixir.MixProject do
3934
]
4035
end
4136

37+
def cli do
38+
[preferred_envs: [test: :test, citest: :test, "testcontainers.test": :test]]
39+
end
40+
4241
defp elixirc_paths(:test), do: ["lib", "test/support", "docker_engine_api"]
4342
defp elixirc_paths(_), do: ["lib", "docker_engine_api"]
4443

test/container_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ defmodule Testcontainers.ContainerTest do
2121
container2 = ContainerBuilder.build(Testcontainers.PostgresContainer.new())
2222

2323
assert Testcontainers.Util.Hash.struct_to_hash(container1) ==
24-
"aa66f22d5fbd75eca0328915af3fa749c624e53475988cdfcecd2b931a8a4d28"
24+
"a0b9a403e485c224323eabc27b2b8e94a6353c785cdc27f9ac2b8a9b67a47cb1"
2525

2626
assert Testcontainers.Util.Hash.struct_to_hash(container2) ==
27-
"aa66f22d5fbd75eca0328915af3fa749c624e53475988cdfcecd2b931a8a4d28"
27+
"a0b9a403e485c224323eabc27b2b8e94a6353c785cdc27f9ac2b8a9b67a47cb1"
2828
end
2929
end
3030

test/copy_to_test.exs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
defmodule CopyToTest do
2+
alias Testcontainers.HttpWaitStrategy
3+
use ExUnit.Case, async: true
4+
5+
test "copy contents to target" do
6+
port = 80
7+
contents = "Hello there"
8+
9+
config =
10+
%Testcontainers.Container{image: "nginx:alpine"}
11+
|> Testcontainers.Container.with_exposed_port(port)
12+
|> Testcontainers.Container.with_waiting_strategy(HttpWaitStrategy.new("/hello.txt", port))
13+
|> Testcontainers.Container.with_copy_to("/usr/share/nginx/html/hello.txt", contents)
14+
15+
assert {:ok, container} = Testcontainers.start_container(config)
16+
17+
mapped_port = Testcontainers.Container.mapped_port(container, port)
18+
{:ok, %{body: body}} = Tesla.get("http://127.0.0.1:#{mapped_port}/hello.txt")
19+
20+
assert contents == body
21+
assert :ok = Testcontainers.stop_container(container.container_id)
22+
end
23+
end

0 commit comments

Comments
 (0)