331 lines
12 KiB
Erlang
331 lines
12 KiB
Erlang
%%%-------------------------------------------------------------------
|
|
%%% @author anlicheng
|
|
%%% @copyright (C) 2025, <COMPANY>
|
|
%%% @doc
|
|
%%%
|
|
%%% @end
|
|
%%% Created : 23. 9月 2025 17:23
|
|
%%%-------------------------------------------------------------------
|
|
-module(docker_commands_tests).
|
|
-author("anlicheng").
|
|
-include("message_pb.hrl").
|
|
|
|
%% API
|
|
-export([
|
|
test_all/0,
|
|
test_pull/0,
|
|
test_check_image_exist/0,
|
|
test_check_image_not_exist/0,
|
|
test_create_container/0,
|
|
test_create_container_without_create_options/0,
|
|
test_create_container_patches_options/0,
|
|
test_check_container_exist/0,
|
|
test_check_container_not_exist/0,
|
|
test_is_container_running/0,
|
|
test_start_container/0,
|
|
test_stop_container/0,
|
|
test_stop_container_with_timeout/0,
|
|
test_kill_container/0,
|
|
test_kill_container_with_signal/0,
|
|
test_remove_container/0,
|
|
test_remove_container_with_options/0,
|
|
test_get_containers/0
|
|
]).
|
|
|
|
-export([test/0]).
|
|
|
|
-define(TEST_IMAGE, <<"docker.1ms.run/library/nginx:latest">>).
|
|
-define(TEST_CMD, [<<"nginx">>, <<"-g">>, <<"daemon off;">>]).
|
|
|
|
test() ->
|
|
try
|
|
test_all()
|
|
catch _:_:Stack ->
|
|
logger:debug("statck is: ~p", [Stack])
|
|
end.
|
|
|
|
-spec test_all() -> ok.
|
|
test_all() ->
|
|
%ok = test_pull(),
|
|
ok = test_check_image_exist(),
|
|
ok = test_check_image_not_exist(),
|
|
ok = test_create_container(),
|
|
ok = test_create_container_without_create_options(),
|
|
ok = test_create_container_patches_options(),
|
|
ok = test_check_container_exist(),
|
|
ok = test_check_container_not_exist(),
|
|
ok = test_is_container_running(),
|
|
%ok = test_start_container(),
|
|
%ok = test_stop_container(),
|
|
%ok = test_stop_container_with_timeout(),
|
|
%ok = test_kill_container(),
|
|
%ok = test_kill_container_with_signal(),
|
|
%ok = test_remove_container(),
|
|
%ok = test_remove_container_with_options(),
|
|
%ok = test_get_containers(),
|
|
ok.
|
|
|
|
-spec test_pull() -> ok.
|
|
test_pull() ->
|
|
ok = docker_commands:pull_image(?TEST_IMAGE, fun(Msg) -> logger:debug("msg is: ~p", [Msg]) end).
|
|
|
|
-spec test_check_image_exist() -> ok.
|
|
test_check_image_exist() ->
|
|
ok = test_pull(),
|
|
true = docker_commands:check_image_exist(?TEST_IMAGE),
|
|
ok.
|
|
|
|
-spec test_check_image_not_exist() -> ok.
|
|
test_check_image_not_exist() ->
|
|
false = docker_commands:check_image_exist(<<"docker.1ms.run/library/not-exists-for-efka-tests:latest">>),
|
|
ok.
|
|
|
|
-spec test_create_container() -> ok.
|
|
test_create_container() ->
|
|
Name = test_container_name(<<"create">>),
|
|
ContainerDir = prepare_container_dir(Name),
|
|
try
|
|
ok = test_pull(),
|
|
{ok, ContainerId} = docker_commands:create_container(ContainerDir, minimal_params(Name)),
|
|
true = is_binary(ContainerId),
|
|
ok
|
|
after
|
|
cleanup_container(Name)
|
|
end.
|
|
|
|
-spec test_create_container_without_create_options() -> ok.
|
|
test_create_container_without_create_options() ->
|
|
Name = test_container_name(<<"create-default">>),
|
|
ContainerDir = prepare_container_dir(Name),
|
|
Options = docker_container_builder:build_options(Name, ContainerDir, undefined),
|
|
assert_patched_default_options(Name, ContainerDir, Options),
|
|
ok.
|
|
|
|
-spec test_create_container_patches_options() -> ok.
|
|
test_create_container_patches_options() ->
|
|
Name = test_container_name(<<"create-patch">>),
|
|
ContainerDir = prepare_container_dir(Name),
|
|
Create = #'DockerCreateOptions'{
|
|
config = #'DockerContainerConfig'{
|
|
image = ?TEST_IMAGE,
|
|
cmd = ?TEST_CMD,
|
|
env = [<<"EXISTING_ENV=1">>],
|
|
volumes = [<<"/data">>]
|
|
},
|
|
host_config = #'DockerHostConfig'{
|
|
binds = [<<"/tmp:/tmp">>]
|
|
}
|
|
},
|
|
Params = #'ContainerDeployParams'{
|
|
container_name = Name,
|
|
create = Create
|
|
},
|
|
Options = docker_container_builder:build_options(Name, ContainerDir, Create),
|
|
assert_patched_default_options(Name, ContainerDir, Options),
|
|
assert_existing_options_preserved(Options),
|
|
try
|
|
ok = test_pull(),
|
|
{ok, _ContainerId} = docker_commands:create_container(ContainerDir, Params),
|
|
ok
|
|
after
|
|
cleanup_container(Name)
|
|
end.
|
|
|
|
-spec test_check_container_exist() -> ok.
|
|
test_check_container_exist() ->
|
|
Name = test_container_name(<<"exist">>),
|
|
with_created_container(Name, fun(_ContainerDir, _ContainerId) ->
|
|
true = docker_commands:check_container_exist(Name),
|
|
ok
|
|
end).
|
|
|
|
-spec test_check_container_not_exist() -> ok.
|
|
test_check_container_not_exist() ->
|
|
false = docker_commands:check_container_exist(test_container_name(<<"missing">>)),
|
|
ok.
|
|
|
|
-spec test_is_container_running() -> ok.
|
|
test_is_container_running() ->
|
|
Name = test_container_name(<<"running">>),
|
|
with_created_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok = docker_commands:start_container(Name),
|
|
true = docker_commands:is_container_running(ContainerId),
|
|
ok = docker_commands:stop_container(Name),
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_start_container() -> ok.
|
|
test_start_container() ->
|
|
Name = test_container_name(<<"start">>),
|
|
with_created_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
ok = docker_commands:start_container(Name),
|
|
true = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_stop_container() -> ok.
|
|
test_stop_container() ->
|
|
Name = test_container_name(<<"stop">>),
|
|
with_started_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
ok = docker_commands:stop_container(Name),
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_stop_container_with_timeout() -> ok.
|
|
test_stop_container_with_timeout() ->
|
|
Name = test_container_name(<<"stop-timeout">>),
|
|
with_started_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
ok = docker_commands:stop_container(Name, 1),
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_kill_container() -> ok.
|
|
test_kill_container() ->
|
|
Name = test_container_name(<<"kill">>),
|
|
with_started_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
ok = docker_commands:kill_container(Name),
|
|
timer:sleep(200),
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_kill_container_with_signal() -> ok.
|
|
test_kill_container_with_signal() ->
|
|
Name = test_container_name(<<"kill-signal">>),
|
|
with_started_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
ok = docker_commands:kill_container(Name, <<"SIGKILL">>),
|
|
timer:sleep(200),
|
|
false = docker_commands:is_container_running(ContainerId),
|
|
ok
|
|
end).
|
|
|
|
-spec test_remove_container() -> ok.
|
|
test_remove_container() ->
|
|
Name = test_container_name(<<"remove">>),
|
|
with_created_container(Name, fun(_ContainerDir, _ContainerId) ->
|
|
ok = docker_commands:remove_container(Name),
|
|
false = docker_commands:check_container_exist(Name),
|
|
ok
|
|
end, false).
|
|
|
|
-spec test_remove_container_with_options() -> ok.
|
|
test_remove_container_with_options() ->
|
|
Name = test_container_name(<<"remove-opts">>),
|
|
with_started_container(Name, fun(_ContainerDir, _ContainerId) ->
|
|
ok = docker_commands:remove_container(Name, true, false),
|
|
false = docker_commands:check_container_exist(Name),
|
|
ok
|
|
end, false).
|
|
|
|
-spec test_get_containers() -> ok.
|
|
test_get_containers() ->
|
|
Name = test_container_name(<<"list">>),
|
|
with_created_container(Name, fun(_ContainerDir, ContainerId) ->
|
|
{ok, Containers} = docker_commands:get_containers(),
|
|
true = is_list(Containers),
|
|
true = contains_container(Name, ContainerId, Containers),
|
|
ok
|
|
end).
|
|
|
|
-spec with_created_container(binary(), fun((string(), binary()) -> ok)) -> ok.
|
|
with_created_container(Name, Fun) ->
|
|
with_created_container(Name, Fun, true).
|
|
|
|
-spec with_created_container(binary(), fun((string(), binary()) -> ok), boolean()) -> ok.
|
|
with_created_container(Name, Fun, Cleanup) when is_binary(Name), is_function(Fun, 2), is_boolean(Cleanup) ->
|
|
ContainerDir = prepare_container_dir(Name),
|
|
try
|
|
ok = test_pull(),
|
|
{ok, ContainerId} = docker_commands:create_container(ContainerDir, minimal_params(Name)),
|
|
ok = Fun(ContainerDir, ContainerId)
|
|
after
|
|
case Cleanup of
|
|
true ->
|
|
cleanup_container(Name);
|
|
false ->
|
|
ok
|
|
end
|
|
end.
|
|
|
|
-spec with_started_container(binary(), fun((string(), binary()) -> ok)) -> ok.
|
|
with_started_container(Name, Fun) ->
|
|
with_started_container(Name, Fun, true).
|
|
|
|
-spec with_started_container(binary(), fun((string(), binary()) -> ok), boolean()) -> ok.
|
|
with_started_container(Name, Fun, Cleanup) when is_binary(Name), is_function(Fun, 2), is_boolean(Cleanup) ->
|
|
with_created_container(Name, fun(ContainerDir, ContainerId) ->
|
|
ok = docker_commands:start_container(Name),
|
|
ok = Fun(ContainerDir, ContainerId)
|
|
end, Cleanup).
|
|
|
|
-spec minimal_params(binary()) -> message_pb:'ContainerDeployParams'().
|
|
minimal_params(Name) when is_binary(Name) ->
|
|
#'ContainerDeployParams'{
|
|
container_name = Name,
|
|
create = #'DockerCreateOptions'{
|
|
config = #'DockerContainerConfig'{
|
|
image = ?TEST_IMAGE,
|
|
cmd = ?TEST_CMD
|
|
}
|
|
}
|
|
}.
|
|
|
|
-spec prepare_container_dir(binary()) -> string().
|
|
prepare_container_dir(Name) when is_binary(Name) ->
|
|
Dir = lists:flatten(io_lib:format("/tmp/efka_docker_tests/~ts/", [Name])),
|
|
ok = filelib:ensure_dir(Dir ++ "placeholder"),
|
|
ok = file:write_file(Dir ++ "service.conf", <<>>, [write]),
|
|
Dir.
|
|
|
|
-spec cleanup_container(binary()) -> ok.
|
|
cleanup_container(Name) when is_binary(Name) ->
|
|
_ = docker_commands:remove_container(Name, true, false),
|
|
ok.
|
|
|
|
-spec assert_patched_default_options(binary(), string(), map()) -> ok.
|
|
assert_patched_default_options(Name, ContainerDir, Options)
|
|
when is_binary(Name), is_list(ContainerDir), is_map(Options) ->
|
|
ConfigFile = list_to_binary(docker_helper:get_config_file(ContainerDir)),
|
|
ExpectedBind = <<ConfigFile/binary, ":/usr/local/etc/service.conf">>,
|
|
#{<<"Env">> := Env,
|
|
<<"Volumes">> := Volumes,
|
|
<<"HostConfig">> := #{<<"Binds">> := Binds}} = Options,
|
|
true = lists:member(<<"CONTAINER_NAME=", Name/binary>>, Env),
|
|
true = maps:is_key(<<"/usr/local/etc/service.conf">>, Volumes),
|
|
true = lists:member(ExpectedBind, Binds),
|
|
ok.
|
|
|
|
-spec assert_existing_options_preserved(map()) -> ok.
|
|
assert_existing_options_preserved(Options) when is_map(Options) ->
|
|
#{<<"Env">> := Env,
|
|
<<"Volumes">> := Volumes,
|
|
<<"HostConfig">> := #{<<"Binds">> := Binds}} = Options,
|
|
true = lists:member(<<"EXISTING_ENV=1">>, Env),
|
|
true = maps:is_key(<<"/data">>, Volumes),
|
|
true = lists:member(<<"/tmp:/tmp">>, Binds),
|
|
ok.
|
|
|
|
-spec contains_container(binary(), binary(), [map()]) -> boolean().
|
|
contains_container(Name, ContainerId, Containers) when is_binary(Name), is_binary(ContainerId), is_list(Containers) ->
|
|
lists:any(fun(Container) -> container_matches(Name, ContainerId, Container) end, Containers).
|
|
|
|
-spec container_matches(binary(), binary(), map()) -> boolean().
|
|
container_matches(Name, ContainerId, #{<<"Id">> := Id, <<"Names">> := Names}) when is_binary(Id), is_list(Names) ->
|
|
lists:member(<<"/", Name/binary>>, Names) orelse has_id_prefix(ContainerId, Id);
|
|
container_matches(_Name, _ContainerId, _Container) ->
|
|
false.
|
|
|
|
-spec has_id_prefix(binary(), binary()) -> boolean().
|
|
has_id_prefix(ExpectedId, ActualId) when is_binary(ExpectedId), is_binary(ActualId) ->
|
|
PrefixLen = erlang:min(byte_size(ExpectedId), byte_size(ActualId)),
|
|
binary:part(ExpectedId, 0, PrefixLen) =:= binary:part(ActualId, 0, PrefixLen).
|
|
|
|
-spec test_container_name(binary()) -> binary().
|
|
test_container_name(Prefix) when is_binary(Prefix) ->
|
|
Suffix = integer_to_binary(erlang:unique_integer([positive])),
|
|
<<"efka-test-", Prefix/binary, "-", Suffix/binary>>.
|